#include <config.h>
#include <stdio.h>
#include <sys/stat.h>
#include "IMSvrArg.hh"
#include "IMUtil.hh"
#include "IMLog.hh"

void
IMSvrArg::show_usage()
{
    fprintf(stderr,
	    "iiimd [-d] [-lc_basiclocale localename] \n"
	    "      [-ifpath_name ifpath] [-if ifname] \n"
	    "      [-port portnumber] [-hostname hostname] \n"
	    "      [-conversionkeys keys] \n"
	    "      [-ns_map filename ] \n"
	    "      [-preferredload] \n" );
}

void
IMSvrArg::initialize()
{
    addopt(IMDIR, "imdir");
    addopt(BASICLOCALE, "lc_basiclocale");
    addopt(IFPATHNAME, "ifpath_name");
    addopt(IFNAME, "if");
    addopt(PORT, "port");
    addopt(HOSTNAME, "hostname");
    addopt(NSMAP_CFG_FILE, "ns_map");
    addopt(CONFIGFILE, "cf");
    addopt(CONVERSIONKEYS, "conversionkeys");
    addopt(LABEL, "label");
    addopt(SETTRIGGEROFFKEYS, "setTriggerOffKeys");
    // addopt(IMCONFIG, "imconfig");
    // addopt(DIRECT_XSUNIM, "direct_xsunim");
    // addopt(SYSLOG, "syslog");
    addopt(MESSAGE_LOCALE, "message_locale");
    addopt(LOOKUPROW, "lookupRow");
    addopt(LOOKUPCOL, "lookupRow");
    addopt(NODAEMON, "nodaemon");
    addopt(DEBUGFLAG, "d");
    addopt(USER, "user");
    addopt(VARDIR, "vardir");
#if defined(HAVE_UNIX_SOCKET)
    addopt(UDSFILE, "udsfile");
#endif /* HAVE_UNIX_SOCKET */
    addopt(DESKTOP, "desktop");
    addopt(PREFERRED_LOADING, "preferredload");
}

static enum IMLog::LOG_LEVEL
convert_log_level_string(
    const char* str
)
{
    if (!strcmp(str, "QUIET")) {
	return IMLog::QUIET;
    } else if (!strcmp(str, "ERROR")) {
	return IMLog::ERROR;
    } else if (!strcmp(str, "WARNING")) {
	return IMLog::WARNING;
    } else if (!strcmp(str, "NORMAL")) {
	return IMLog::NORMAL;
    } else if (!strcmp(str, "INFO")) {
	return IMLog::INFO;
    } else if (!strcmp(str, "VERBOSE")) {
	return IMLog::VERBOSE;
    } else if (!strcmp(str, "DEBUG")) {
	return IMLog::DEBUGLOG;
    }
    return IMLog::INVALID;
}

static enum IMLog::LOG_DESTINATION
convert_log_facility_string(
    const char* str
)
{
    if (!strcmp(str, "AUTH")) {
	return IMLog::SYSLOG_AUTHPRIV;
    } else if (!strcmp(str, "USER")) {
	return IMLog::SYSLOG_USER;
    } else if (!strcmp(str, "LOCAL0")) {
	return IMLog::SYSLOG_LOCAL0;
    } else if (!strcmp(str, "LOCAL1")) {
	return IMLog::SYSLOG_LOCAL1;
    } else if (!strcmp(str, "LOCAL2")) {
	return IMLog::SYSLOG_LOCAL2;
    } else if (!strcmp(str, "LOCAL3")) {
	return IMLog::SYSLOG_LOCAL3;
    } else if (!strcmp(str, "LOCAL4")) {
	return IMLog::SYSLOG_LOCAL4;
    } else if (!strcmp(str, "LOCAL5")) {
	return IMLog::SYSLOG_LOCAL5;
    } else if (!strcmp(str, "LOCAL6")) {
	return IMLog::SYSLOG_LOCAL6;
    } else if (!strcmp(str, "LOCAL7")) {
	return IMLog::SYSLOG_LOCAL7;
    } else if (!strcmp(str, "STDOUT")) {
	return IMLog::IMLOG_STDOUT;
    } else if (!strcmp(str, "STDERR")) {
	return IMLog::IMLOG_STDERR;
    }
    return IMLog::IMLOG_DEFAULT;
}

bool
IMSvrArg::set_argopt()
{
    IMSvrArgMap::iterator it;
    enum IMSvrCfgOpt opt;

    for (it = optmap.begin(); it != optmap.end(); it++) {
	ArgVal &val = it->second;
	if (val.specified) {
	    opt = val.opt;
	    switch (get_type(opt)) {
	      case IMSvrCfg::ARG_BOOL:
	       setbool(opt, val.arg.b);
	       break;
	      case IMSvrCfg::ARG_NUMBER:
	       setnum(opt, val.arg.n);
	       break;
	      case IMSvrCfg::ARG_STRING:
	       setstr(opt, val.arg.s.c_str());
	       break;
	      default:
	       ERROR_INTERNAL("Argument type is invalid.  That must not happen...");
	    }
	}
    }

    return true;
}

bool
IMSvrArg::configure(
    IMSvr *pimsvr
)
{
    if (!valid_flag) return false;

    if (!set_argopt()) return false;

    if (!pxmlsvrcfg) {
        if (get_boolval(USER)) {
	    struct stat st;
            string home_dir, conffile;

            // iiimd run as per-user daemon.
            // use ~/.iiim/iiimd.conf as a default configuration file.
            if (!get_home_dir(home_dir)) return false;

	    conffile = home_dir + string("/.iiim/iiimd.xml.conf");
	    if (stat(conffile.c_str(), &st) == -1) {
		/* it was failed to stat. use the system-wide config */
		conffile = string(get_strval(IMSvrCfg::CONFIGFILE));
	    }
	    pxmlsvrcfg = new IMSvrXMLConf(this, conffile.c_str());
        } else {
	    pxmlsvrcfg = new IMSvrXMLConf(this, get_strval(IMSvrCfg::CONFIGFILE));
        }
    }
    if (!pxmllecfg) {
	if (get_boolval(USER)) {
	    /*
	     * iiimd run as per-user daemon.
	     * use ~/.iiim/le.xml.conf as a default configuration file.
	     */
	    string home_dir;
	    if (!get_home_dir(home_dir))
		return false;
	    pxmllecfg = iiim_le_xmlconf_new((home_dir + string("/.iiim/le.xml.conf")).c_str());
	} else {
	    pxmllecfg = iiim_le_xmlconf_new(get_strval(IMSvrCfg::LECONFFILE));
	}
    }

    /* turn on debug mode. */
    if (get_boolval(DEBUGFLAG)) {
	// output full debug info to stderr.
	IMLog::get_instance()->set_log_level(IMLog::DEBUGLOG);
	IMLog::get_instance()->set_default_destination(IMLog::IMLOG_STDERR);
	LOG_DEBUG("Turn on debug mode.");
    } else if (!get_boolval(NODAEMON)) {
	// make the program daemon.
	IMDaemon::get_instance()->setup(get_strval(IMSvrCfg::IMDIR));
    }
    /* ... actual configuration ... */
    // file base configuration.
    if (pxmlsvrcfg) {
	if (!pxmlsvrcfg->configure(pimsvr))
	    return false;
    }
    if (pxmllecfg) {
	/* don't bother the return value to allow to run without xml conf */
	iiim_le_xmlconf_load_file(pxmllecfg);
    }
    if (!set_argopt()) return false;

    get_usermgr(pimsvr)->set_command_name(get_command_name());

#if 0
    /* LE path */
    config_lepath(pimsvr, get_strval(IMSvrCfg::IFPATHNAME));
#else
    config_le(pimsvr,
	      get_strval(IMSvrCfg::IFPATHNAME),
	      *pxmllecfg,
	      get_boolval(IMSvrCfg::PREFERRED_LOADING));
#endif

    /* IMLog */
    if (!get_boolval(DEBUGFLAG)) {
	enum IMLog::LOG_LEVEL lv;
	lv = convert_log_level_string(get_strval(IMSvrCfg::LOG_LEVEL));
	if (lv == IMLog::INVALID) {
	    LOG_ERROR("Invalid log level %s.", get_strval(IMSvrCfg::LOG_LEVEL));
	    return false;
	}

	enum IMLog::LOG_DESTINATION dest;
	dest = convert_log_facility_string(get_strval(IMSvrCfg::LOG_FACILITY));
	if (lv == IMLog::INVALID) {
	    LOG_ERROR("Invalid log facility %s.", get_strval(IMSvrCfg::LOG_FACILITY));
	    return false;
	}

	IMLog::get_instance()->set_log_level(lv);
	IMLog::get_instance()->set_default_destination(dest);
	LOG_INFO("Set log facility to %s, log level to %s.",
		 get_strval(IMSvrCfg::LOG_FACILITY),		 
		 get_strval(IMSvrCfg::LOG_LEVEL));
    }


    /* By default, allow the connection from localhost. */
    get_usermgr(pimsvr)->set_entry("localhost", IMAuth::PERMIT);
    get_usermgr(pimsvr)->set_entry("localhost.localdomain", IMAuth::PERMIT);

    if (!pnsmapcfg) {
      pnsmapcfg = new IMNSMapConf(get_strval(IMSvrCfg::NSMAP_CFG_FILE));
    }

    if (pnsmapcfg) {
	pnsmapcfg->load();
    }

    int count;
    IMNsMapStruct *nsmp = pnsmapcfg->get_nsmap_info(&count);
    set_nsmap_config(pimsvr, nsmp, count);

    LOG_NORMAL("started.");

    return true;
}

bool
IMSvrArg::parse_arguments(
    int argc,
    char **argv
)
{
    int i;
    char *key;
    IMSvrArgMap::iterator it;
    enum IMSvrCfgOpt opt;

    for (i = 1; i < argc;) {
	key = argv[i];
	if (*key != '-') {
	    show_usage();
	    return false;
	}
	key++;
	it = optmap.find(key);
	if (optmap.end() == it) {
	    show_usage();
	    return false;
	}
	ArgVal &val = it->second;
	opt = val.opt;
	if (val.specified) {
	    fprintf(stderr, "Duplicated option:%s\n", key);
	    show_usage();
	    return false;
	}

	if (get_type(opt) == IMSvrCfg::ARG_BOOL) {
	    val.arg.b = true;
	    val.specified = true;
	} else {
	    i++;
	    if (i >= argc) {
		show_usage();
		return false;
	    }
	    if (get_type(opt) == IMSvrCfg::ARG_NUMBER) {
		char *ptr;
		long num;
		num = strtol(argv[i], &ptr, 10);
		if (*ptr != '\0') {
		    show_usage();
		    return false;
		}
		val.arg.n = num;
		val.specified = true;
	    } else if (get_type(opt) == IMSvrCfg::ARG_STRING) {
		val.arg.s = argv[i];
		val.specified = true;
	    } else {
		ERROR_INTERNAL("Argument type is invalid.");
	    }
	}
	i++;
    }
    return true;
}

void
IMSvrArg::addopt(
    enum IMSvrCfg::IMSvrCfgOpt opt,
    const char* option
)
{
    optmap.insert(IMSvrArgMap::value_type(option, ArgVal(opt)));
}

IMSvrArg::IMSvrArg(
    int argc,
    char **argv
) :
    IMSvrCfg(argv[0])
{
    pxmlsvrcfg = NULL;
    pxmllecfg = NULL;
    pnsmapcfg = NULL;
    initialize();
    valid_flag = parse_arguments(argc, argv);
}

IMSvrArg::~IMSvrArg()
{
    if (pxmlsvrcfg)
	delete pxmlsvrcfg;
    if (pxmllecfg)
	iiim_le_xmlconf_free(pxmllecfg);
    if (pnsmapcfg)
	delete pnsmapcfg;
}

/* Local Variables: */
/* c-file-style: "iiim-project" */
/* End: */
