/*
 * This file is part of the Nice GLib ICE library.
 *
 * (C) 2008-2009 Collabora Ltd.
 *  Contact: Youness Alaoui
 * (C) 2007-2009 Nokia Corporation. All rights reserved.
 *  Contact: Rémi Denis-Courmont
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Nice GLib ICE library.
 *
 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
 * Corporation. All Rights Reserved.
 *
 * Contributors:
 *   Youness Alaoui, Collabora Ltd.
 *   Rémi Denis-Courmont, Nokia
 *
 * Alternatively, the contents of this file may be used under the terms of the
 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
 * case the provisions of LGPL are applicable instead of those above. If you
 * wish to allow use of your version of this file only under the terms of the
 * LGPL and not to allow others to use your version of this file under the
 * MPL, indicate your decision by deleting the provisions above and replace
 * them with the notice and other provisions required by the LGPL. If you do
 * not delete the provisions above, a recipient may use your version of this
 * file under either the MPL or the LGPL.
 */

#ifndef STUN_TIMER_H
# define STUN_TIMER_H 1

/**
 * SECTION:timer
 * @short_description: STUN timer Usage
 * @include: stun/usages/timer.h
 * @stability: Stable
 *
 * The STUN timer usage is a set of helpful utility functions that allows you
 * to easily track when a STUN message should be retransmitted or considered
 * as timed out.
 *
 *
 <example>
   <title>Simple example on how to use the timer usage</title>
   <programlisting>
   StunTimer timer;
   unsigned remainder;
   StunUsageTimerReturn ret;

   // Build the message, etc..
   ...

   // Send the message and start the timer
   send(socket, request, sizeof(request));
   stun_timer_start(&timer, STUN_TIMER_DEFAULT_TIMEOUT,
                    STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);

   // Loop until we get the response
   for (;;) {
     remainder = stun_timer_remainder(&timer);

     // Poll the socket until data is received or the timer expires
     if (poll (&pollfd, 1, delay) <= 0) {
       // Time out and no response was received
       ret = stun_timer_refresh (&timer);
       if (ret == STUN_USAGE_TIMER_RETURN_TIMEOUT) {
         // Transaction timed out
         break;
       } else if (ret == STUN_USAGE_TIMER_RETURN_RETRANSMIT) {
         // A retransmission is necessary
         send(socket, request, sizeof(request));
         continue;
       } else if (ret == STUN_USAGE_TIMER_RETURN_SUCCESS) {
         // The refresh succeeded and nothing has to be done, continue polling
         continue;
       }
     } else {
       // We received a response, read it
       recv(socket, response, sizeof(response));
       break;
     }
   }

   // Check if the transaction timed out or not
   if (ret == STUN_USAGE_TIMER_RETURN_TIMEOUT) {
     // do whatever needs to be done in that case
   } else {
     // Parse the response
   }

   </programlisting>
 </example>
 */

#ifdef _WIN32
#include <winsock2.h>
#else
# include <sys/types.h>
# include <sys/time.h>
# include <time.h>
#endif


/**
 * StunTimer:
 *
 * An opaque structure representing a STUN transaction retransmission timer
 */
typedef struct stun_timer_s StunTimer;

struct stun_timer_s {
  struct timeval deadline;
  unsigned delay;
  unsigned retransmissions;
  unsigned max_retransmissions;
};


/**
 * STUN_TIMER_DEFAULT_TIMEOUT:
 *
 * The default intial timeout to use for the timer
 * RFC recommendds 500, but it's ridiculous, 50ms is known to work in most
 * cases as it is also what is used by SIP style VoIP when sending A-Law and
 * mu-Law audio, so 200ms should be hyper safe. With an initial timeout
 * of 200ms, a default of 7 transmissions, the last timeout will be
 * 16 * 200ms, and we expect to receive a response from the stun server
 * before (1 + 2 + 4 + 8 + 16 + 32 + 16) * 200ms = 15200 ms after the initial
 * stun request has been sent.
 */
#define STUN_TIMER_DEFAULT_TIMEOUT 200

/**
 * STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS:
 *
 * The default maximum retransmissions allowed before a timer decides to timeout
 */
#define STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS 7

/**
 * STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT:
 *
 * The default intial timeout to use for a reliable timer
 */
#define STUN_TIMER_DEFAULT_RELIABLE_TIMEOUT 7900

/**
 * StunUsageTimerReturn:
 * @STUN_USAGE_TIMER_RETURN_SUCCESS: The timer was refreshed successfully
 * and there is nothing to be done
 * @STUN_USAGE_TIMER_RETURN_RETRANSMIT: The timer expired and the message
 * should be retransmitted now.
 * @STUN_USAGE_TIMER_RETURN_TIMEOUT: The timer expired as well as all the
 * retransmissions, the transaction timed out
 *
 * Return value of stun_usage_timer_refresh() which provides you with status
 * information on the timer.
 */
typedef enum {
  STUN_USAGE_TIMER_RETURN_SUCCESS,
  STUN_USAGE_TIMER_RETURN_RETRANSMIT,
  STUN_USAGE_TIMER_RETURN_TIMEOUT
} StunUsageTimerReturn;

# ifdef __cplusplus
extern "C" {
# endif


/**
 * stun_timer_start:
 * @timer: The #StunTimer to start
 * @initial_timeout: The initial timeout to use before the first retransmission
 * @max_retransmissions: The maximum number of transmissions before the
 * #StunTimer times out
 *
 * Starts a STUN transaction retransmission timer.
 * This should be called as soon as you send the message for the first time on
 * a UDP socket.
 * The timeout before the next retransmission is set to @initial_timeout, then
 * each time a packet is retransmited, that timeout will be doubled, until the
 * @max_retransmissions retransmissions limit is reached.
 * <para>
 * To determine the total timeout value, one can use the following equation :
 <programlisting>
 total_timeout =  initial_timeout * (2^(max_retransmissions + 1) - 1);
 </programlisting>
 * </para>
 *
 * See also: #STUN_TIMER_DEFAULT_TIMEOUT
 *
 * See also: #STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS
 */
void stun_timer_start (StunTimer *timer, unsigned int initial_timeout,
      unsigned int max_retransmissions);

/**
 * stun_timer_start_reliable:
 * @timer: The #StunTimer to start
 * @initial_timeout: The initial timeout to use before the first retransmission
 *
 * Starts a STUN transaction retransmission timer for a reliable transport.
 * This should be called as soon as you send the message for the first time on
 * a TCP socket
 */
void stun_timer_start_reliable (StunTimer *timer, unsigned int initial_timeout);

/**
 * stun_timer_refresh:
 * @timer: The #StunTimer to refresh
 *
 * Updates a STUN transaction retransmission timer.
 * Returns: A #StunUsageTimerReturn telling you what to do next
 */
StunUsageTimerReturn stun_timer_refresh (StunTimer *timer);

/**
 * stun_timer_remainder:
 * @timer: The #StunTimer to query
 *
 * Query the timer on the time left before the next refresh should be done
 * Returns: The time remaining for the timer to expire in milliseconds
 */
unsigned stun_timer_remainder (const StunTimer *timer);

# ifdef __cplusplus
}
# endif

#endif /* !STUN_TIMER_H */
