#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include "LEMgr.hh"
#include "IMInputContext.hh"
#include "IMLog.hh"
#include "IMUtil.hh"

void
LEMgr::listup_LEs()
{
    DirectoryInfo di;
    DirectoryInfoVec::iterator i;
    DirectoryInfoVec *pdv;

    if (!di.refresh(lepath.c_str())) return;
    pdv = di.get_directory_info();
    for (i = pdv->begin(); i != pdv->end();i++) {
	LEBase *pleb = new LEBase(i->dirname, i->filename);
	// memory error.
	if (!pleb) return;
	if (pleb->errorp()) {
	    delete pleb;
	    continue;
	}
	lemap.insert(make_pair(i->filename, pleb));
    }
    psunim_default_lebase = new LEBase(LEBase::SUNIM_DEFAULT);
    if (!psunim_default_lebase || psunim_default_lebase->errorp()) {
	LOG_CRITICAL("Could not create default sunim LE.  Exit.");
	// TODO! we should use exception.
	exit(255);
    }
}

void
LEMgr::check_new_LEs()
{
    DirectoryInfo di;
    DirectoryInfoVec::iterator i;
    DirectoryInfoVec *pdv;

    if (!di.refresh(lepath.c_str())) return;
    pdv = di.get_directory_info();
    for (i = pdv->begin(); i != pdv->end(); ++i) {
	if (lemap.find(i->filename) != lemap.end()) continue;
	LEBase *pleb = new LEBase(i->dirname, i->filename);
	if (!pleb) return;
	if (pleb->errorp()) {
	    delete pleb;
	    continue;
	}
	lemap.insert(make_pair(i->filename, pleb));
    }
    return;
}

void
LEMgr::check_reloaded_LEs()
{
    LENameVec::iterator itl;
    LEBaseMap::iterator pos;
    LEBase *ple;
    bool reload_f = false;

    if (reload_les.empty()) return;

    for (itl = reload_les.begin(); itl != reload_les.end();) {
        pos = lemap.find(*itl);
	if (pos == lemap.end()) {
	    itl = reload_les.erase(itl);
	    continue;
	}
	ple = pos->second;

	// remove if reloading LE is failed.
	if (ple->errorp()) {
	    lemap.erase(pos);
	    reload_f = true;
	    itl = reload_les.erase(itl);
	    delete ple;
	    continue;
	}
	// succeed to reload
	if (!ple->reloadp()) {
	    itl = reload_les.erase(itl);
	    reload_f = true;
	    continue;
	}
	++itl;
    }

    if (reload_f) {
        initialize_ledata();
    }
}

void
LEMgr::reload()
{
    LEBaseMap::iterator itl;
    LEBase* ple;

    check_new_LEs();
    for (itl = lemap.begin(); itl != lemap.end(); ) {
        ple = itl->second;
        if (!ple->reload()) {
	  /* TODO */
            if (ple->errorp()) {
	        lemap.erase(itl++);
	        delete ple;
	        continue;
	    }

	    LENameVec::iterator pos;
	    pos = find(reload_les.begin(), reload_les.end(), itl->first);
	    if (pos == reload_les.end())
	    reload_les.push_back(itl->first);
	}
	++itl;
    }
    /* reload XML config file */
    pxmllecfg->reload();
    /* re-initialized */
    initialize_ledata();
}

bool
LEMgr::initialize_ledata()
{
    LEBaseMap::iterator itl;
    LEBase* ple;
    const IMLangList *piml;
    const IMDescriptorList *pimd;
    const IMObjectWithDescList *pimodl;

    langlist.clear();
    imdesclist.clear();
    imobjectdesclist.clear();

    for (itl = lemap.begin();itl != lemap.end();++itl) {
	ple = itl->second;
	piml = ple->get_langlist();
	if (piml) {
	    langlist.insert(langlist.end(), piml->begin(), piml->end());
	}
	pimd = ple->get_imdesclist();
	if (pimd) {
	    imdesclist.insert(imdesclist.end(), pimd->begin(), pimd->end());
	}
	pimodl = ple->get_objectdesclist();
	if (pimd) {
	    imobjectdesclist.insert(imobjectdesclist.end(),
				    pimodl->begin(), pimodl->end());
	}
    }

    langlist.sort();
    langlist.unique();

    ledata_inited = true;
    return true;
}

const IMLangList*
LEMgr::get_all_langlist()
{
    if (!ledata_inited) {
        initialize_ledata();
    } else {
        check_reloaded_LEs();
    }
    return &langlist;
}

const IMDescriptorList*
LEMgr::get_all_imdesclist()
{
    if (!ledata_inited) {
        initialize_ledata();
    } else {
        check_reloaded_LEs();
    }
    return &imdesclist;
}

const IMObjectWithDescList*
LEMgr::get_all_imobjectdesclist()
{
    if (!ledata_inited) {
        initialize_ledata();
    } else {
        check_reloaded_LEs();
    }
    return &imobjectdesclist;
}

LEContext*
LEMgr::choose_LE(
    IMInputContext *pic
)
{
    const ICAttribute &attr = pic->get_icattr();
    LEBaseMap::iterator itl;
    LEBase* ple;
    const IMLangList *pimll;
    const IMLang *piml = NULL;
    const string *preq_lang = NULL;
    const u16string *preq_imdesc = NULL;

    if (!attr.get_inputlanguage().empty()) {
	preq_lang = attr.get_inputlanguage().get_string();
	if (!preq_lang) goto notfound;
    }
    if (!attr.get_inputmethod().empty()) {
	preq_imdesc = &attr.get_inputmethod();
    }

    if (preq_lang)
	LOG_DEBUG("requested lang = %s", preq_lang->c_str());
    if (preq_imdesc)
	LOG_DEBUG("requested desc = %s", preq_imdesc->c_str());
    if (pxmllecfg != NULL && preq_lang != NULL && preq_imdesc == NULL) {
	/* choose LE from the order described in XML config */
	IMModuleInfoVec *lev = pxmllecfg->get_leinfo();

	for (IMModuleInfoVec::iterator i = lev->begin(); i != lev->end(); i++) {
	    LOG_DEBUG("searching lang... %s", i->lang.c_str());
	    if (*preq_lang == i->lang) {
		itl = NULL;
		LOG_DEBUG("considering to use lang %s", i->lang.c_str());
		for (ModuleInfoVec::iterator j = i->modules.begin(); j != i->modules.end(); j++) {
		    char fname[j->size()+1];
		    string filename;
		    string pathname;

		    strcpy(fname, j->c_str());
		    filename = basename(fname);
		    strcpy(fname, j->c_str());
		    pathname = dirname(fname);

		    LOG_DEBUG("searching module... %s", filename.c_str());
		    if (lepath[lepath.size()-1] == '/') {
			pathname += '/';
		    }
		    if (lepath != pathname)
			continue;
		    if ((itl = lemap.find(filename)) == lemap.end()) {
			continue;
		    } else {
			ple = itl->second;
			break;
		    }
		}
		if (itl == (LEBaseMap::iterator)NULL || itl == lemap.end()) {
			LOG_DEBUG("Not found the requested module. trying to find out with the traditional way...");
			goto retry_traditional;
		} else {
		    return ple->create_lecontext(pic, preq_lang->c_str());
		}
	    }
	}
	LOG_DEBUG("Not found the requested module. trying to find out with the traditional way...");
	goto retry_traditional;
    } else {
retry_traditional:
	for (itl = lemap.begin();itl != lemap.end();++itl) {
	    ple = itl->second;

	    // choose LE by language.
	    if (preq_lang) {
		IMLangList::const_iterator itlang;
		pimll = ple->get_langlist();
		if (!pimll) continue;
		itlang = find_if(pimll->begin(), pimll->end(), IMLangMatchPredicate(preq_lang));
		if (itlang != pimll->end()) {
		    piml = &*itlang;
		} else {
		    continue;
		}
	    }

	    // choose LE by imname.
	    if (preq_imdesc) {
		IMDescriptorList::const_iterator itimd;
		const IMDescriptorList *pimd = ple->get_imdesclist();
		if (!pimd) continue;
		itimd = find_if(pimd->begin(), pimd->end(),
				IMDescriptorMatchPredicate(preq_imdesc));
		if (itimd != pimd->end()) {
		    break;
		} else {
		    continue;
		}
	    }
	    break;
	}
	if (itl == lemap.end()) goto notfound;

	if (piml) {
	    return ple->create_lecontext(pic, piml->get_id());
	}
    }

    return ple->create_lecontext(pic, NULL);

notfound:
    return psunim_default_lebase->create_lecontext(pic, NULL);
}

LEMgr::LEMgr(
    const char* x_lepath
)
{
    lepath = x_lepath;
    pxmllecfg = NULL;
    ledata_inited = false;
    listup_LEs();
}

LEMgr::LEMgr(
    const char *x_lepath,
    IMLEXMLConf &xml
)
{
    lepath = x_lepath;
    pxmllecfg = &xml;
    ledata_inited = false;
    listup_LEs();
}

LEMgr::~LEMgr()
{
    delete_all(lemap);
}

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