/*
 * Copyright (C) 2012 AVM GmbH <info@avm.de>
 * Copyright (C) 2005 Luca Veltri - University of Parma - Italy
 * 
 * This file is part of MjSip (http://www.mjsip.org)
 * 
 * MjSip 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.
 * 
 * MjSip 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 MjSip; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Author(s):
 * Luca Veltri (luca.veltri@unipr.it)
 */

package org.zoolu.sip.provider;

import java.util.Locale;

import org.zoolu.tools.Configure;
import org.zoolu.tools.Parser;
import org.zoolu.tools.RotatingLog;
import org.zoolu.tools.Timer;

/**
 * SipStack includes all static attributes used by the sip stack.
 * <p>
 * Static attributes includes the logging configuration, default SIP port,
 * deafult supported transport protocols, timeouts, etc.
 */
public class SipStack extends Configure {
	// ********************** private attributes **********************

	/** Whether SipStack configuration has been already loaded */
	private static boolean is_init = false;

	/** The default SipProvider */
	// private static SipProvider provider=null;
	// *********************** software release ***********************
	/** Release */
	public static final String release = "mjsip stack 1.6";
	/** Authors */
	public static final String authors = "Luca Veltri - University of Parma (Italy)";

	// ********************** static attributes ***********************

	/** String value "no-ua-info" used for setting no 'User-Agent' header filed. */
	// public static final String NO_UA_INFO="NO-UA-INFO";
	/** String value "no-server-info" used for setting no 'Server' header filed. */
	// public static final String NO_SERVER_INFO="NO-SERVER-INFO";
	// ************* default sip provider configurations **************
	/**
	 * Default SIP port. Note that this is not the port used by the running
	 * stack, but simply the standard default SIP port. <br>
	 * Normally it sould be set to 5060 as defined by RFC 3261. Using a
	 * different value may cause some problems when interacting with other
	 * unaware SIP UAs.
	 */
	public static int default_port = 5060;
	/** Default supported transport protocols. */
	public static String[] default_transport_protocols = {
			SipProvider.PROTO_UDP, SipProvider.PROTO_TCP };
	/** Default max number of contemporary open transport connections. */
	public static int default_nmax_connections = 32;
	/**
	 * Whether adding 'rport' parameter on via header fields of outgoing
	 * requests.
	 */
	public static boolean use_rport = true;
	/**
	 * Whether adding (forcing) 'rport' parameter on via header fields of
	 * incoming requests.
	 */
	public static boolean force_rport = false;

	// ******************** general configurations ********************

	/** default max-forwards value (RFC3261 recommends value 70) */
	public static int max_forwards = 70;
	/**
	 * starting retransmission timeout (milliseconds); called T1 in RFC2361;
	 * they suggest T1=500ms
	 */
	public static long retransmission_timeout = 2000;
	/**
	 * maximum retransmission timeout (milliseconds); called T2 in RFC2361; they
	 * suggest T2=4sec
	 */
	public static long max_retransmission_timeout = 16000;
	/** transaction timeout (milliseconds); RFC2361 suggests 64*T1=32000ms */
	public static long transaction_timeout = 128000;
	/** clearing timeout (milliseconds); T4 in RFC2361; they suggest T4=5sec */
	public static long clearing_timeout = 5000;

	/** Whether using only one thread for all timer instances. */
	public static boolean single_timer = false;

	/**
	 * Whether 1xx responses create an "early dialog" for methods that create
	 * dialog.
	 */
	public static boolean early_dialog = false;

	/**
	 * Default 'expires' value in seconds. RFC2361 suggests 3600s as default
	 * value.
	 * Changed for FRITZ!Box to 300s
	 */
	public static int default_expires = 300;

	/**
	 * UA info included in request messages in the 'User-Agent' header field.
	 * Use "NONE" if the 'User-Agent' header filed must not be added.
	 */
	public static String ua_info = release;
	/**
	 * Server info included in response messages in the 'Server' header field
	 * Use "NONE" if the 'Server' header filed must not be added.
	 */
	public static String server_info = release;

	// ************************ debug and logs ************************

	/** Base level (offset) for logging Transport events */
	public static int LOG_LEVEL_TRANSPORT = 1;
	/** Base level (offset) for logging Transaction events */
	public static int LOG_LEVEL_TRANSACTION = 2;
	/** Base level (offset) for logging Dialog events */
	public static int LOG_LEVEL_DIALOG = 2;
	/** Base level (offset) for logging Call events */
	public static int LOG_LEVEL_CALL = 1;
	/** Base level (offset) for logging UA events */
	public static int LOG_LEVEL_UA = 0;

	/** Log level. Only logs with a level less or equal to this are written. */
	public static int debug_level = 1;
	/**
	 * Path for the log folder where log files are written. By default, it is
	 * used the "./log" folder. Use ".", to store logs in the current root
	 * folder.
	 */
	public static String log_path = "log";
	/** The size limit of the log file [kB] */
	public static int max_logsize = 2048; // 2MB
	/**
	 * The number of rotations of log files. Use '0' for NO rotation, '1' for
	 * rotating a single file
	 */
	public static int log_rotations = 0; // no rotation
	/**
	 * The rotation period, in MONTHs or DAYs or HOURs or MINUTEs examples:
	 * log_rotation_time=3 MONTHS, log_rotations=90 DAYS Default value:
	 * log_rotation_time=2 MONTHS
	 */
	private static String log_rotation_time = null;
	/** The rotation time scale */
	public static int rotation_scale = RotatingLog.MONTH;
	/** The rotation time value */
	public static int rotation_time = 2;

	// ************************** costructor **************************

	/** Parses a single text line (read from the config file) */
	protected void parseLine(String line) {
		String attribute;
		Parser par;
		int index = line.indexOf("=");
		if (index > 0) {
			attribute = line.substring(0, index).trim();
			par = new Parser(line, index + 1);
		} else {
			attribute = line;
			par = new Parser("");
		}
		char[] delim = { ' ', ',' };

		// general configurations
		if (attribute.equals("default_port")) {
			default_port = par.getInt();
			return;
		}
		if (attribute.equals("default_transport_protocols")) {
			default_transport_protocols = par.getWordArray(delim);
			return;
		}
		if (attribute.equals("default_nmax_connections")) {
			default_nmax_connections = par.getInt();
			return;
		}
		if (attribute.equals("use_rport")) {
			use_rport = (par.getString().toLowerCase(Locale.US).startsWith("y"));
			return;
		}
		if (attribute.equals("force_rport")) {
			force_rport = (par.getString().toLowerCase(Locale.US).startsWith("y"));
			return;
		}
		if (attribute.equals("max_forwards")) {
			max_forwards = par.getInt();
			return;
		}
		if (attribute.equals("retransmission_timeout")) {
			retransmission_timeout = par.getInt();
			return;
		}
		if (attribute.equals("max_retransmission_timeout")) {
			max_retransmission_timeout = par.getInt();
			return;
		}
		if (attribute.equals("transaction_timeout")) {
			transaction_timeout = par.getInt();
			return;
		}
		if (attribute.equals("clearing_timeout")) {
			clearing_timeout = par.getInt();
			return;
		}
		if (attribute.equals("single_timer")) {
			single_timer = (par.getString().toLowerCase(Locale.US).startsWith("y"));
			return;
		}
		if (attribute.equals("early_dialog")) {
			early_dialog = (par.getString().toLowerCase(Locale.US).startsWith("y"));
			return;
		}
		if (attribute.equals("default_expires")) {
			default_expires = par.getInt();
			return;
		}
		if (attribute.equals("ua_info")) {
			ua_info = par.getRemainingString().trim();
			return;
		}
		if (attribute.equals("server_info")) {
			server_info = par.getRemainingString().trim();
			return;
		}

		// debug and logs
		if (attribute.equals("debug_level")) {
			debug_level = par.getInt();
			return;
		}
		if (attribute.equals("log_path")) {
			log_path = par.getString();
			return;
		}
		if (attribute.equals("max_logsize")) {
			max_logsize = par.getInt();
			return;
		}
		if (attribute.equals("log_rotations")) {
			log_rotations = par.getInt();
			return;
		}
		if (attribute.equals("log_rotation_time")) {
			log_rotation_time = par.getRemainingString();
			return;
		}

		// old parameters
		if (attribute.equals("host_addr"))
			printLog("WARNING: parameter 'host_addr' is no more supported; use 'via_addr' instead.");
		if (attribute.equals("all_interfaces"))
			printLog("WARNING: parameter 'all_interfaces' is no more supported; use 'host_iaddr' for setting a specific interface or let it undefined.");
		if (attribute.equals("use_outbound"))
			printLog("WARNING: parameter 'use_outbound' is no more supported; use 'outbound_addr' for setting an outbound proxy or let it undefined.");
		if (attribute.equals("log_file"))
			printLog("WARNING: parameter 'log_file' is no more supported.");
	}

	/** Converts the entire object into lines (to be saved into the config file) */
	protected String toLines() { // currently not implemented..
		return "SipStack/" + release;
	}

	/** Costructs a non-static SipStack */
	private SipStack() {
	}

	/** Inits SipStack */
	public static void init() {
		init(null);
	}

	/** Inits SipStack from the specified <i>file</i> */
	public static void init(String file) {
		(new SipStack()).loadFile(file);

		// user-agent info
		if (ua_info != null
				&& (ua_info.length() == 0
						|| ua_info.equalsIgnoreCase(Configure.NONE) || ua_info
						.equalsIgnoreCase("NO-UA-INFO")))
			ua_info = null;

		// server info
		if (server_info != null
				&& (server_info.length() == 0
						|| server_info.equalsIgnoreCase(Configure.NONE) || server_info
						.equalsIgnoreCase("NO-SERVER-INFO")))
			server_info = null;

		// timers
		Timer.SINGLE_THREAD = single_timer;

		// logs
		if (debug_level > 0) {
			if (log_rotation_time != null) {
				SipParser par = new SipParser(log_rotation_time);
				rotation_time = par.getInt();
				String scale = par.getString();
				if (scale == null)
					scale = "null";
				if (scale.toUpperCase(Locale.US).startsWith("MONTH"))
					rotation_scale = RotatingLog.MONTH;
				else if (scale.toUpperCase(Locale.US).startsWith("DAY"))
					rotation_scale = RotatingLog.DAY;
				else if (scale.toUpperCase(Locale.US).startsWith("HOUR"))
					rotation_scale = RotatingLog.HOUR;
				else if (scale.toUpperCase(Locale.US).startsWith("MINUTE"))
					rotation_scale = RotatingLog.MINUTE;
				else {
					rotation_time = 7;
					rotation_scale = RotatingLog.DAY;
					printLog("Error with the log rotation time. Logs will rotate every week.");
				}
			}
		}

		is_init = true;
		// if (file!=null) printLog("SipStack loaded",1);
	}

	/** Whether SipStack has been already initialized */
	public static boolean isInit() {
		return is_init;
	}

	// ************************ private methods ***********************

	/** Logs a string message. */
	private static void printLog(String str) {
		System.out.println("SipStack: " + str);
	}
}
