/*
 * Copyright (C) 2002-2003 Fhg Fokus
 *
 * This file is part of SEMS, a free SIP media server.
 *
 * SEMS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version. This program is released under
 * the GPL with the additional exemption that compiling, linking,
 * and/or using OpenSSL is allowed.
 *
 * For a license to use the SEMS software under conditions
 * other than those described here, or to purchase support for this
 * software, please contact iptel.org by e-mail at the following addresses:
 *    info@iptel.org
 *
 * SEMS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef SessionTimer_h
#define SessionTimer_h

#include "AmApi.h"
#include "AmSession.h"

#ifndef MOD_NAME
#define MOD_NAME "session_timer"
#endif

#define TIMER_OPTION_TAG  "timer"

/* Session Timer: -ssa */
class AmTimeoutEvent;
// these are the timer IDs for session timer
// Caution: do not use these for other purposes
#define ID_SESSION_TIMER_TIMERS_START -2
#define ID_SESSION_TIMER_TIMERS_END -1

#define ID_SESSION_INTERVAL_TIMER -1
#define ID_SESSION_REFRESH_TIMER  -2

/* Session Timer default configuration: */
#define DEFAULT_ENABLE_SESSION_TIMER 1
#define SESSION_EXPIRES              120  // seconds
#define MINIMUM_TIMER                90   // seconds

#define MAXIMUM_TIMER                900   // seconds - 15 min

/** \brief Factory of the session timer event handler */
class SessionTimerFactory: public AmSessionEventHandlerFactory
{
  bool checkSessionExpires(const AmSipRequest& req, AmConfigReader& cfg);

 public:
  SessionTimerFactory(const string& name)
    : AmSessionEventHandlerFactory(name) {}

  int onLoad();
  bool onInvite(const AmSipRequest& req, AmConfigReader& cfg);

  AmSessionEventHandler* getHandler(AmSession* s);
};

/** \brief config for the session timer */
class AmSessionTimerConfig
{

  /** Session Timer: enable? */
  int EnableSessionTimer;
  /** Session Timer: Desired Session-Expires */
  unsigned int SessionExpires;
  /** Session Timer: Minimum Session-Expires */
  unsigned int MinimumTimer;

  unsigned int MaximumTimer;

public:
  AmSessionTimerConfig();
  ~AmSessionTimerConfig();
  

  /** Session Timer: Enable Session Timer?
      returns 0 on invalid value */
  int setEnableSessionTimer(const string& enable);
  /** Session Timer: Setter for Desired Session-Expires, 
      returns 0 on invalid value */
  int setSessionExpires(const string& se);
  /** Session Timer: Setter for Minimum Session-Expires, 
      returns 0 on invalid value */
  int setMinimumTimer(const string& minse);

  void setEnableSessionTimer(bool enable)  { EnableSessionTimer = enable; }
  void setSessionExpires(unsigned int se)  { SessionExpires = se; }
  void setMinimumTimer(unsigned int minse) { MinimumTimer = minse; }
  void setMaximumTimer(unsigned int maxse) { MaximumTimer = maxse; }

  bool getEnableSessionTimer() const { return EnableSessionTimer; }
  unsigned int getSessionExpires() const { return SessionExpires; }
  unsigned int getMinimumTimer() const { return MinimumTimer; }
  unsigned int getMaximumTimer() const { return MaximumTimer; }

  int readFromConfig(AmConfigReader& cfg);
};

struct SIPRequestInfo;

/** \brief SessionEventHandler for implementing session timer logic for a session */
class SessionTimer: public AmSessionEventHandler
{
protected:
  AmSessionTimerConfig session_timer_conf;
  AmSession* s;

  // map to save sent requests, so we can resent in case of 422
  //std::map<unsigned int, SIPRequestInfo> sent_requests;

  enum SessionRefresher {
    refresh_local,
    refresh_remote
  };
  enum SessionRefresherRole {
    UAC,
    UAS
  };

  bool                 remote_timer_aware;
  unsigned int         min_se;
  unsigned int         session_interval;  
  SessionRefresher     session_refresher;
  SessionRefresherRole session_refresher_role;
  bool                 accept_501_reply;

  void updateTimer(AmSession* s,const AmSipRequest& req);
  void updateTimer(AmSession* s,const AmSipReply& reply);
    
  virtual void setTimers(AmSession* s);
  void retryRefreshTimer(AmSession* s);
  void removeTimers(AmSession* s);

  string getReplyHeaders(const AmSipRequest& req);
  string getRequestHeaders(const string& method);

  /* Session Timer: -ssa */

  // @return true if OK
  void onTimeout();
  void onTimeoutEvent(AmTimeoutEvent* timeout_ev);

 public:
  SessionTimer(AmSession*);
  virtual ~SessionTimer();

  /* @see AmSessionEventHandler */
  virtual int  configure(AmConfigReader& conf); 
  virtual bool process(AmEvent*);

  virtual bool onSipRequest(const AmSipRequest&);
  virtual bool onSipReply(const AmSipRequest&, const AmSipReply&, 
			  AmBasicSipDialog::Status old_dlg_status);

  virtual bool onSendRequest(AmSipRequest& req, int& flags);
  virtual bool onSendReply(const AmSipRequest& req, AmSipReply& reply, int& flags);
};


/** \brief contains necessary information for UAC auth of a SIP request */
struct SIPRequestInfo {
  string method;
  AmMimeBody body;
  string hdrs;

  SIPRequestInfo(const string& method,
		 const AmMimeBody* body,
		 const string& hdrs)
    : method(method), hdrs(hdrs) 
  { 
    if(body) this->body = *body;
  }

  SIPRequestInfo() {}

};

#endif
