/***************************************************************************
 *   Copyright (C) 2003-2005 by liuspider                                  *
 *   liuspider@users.sourceforge.net                                       *
 *                                                                         *
 *   This program 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.                                   *
 ***************************************************************************/
#define Uses_SCIM_CONFIG_PATH
#define Uses_SCIM_HOTKEY

#include "scimimenginesettings.h"
#include "scimimenginesettingsui.h"
#include "skimkeygrabber.h"
#include <qlistview.h> 
#include <qpushbutton.h>
#include <qlayout.h>
#include <qhbox.h>
#include <qvbox.h>
#include <qpixmap.h> 
#include <qiconset.h> 
#include <kgenericfactory.h>
#include <klocale.h>

typedef KGenericFactory<ScimIMEngineSettings> KCMScimIMEngSettingsLoaderFactory;

//copied from scim/modules/SetupUI/scim_imengine_setup.cpp
typedef std::map <scim::String, scim::KeyEventList>                                       MapStringKeyEventList;

K_EXPORT_COMPONENT_FACTORY( kcm_skimplugin_scim_imengines, 
    KCMScimIMEngSettingsLoaderFactory( "kcm_skimplugin_scim_imengines" ) )

class ScimIMEngineSettings::ScimIMEngineSettingsPrivate {
public:
    struct itemExtraInfo{
        bool enabled;
        int uuidIndex;
    };
    QMap<QCheckListItem*, itemExtraInfo> itemInfos;
    QStringList uuids;
    QMap<QString, QString> hotkey_map;
    bool enabledIMEsChanged, hotkeysChanged;
};

ScimIMEngineSettings::ScimIMEngineSettings(QWidget *parent, 
  const char */*name*/, const QStringList &args)
 : KAutoCModule( KCMScimIMEngSettingsLoaderFactory::instance(), parent, args ),
   d(new ScimIMEngineSettingsPrivate)
{
    ui = new SCIMIMEConfigUI(this);
    setMainWidget(ui);
    connect(ui->listView, SIGNAL(clicked(QListViewItem *)), SLOT(checkBoxModified(QListViewItem*)));
    connect(ui->listView, SIGNAL(currentChanged(QListViewItem *)), SLOT(updateEditHotkeysBtnStates(QListViewItem *)));
    connect(ui->enableAllBtn, SIGNAL(clicked()), SLOT(enableAllIMEs()));
    connect(ui->disableAllBtn, SIGNAL(clicked()), SLOT(disableAllIMEs()));
    connect(ui->editHotkeysBtn, SIGNAL(clicked()), SLOT(editHotkeys()));

    m_config = SkimPluginManager::scimConfigObject();

    d->enabledIMEsChanged = d->hotkeysChanged = false;

    load();
    
    ui->editHotkeysBtn->setEnabled(false);
}

void ScimIMEngineSettings::updateEditHotkeysBtnStates(QListViewItem * curit){
    if(!curit)
      return;
      
    QCheckListItem * item;
    if(!(item = dynamic_cast<QCheckListItem *>(curit)))
      return;
      
    if(item->type() == QCheckListItem::CheckBox)
        ui->editHotkeysBtn->setEnabled(true);
    else
        ui->editHotkeysBtn->setEnabled(false);
}

void ScimIMEngineSettings::enableAllIMEs() {
    toggleAllIMEs(true);
}

void ScimIMEngineSettings::disableAllIMEs() {
    toggleAllIMEs(false);
}

void ScimIMEngineSettings::toggleAllIMEs(bool enabled) {
    bool sthchanged = false;
    QListViewItemIterator it(ui->listView);
    QCheckListItem * item;
    while ( it.current() ) {
        if((item = dynamic_cast<QCheckListItem *>(it.current())) && item->type() == QCheckListItem::CheckBox && 
            d->itemInfos.contains(item)) {
            item->setOn(enabled);
            if(item->isOn() != d->itemInfos[item].enabled)
                sthchanged = true;
        }
        it++;
    }
    if(sthchanged)
        emit changed(true);
}

#define PIX_COLUMN 0
#define NAME_COLUMN 0
#define HOT_KEYS_COLUMN 1
void ScimIMEngineSettings::load() {
    clear();
    
    {// Load Hotkeys.
        scim::IMEngineHotkeyMatcher hotkey_matcher;
//         m_config->reload();
        hotkey_matcher.load_hotkeys (m_config);
        scim::KeyEventList keys;
        std::vector <scim::String> uuids;
        MapStringKeyEventList hotkey_map;
        
        if (hotkey_matcher.get_hotkeys (keys, uuids) > 0) {
            scim::String str;
            
            for (uint i = 0; i < keys.size (); ++i) {
                hotkey_map[uuids[i]].push_back(keys [i]);
            }
            
            for (MapStringKeyEventList::iterator it = hotkey_map.begin (); it != hotkey_map.end (); ++it){
                scim::scim_key_list_to_string (str, it->second);
                d->hotkey_map [it->first.c_str()] = str.c_str();
            }
        }
    }

    QStringList & uuids = d->uuids;
    QStringList names;
    QStringList langs;
    std::vector <scim::String> icons;
    
    std::map <QString, std::vector <size_t> > groups;
    
    QString      lang_name;
    size_t i;
    
    get_factory_list (uuids, names, langs, icons);

    for (i = 0; i < uuids.size (); ++i) {
        groups [langs [i]].push_back (i);
    }
    
    // Add language group
    for (std::map <QString, std::vector <size_t> >::iterator it = groups.begin ();
         it != groups.end (); ++ it) {
        scim::String raw_name(it->first.utf8());
        lang_name = QString::fromUtf8(scim::scim_get_language_name (raw_name).c_str());
        QCheckListItem * langGroupItem = 
          new QCheckListItem(ui->listView, lang_name, QCheckListItem::CheckBoxController);

        // Add factories for this group
//         bool inconsistent = false;
        std::vector<scim::String> disabled;
        disabled = scim::scim_global_config_read (
            scim::String (SCIM_GLOBAL_CONFIG_DISABLED_IMENGINE_FACTORIES), disabled);
        QString disabledFactories = QString::fromLatin1(scim::scim_combine_string_list(disabled).c_str());
        for (i = 0; i < it->second.size (); ++i) {
          size_t id = it->second [i];
          QCheckListItem * item =
            new QCheckListItem(langGroupItem, names [id], QCheckListItem::CheckBox);
          ScimIMEngineSettingsPrivate::itemExtraInfo info = {false, it->second [i]};
          QPixmap * p = new QPixmap(icons [id].c_str ());
          QIconSet * icon = new QIconSet(*p);
          pixmaps.push_back(p);
          iconsets.push_back(icon);
          item->setPixmap(PIX_COLUMN, icon->pixmap(QIconSet::Automatic, QIconSet::Normal));
          if(!disabledFactories.contains(uuids[id])) {
            item->setOn(true);
            info.enabled = true;
          }
          d->itemInfos[item] = info;
          if(d->hotkey_map.contains(uuids[id]))
            item->setText(HOT_KEYS_COLUMN, d->hotkey_map[uuids[id]]);
//             inconsistent = true;
        }
//         if(inconsistent)
//           langGroupItem->setState(QCheckListItem::NoChange);
    }
}

void ScimIMEngineSettings::save()
{
    QCheckListItem * item;
    { //write disabled IMEs
        std::vector<scim::String> disabledUUIDsList;
        QListViewItemIterator it(ui->listView/*, QListViewItemIterator::NotChecked*/);
        while ( it.current() ) {
            if((item = dynamic_cast<QCheckListItem *>(it.current())) && d->itemInfos.contains(item)) {
                d->itemInfos[item].enabled = item->isOn();
                if(!item->isOn())
                    disabledUUIDsList.push_back(d->uuids[d->itemInfos[item].uuidIndex].latin1());
            }
            it++;
        }
        
        scim::scim_global_config_write (
            scim::String (SCIM_GLOBAL_CONFIG_DISABLED_IMENGINE_FACTORIES), disabledUUIDsList);
    }

    {// Save Hotkeys.
        scim::IMEngineHotkeyMatcher hotkey_matcher;
    
        QListViewItemIterator it(ui->listView);
        scim::KeyEventList keylist;
        d->hotkey_map.clear();
        while ( it.current() ) {
            if((item = dynamic_cast<QCheckListItem *>(it.current())) && item->type() == QCheckListItem::CheckBox
                && d->itemInfos.contains(item)) {
                if(!item->text(HOT_KEYS_COLUMN).isNull()) {
                    if (scim::scim_string_to_key_list (keylist, scim::String (item->text(HOT_KEYS_COLUMN).latin1()))) {
                        hotkey_matcher.add_hotkeys (keylist, d->uuids[d->itemInfos[item].uuidIndex].latin1());
                        d->hotkey_map[d->uuids[d->itemInfos[item].uuidIndex]] = item->text(HOT_KEYS_COLUMN);
                    }
                }
            }
            it++;
        }
    
        hotkey_matcher.save_hotkeys (m_config);
    }
    d->enabledIMEsChanged = d->hotkeysChanged = false;
    m_config->flush();
}

void ScimIMEngineSettings::editHotkeys(){
    bool sthchanged = false;
    QListViewItem * cit = ui->listView->currentItem();
    QCheckListItem * item;
    if(!cit || !(item = dynamic_cast<QCheckListItem *>(cit)) || item->type() != QCheckListItem::CheckBox) {
        ui->editHotkeysBtn->setEnabled(false);
        return;
    }
    SkimShortcutListEditor keyEditor(ui);
    QStringList keylist = QStringList::split(',', item->text(HOT_KEYS_COLUMN));
    keyEditor.setStringList(keylist);
    keyEditor.setCaption(i18n("Specify Hotkeys for IME \"%1\"").arg(item->text(NAME_COLUMN)));
    if(keyEditor.exec() == QDialog::Accepted ) {
        item->setText(HOT_KEYS_COLUMN, keyEditor.getCombinedString());
        if(d->itemInfos.contains(item))
            if(d->hotkey_map.contains(d->uuids[d->itemInfos[item].uuidIndex])) {
                if(item->text(HOT_KEYS_COLUMN) != d->hotkey_map[d->uuids[d->itemInfos[item].uuidIndex]])
                    sthchanged = true;
            } else if(!item->text(HOT_KEYS_COLUMN).isNull())
                sthchanged = true;
    }
    
    if(sthchanged) {
        d->hotkeysChanged = true;
        emit changed(true);
    } else {
        d->hotkeysChanged = false;
        QListViewItemIterator it(ui->listView);
        while ( it.current() ) {
            if((item = dynamic_cast<QCheckListItem *>(it.current()))) {
            if(d->itemInfos.contains(item)) {
                if(d->hotkey_map.contains(d->uuids[d->itemInfos[item].uuidIndex])) {
                    if(item->text(HOT_KEYS_COLUMN) != d->hotkey_map[d->uuids[d->itemInfos[item].uuidIndex]]) {
                        d->hotkeysChanged = true;
                        break;
                    }
                } else if(!item->text(HOT_KEYS_COLUMN).isNull()){
                    d->hotkeysChanged = true;
                    break;
                }
            }
            }
            it++;
        }
        if(!it.current() && !d->enabledIMEsChanged)
            emit changed(false);
        else
            emit changed(true);
     }
}

void ScimIMEngineSettings::defaults()
{
    QCheckListItem * item;
    QListViewItemIterator it(ui->listView);
    while ( it.current() ) {
        if((item = dynamic_cast<QCheckListItem *>(it.current()))) {
          item->setOn(true);
        }
        it++;
    }
}

void ScimIMEngineSettings::checkBoxModified(QListViewItem * curit)
{
    if(!curit)
      return;
      
    QCheckListItem * item;
    if(!(item = dynamic_cast<QCheckListItem *>(curit)))
      return;
      
    if(item->type() != QCheckListItem::CheckBox 
        && item->type() != QCheckListItem::CheckBoxController )
      return;

    QListViewItemIterator it(ui->listView/*, QListViewItemIterator::Checked*/);
    d->enabledIMEsChanged = false;
    while ( it.current() ) {
        if((item = dynamic_cast<QCheckListItem *>(it.current()))) {
          if(d->itemInfos.contains(item)) {
            if(item->isOn() != d->itemInfos[item].enabled) {
              d->enabledIMEsChanged = true;
              break;
            }
          }
        }
        it++;
    }
    if(!it.current() && !d->hotkeysChanged)
        emit changed(false);
    else
        emit changed(true);
}

ScimIMEngineSettings::~ScimIMEngineSettings()
{
    clear();
//     delete m_config;
//     if (cmodule && cmodule->valid ())
//         delete cmodule;
}

void ScimIMEngineSettings::clear()
{
    d->hotkey_map.clear();
    d->itemInfos.clear();
    ui->listView->clear();
    for(uint i = 0; i < iconsets.size(); i++)
      delete iconsets[i];
    for(uint i = 0; i < pixmaps.size(); i++)
      delete pixmaps[i];
    iconsets.clear();
    pixmaps.clear();
}

void ScimIMEngineSettings::get_factory_list (QStringList &uuids,
                          QStringList &names,
                          QStringList &langs,
                          std::vector <scim::String> &icons)
{
    std::vector<scim::String>    module_list;
    scim::IMEngineFactoryPointer factory;
    scim::IMEngineModule         module;

    scim::scim_get_imengine_module_list (module_list);

    uuids.clear ();
    names.clear ();
    langs.clear ();
    icons.clear ();


    for (size_t i = 0; i < module_list.size (); ++ i) {
        module.load (module_list [i], m_config);
        if (module.valid ()) {
            for (size_t j = 0; j < module.number_of_factories (); ++j) {
                //FIXME
//                 try {
                    factory = module.create_factory (j);
//                 } catch (...) {
//                     factory.reset ();
//                 }

                if (!factory.null ()) {
                    if (uuids.find(QString::fromLatin1(factory->get_uuid ().c_str())) == uuids.end ()) {
                        uuids.push_back (QString::fromLatin1(factory->get_uuid ().c_str()));
                        names.push_back (
                          QString::fromUtf8(scim::utf8_wcstombs (factory->get_name ()).c_str()));
                        langs.push_back (QString::fromUtf8(
                          scim::scim_get_normalized_language (factory->get_language ()).c_str()));
                        icons.push_back (factory->get_icon_file ());
                    }
                    factory.reset ();
                }
            }
            module.unload ();
        }
    }
}

#include "scimimenginesettings.moc"
