* Copyright (C) 2010-2011 Stefan Sayer
 * 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.
 * 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
 * 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 _SBCCallProfile_h
#define _SBCCallProfile_h

#include "AmConfigReader.h"
#include "HeaderFilter.h"
#include "ampi/UACAuthAPI.h"
#include "ParamReplacer.h"
#include "atomic_types.h"
#include "sip/msg_logger.h"

#include <set>
#include <string>
#include <map>
#include <list>

using std::string;
using std::map;
using std::set;
using std::pair;

typedef map<string, AmArg>   SBCVarMapT;
typedef SBCVarMapT::iterator SBCVarMapIteratorT;
typedef SBCVarMapT::const_iterator SBCVarMapConstIteratorT;

struct CCInterface {
  string cc_name;
  string cc_module;
  map<string, string> cc_values;

  CCInterface(string cc_name)
  : cc_name(cc_name) { }
  CCInterface() { }

typedef std::list<CCInterface> CCInterfaceListT;
typedef CCInterfaceListT::iterator CCInterfaceListIteratorT;

template <class T>
class ref_counted_ptr
    T *ptr;

    void reset(T *p) { if (ptr) dec_ref(ptr); ptr = p; if (ptr) inc_ref(ptr); }
    T *get() const { return ptr; }

    ref_counted_ptr(): ptr(0) { }
    ~ref_counted_ptr() { if (ptr) dec_ref(ptr); }

    ref_counted_ptr(const ref_counted_ptr &other): ptr(other.ptr) { if (ptr) inc_ref(ptr); }
    ref_counted_ptr &operator=(const ref_counted_ptr &other) { reset(other.ptr); return *this; }


class PayloadDesc {
    std::string name;
    unsigned clock_rate; // 0 means "doesn't matter"

    bool match(const SdpPayload &p) const;
    std::string print() const;
    bool operator==(const PayloadDesc &other) const;

    /* FIXME: really want all of this?
     * reads from format: name/clock_rate, nothing need to be set
     * for example: 
     *	  PCMU
     *	  bla/48000
     *	  /48000
     * */
    bool read(const std::string &s);

typedef pair<unsigned int, std::string> ReplyCodeReasonPair;
typedef map<unsigned int, ReplyCodeReasonPair> ReplyTranslationMap;

struct SBCCallProfile
  : public AmObject {
  string md5hash;
  string profile_file;

  string ruri;       /* updated if set */
  string ruri_host;  /* updated if set */
  string from;       /* updated if set */
  string to;         /* updated if set */

  struct Contact {
    string displayname;
    string user;
    string host;
    string port;

    bool   hiding;
    string hiding_prefix;
    string hiding_vars;
  Contact contact;

  string callid;

  string dlg_contact_params;
  string bleg_dlg_contact_params;

  bool transparent_dlg_id;
  bool dlg_nat_handling;
  bool keep_vias;
  bool bleg_keep_vias;

  string outbound_proxy;
  bool force_outbound_proxy;

  string aleg_outbound_proxy;
  bool aleg_force_outbound_proxy;

  string next_hop;
  bool next_hop_1st_req;
  bool patch_ruri_next_hop;

  string aleg_next_hop;

  bool allow_subless_notify;

  vector<FilterEntry> headerfilter;
  vector<FilterEntry> messagefilter;

  bool anonymize_sdp;
  vector<FilterEntry> sdpfilter;
  vector<FilterEntry> sdpalinesfilter;
  vector<FilterEntry> mediafilter;

  string sst_enabled;
  bool sst_enabled_value;
  string sst_aleg_enabled;
  AmConfigReader sst_a_cfg;    // SST config (A leg)
  AmConfigReader sst_b_cfg;    // SST config (B leg)

  bool auth_enabled;
  UACAuthCred auth_credentials;

  bool auth_aleg_enabled;
  UACAuthCred auth_aleg_credentials;

  CCInterfaceListT cc_interfaces;

  SBCVarMapT cc_vars;

  ReplyTranslationMap reply_translations;

  string append_headers;
  string append_headers_req;
  string aleg_append_headers_req;

  string refuse_with;

  bool rtprelay_enabled;
  string force_symmetric_rtp;
  string aleg_force_symmetric_rtp;
  bool force_symmetric_rtp_value;
  bool aleg_force_symmetric_rtp_value;

  bool msgflags_symmetric_rtp;
  bool rtprelay_transparent_seqno;
  bool rtprelay_transparent_ssrc;

  string rtprelay_interface;
  int rtprelay_interface_value;
  string aleg_rtprelay_interface;
  int aleg_rtprelay_interface_value;

  int rtprelay_bw_limit_rate;
  int rtprelay_bw_limit_peak;

  list<atomic_int*> aleg_rtp_counters;
  list<atomic_int*> bleg_rtp_counters;

  string outbound_interface;
  int outbound_interface_value;

  string aleg_outbound_interface;
  int aleg_outbound_interface_value;

  struct TranscoderSettings {
    // non-replaced parameters
    string callee_codec_capabilities_str, audio_codecs_str, 
      transcoder_mode_str, lowfi_codecs_str, dtmf_mode_str,
      audio_codecs_norelay_str, audio_codecs_norelay_aleg_str;

    std::vector<PayloadDesc> callee_codec_capabilities;
    std::vector<SdpPayload> audio_codecs;
    std::vector<SdpPayload> audio_codecs_norelay;
    std::vector<SdpPayload> audio_codecs_norelay_aleg;
    std::vector<SdpPayload> lowfi_codecs;
    enum { Always, OnMissingCompatible, Never } transcoder_mode;
    enum { DTMFAlways, DTMFLowFiCodecs, DTMFNever } dtmf_mode;
    bool readTranscoderMode(const std::string &src);
    bool readDTMFMode(const std::string &src);
    bool enabled;
    bool evaluate(ParamReplacerCtx& ctx, const AmSipRequest& req);

    bool readConfig(AmConfigReader &cfg);
    void infoPrint() const;
    bool operator==(const TranscoderSettings& rhs) const;
    string print() const;

    bool isActive() { return enabled; }
    TranscoderSettings(): enabled(false), transcoder_mode(Never) { }
  } transcoder;

  struct CodecPreferences {
    // non-replaced parameters
    string aleg_prefer_existing_payloads_str, aleg_payload_order_str;
    string bleg_prefer_existing_payloads_str, bleg_payload_order_str;

    /** when reordering payloads in relayed SDP from B leg to A leg prefer already
     * present payloads to the added ones by transcoder; i.e. transcoder codecs
     * are not ordered but added after ordering is done */
    bool aleg_prefer_existing_payloads;
    std::vector<PayloadDesc> aleg_payload_order;
    /** when reordering payloads in relayed SDP from A leg to B leg prefer already
     * present payloads to the added ones by transcoder; i.e. transcoder codecs
     * are not ordered but added after ordering is done */
    bool bleg_prefer_existing_payloads;
    std::vector<PayloadDesc> bleg_payload_order;

    bool readConfig(AmConfigReader &cfg);
    void infoPrint() const;
    bool operator==(const CodecPreferences& rhs) const;
    string print() const;
    void orderSDP(AmSdp& sdp, bool a_leg); // do the SDP changes
    bool shouldOrderPayloads(bool a_leg); // returns if call to orderSDP is needed

    // return true if ordering should be done before adding transcoder codecs
    bool preferExistingCodecs(bool a_leg) {
      return a_leg ? bleg_prefer_existing_payloads : aleg_prefer_existing_payloads;

    bool evaluate(ParamReplacerCtx& ctx, const AmSipRequest& req);

    // default settings
    CodecPreferences(): aleg_prefer_existing_payloads(false) ,bleg_prefer_existing_payloads(false) { }
  } codec_prefs;

  bool contact_hiding;
  string contact_hiding_prefix;
  string contact_hiding_vars;

  bool reg_caching;
  unsigned int min_reg_expires;
  unsigned int max_ua_expires;

  // todo: RTP transcoding mode

  // hold settings
  class HoldSettings {
      struct HoldParams {
        // non-replaced params
        string mark_zero_connection_str, recv_str, alter_b2b_str;

        bool mark_zero_connection;
        bool recv; // sendrecv/recvonly (if set) X sendonly/inactive (if unset)
        bool alter_b2b; // transform B2B hold requests (not locally generated ones)

        HoldParams(): mark_zero_connection(false), recv(false), alter_b2b(false) { }
      } aleg, bleg;

      bool mark_zero_connection(bool a_leg) { return a_leg ? aleg.mark_zero_connection : bleg.mark_zero_connection; }
      bool recv(bool a_leg) { return a_leg ? aleg.recv : bleg.recv; }
      bool alter_b2b(bool a_leg) { return a_leg ? aleg.alter_b2b : bleg.alter_b2b; }

      void readConfig(AmConfigReader &cfg);
      bool evaluate(ParamReplacerCtx& ctx, const AmSipRequest& req);
  } hold_settings;

  // message logging feature
  string msg_logger_path;
  ref_counted_ptr<msg_logger> logger;

  void create_logger(const AmSipRequest& req);

  bool log_rtp;
  bool log_sip;
  bool has_logger() { return logger.get() != NULL; }
  msg_logger* get_logger(const AmSipRequest& req);
  void set_logger_path(const std::string path) { msg_logger_path = path; }
  const string &get_logger_path() { return msg_logger_path; }

  : auth_enabled(false),
  { }

  { }

  bool readFromConfiguration(const string& name, const string profile_file_name);

  bool operator==(const SBCCallProfile& rhs) const;
  string print() const;

  int refuse(ParamReplacerCtx& ctx, const AmSipRequest& req) const;

  int apply_a_routing(ParamReplacerCtx& ctx,
		      const AmSipRequest& req,
		      AmBasicSipDialog& dlg) const;
  int apply_b_routing(ParamReplacerCtx& ctx,
		      const AmSipRequest& req,
		      AmBasicSipDialog& dlg) const;

  int apply_common_fields(ParamReplacerCtx& ctx,
			  AmSipRequest& req) const;

  bool evaluate(ParamReplacerCtx& ctx,
		const AmSipRequest& req);

  bool evaluateRTPRelayInterface();
  bool evaluateRTPRelayAlegInterface();

  void eval_sst_config(ParamReplacerCtx& ctx,
		       const AmSipRequest& req,
		       AmConfigReader& sst_cfg);

  void replace_cc_values(ParamReplacerCtx& ctx,
			 const AmSipRequest& req,
			 AmArg* values);

  void eval_cc_list(ParamReplacerCtx& ctx, const AmSipRequest& req);
  void fix_append_hdrs(ParamReplacerCtx& ctx, const AmSipRequest& req);

  void fix_reg_contact(ParamReplacerCtx& ctx, const AmSipRequest& req,
		       AmUriParser& contact) const;

   * Reg-cache lookup:
   * - searches for alias in the reg-cache.
   * - sets next-hop & outbound_interface on the given dialog
   * @return retargeted R-URI
  string retarget(const string& alias, AmBasicSipDialog& dlg) const;

   * Reg-cache lookup:
   * - searches for alias in the reg-cache.
   * - sets next-hop & outbound_interface in this profile
   * @return retargeted R-URI
  string retarget(const string& alias);

#endif // _SBCCallProfile_h