/***************************************************************************
 *   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 USE_SCIM_KDE_SETTINGS
#include "socketserverthread.h"

#include "skimpluginmanager.h"
#include "skimglobalactions.h"

#include "utils/scimactions.h"

#include <qstringlist.h>
#include <qtimer.h>

#include <kapplication.h>

#define SCIM_DEFAULT_SETTINGS_GROUP "SCIM"

using namespace scim;
SocketServerThread::SocketServerThread(QObject *parent, QStringList & args)
: QObject(parent), QThread(), _panel_agent(0) {
    SCIM_DEBUG_MAIN (1) << "Initialize Socket Server...\n";

    _should_exit = false;

    default_config_module = "socket";
    for(uint argi=0; argi<args.size(); argi++)
      if(args[argi] == "c") {
        default_config_module = String(args[argi+1].latin1());
      }

    panel_agent_lock = new QMutex();

    config_module = new ConfigModule (default_config_module);
    if (config_module != NULL && config_module->valid ())
      scim_config = config_module->create_config ("scim");
    else {
        std::cerr << "Config module \"" << default_config_module << "\"cannot be loaded, using dummy Config.\n Some settings may not be able to get reloaded without restarting.";

        if (config_module) delete config_module;
        config_module = NULL;

        scim_config = new DummyConfig ();
    }

    if(args.contains("no-stay"))
      _should_resident = false;
    else
      _should_resident = true;
    config = ScimKdeSettings::self()->config();
    
    QTimer * exitChecking = new QTimer(this);
    connect(exitChecking, SIGNAL(timeout()), SLOT(exitCheckingSlot()));
    exitChecking->start(500);
}

void SocketServerThread::exitCheckingSlot() {
    kapp->lock();
    if(_should_exit)
        emit disconnectCompleted();
    kapp->unlock();
}
#include <qerrormessage.h>
bool SocketServerThread::initSocketServer(const QString & display) {
    _panel_agent = new PanelAgent ();

    if (!_panel_agent->initialize (default_config_module, String(display.utf8()), _should_resident)) {
        delete _panel_agent;
        _panel_agent = 0;
        return false;
    }

    _panel_agent->signal_connect_reload_config              (slot (this, &SocketServerThread::reloadScimConfig));
    _panel_agent->signal_connect_turn_on                    (slot (this, &SocketServerThread::slot_turn_on));
    _panel_agent->signal_connect_turn_off                   (slot (this, &SocketServerThread::slot_turn_off));
    _panel_agent->signal_connect_update_screen              (slot (this, &SocketServerThread::slot_update_screen));
    _panel_agent->signal_connect_update_spot_location       (slot (this, &SocketServerThread::slot_update_spot_location));
    _panel_agent->signal_connect_update_factory_info        (slot (this, &SocketServerThread::slot_update_factory_info));
    _panel_agent->signal_connect_show_help                  (slot (this, &SocketServerThread::slot_show_help));
    _panel_agent->signal_connect_show_factory_menu          (slot (this, &SocketServerThread::slot_show_factory_menu));
    _panel_agent->signal_connect_show_preedit_string        (slot (this, &SocketServerThread::slot_show_preedit_string));
    _panel_agent->signal_connect_show_aux_string            (slot (this, &SocketServerThread::slot_show_aux_string));
    _panel_agent->signal_connect_show_lookup_table          (slot (this, &SocketServerThread::slot_show_lookup_table));
    _panel_agent->signal_connect_hide_preedit_string        (slot (this, &SocketServerThread::slot_hide_preedit_string));
    _panel_agent->signal_connect_hide_aux_string            (slot (this, &SocketServerThread::slot_hide_aux_string));
    _panel_agent->signal_connect_hide_lookup_table          (slot (this, &SocketServerThread::slot_hide_lookup_table));
    _panel_agent->signal_connect_update_preedit_string      (slot (this, &SocketServerThread::slot_update_preedit_string));
    _panel_agent->signal_connect_update_preedit_caret       (slot (this, &SocketServerThread::slot_update_preedit_caret));
    _panel_agent->signal_connect_update_aux_string          (slot (this, &SocketServerThread::slot_update_aux_string));
    _panel_agent->signal_connect_update_lookup_table        (slot (this, &SocketServerThread::slot_update_lookup_table));
    _panel_agent->signal_connect_register_properties        (slot (this, 
                                                                &SocketServerThread::slot_register_frontend_properties));
    _panel_agent->signal_connect_update_property            (slot (this, &SocketServerThread::slot_update_frontend_property));
    _panel_agent->signal_connect_register_helper_properties (slot (this, &SocketServerThread::slot_register_helper_properties));
    _panel_agent->signal_connect_update_helper_property     (slot (this, &SocketServerThread::slot_update_helper_property));
    _panel_agent->signal_connect_register_helper            (slot (this, &SocketServerThread::slot_register_helper));
    _panel_agent->signal_connect_remove_helper              (slot (this, &SocketServerThread::slot_remove_helper));
    _panel_agent->signal_connect_lock                       (slot (this, &SocketServerThread::slot_lock));
    _panel_agent->signal_connect_unlock                     (slot (this, &SocketServerThread::slot_unlock));

    _panel_agent->get_helper_list (_helper_list);

    return true;
}

void SocketServerThread::run() {
    SCIM_DEBUG_MAIN(1) << " In Socket Server Thread...\n";
    if (!_panel_agent || !_panel_agent->run ())
        std::cerr << "Failed to run Panel.\n";
    kapp->lock();
    _should_exit = true;
    kapp->unlock();
}

void SocketServerThread::start_auto_start_helpers (void)
{
    SCIM_DEBUG_MAIN(1) << "start_auto_start_helpers ()\n";

    // Add Helper object items.
    for (size_t i = 0; i < _helper_list.size (); ++i) {
        if ((_helper_list [i].option & SCIM_HELPER_AUTO_START) != 0) {
            _panel_agent->start_helper (_helper_list [i].uuid);
        }
    }
}

void SocketServerThread::start_helper(const String & uuid)
{
    _panel_agent->start_helper (uuid);
}

void SocketServerThread::slot_update_preedit_string(const String &str, const AttributeList &attrs) {
    SCIM_DEBUG_MAIN (1) << "  socket_update_preedit_string...\n";
    kapp->lock();
    emit( updatePreeditStringReq( QString::fromUtf8( str.c_str() ), attrs ) );
    kapp->unlock();
}

void SocketServerThread::slot_update_spot_location(int x, int y) {
    kapp->lock();
    emit( updateSpotLocationReq( x, y ) );
    kapp->unlock();
}

void SocketServerThread::slot_update_frontend_property(const Property &prop) {
    slot_update_helper_property(-1, prop);
}

void SocketServerThread::slot_update_helper_property(int id, const Property &prop) {
    
    SCIM_DEBUG_MAIN (2) << "  socket_update_property on client\n";

    kapp->lock();
    emit updateProperty(prop, id);
    kapp->unlock();
}

void SocketServerThread::slot_register_frontend_properties(const PropertyList &props) {
    slot_register_helper_properties(-1, props);
}

void SocketServerThread::slot_register_helper_properties (int id, const PropertyList &props) {
    SCIM_DEBUG_MAIN (1) << "  Register properties...\n";

    kapp->lock();
    emit registerProperties(props, id);
    kapp->unlock();
}

void SocketServerThread::slot_register_helper (int id, const HelperInfo &helper)
{
    kapp->lock();
    emit registerHelper(id, helper);
    kapp->unlock();
}

void SocketServerThread::slot_lock (void)
{
    panel_agent_lock->lock();
}

void SocketServerThread::slot_unlock (void)
{
    panel_agent_lock->unlock();
}

void SocketServerThread::slot_remove_helper (int id)
{
    kapp->lock();
    emit removeHelper(id);
    kapp->unlock();
}

void SocketServerThread::slot_show_preedit_string () {
    SCIM_DEBUG_MAIN (1) << "  Show preedit string...\n";
    kapp->lock();
    emit( showPreeditStringReq() );
    kapp->unlock();
}

void SocketServerThread::slot_show_aux_string () {
    SCIM_DEBUG_MAIN (1) << "  Show aux string...\n";
    kapp->lock();
    emit( showAuxStringReq() );
    kapp->unlock();
}

void SocketServerThread::slot_show_lookup_table () {
    SCIM_DEBUG_MAIN (1) << "  Show lookup table...\n";
    kapp->lock();
    emit( showLookupTableReq() );
    kapp->unlock();
}

void SocketServerThread::slot_turn_on () {
    SCIM_DEBUG_MAIN (1) << "  Turn on panel...\n";
    kapp->lock();
    emit( turnOnPanelReq() );
    kapp->unlock();
}

void SocketServerThread::slot_hide_preedit_string () {
    SCIM_DEBUG_MAIN (1) << "  Hide preedit string...\n";
    kapp->lock();
    emit( hidePreeditStringReq() );
    kapp->unlock();
}

void SocketServerThread::slot_hide_aux_string () {
    SCIM_DEBUG_MAIN (1) << "  Hide aux string...\n";
    kapp->lock();
    emit( hideAuxStringReq() );
    kapp->unlock();
}

void SocketServerThread::slot_hide_lookup_table () {
    SCIM_DEBUG_MAIN (1) << "  Hide lookup table...\n";
    kapp->lock();
    emit( hideLookupTableReq() );
    kapp->unlock();
}

void SocketServerThread::slot_turn_off() {
    SCIM_DEBUG_MAIN (1) << "  socket_turn_off...\n";
    kapp->lock();
    emit( turnOffPanelReq() );
    kapp->unlock();
}


void SocketServerThread::slot_update_screen (int screen) {
    SCIM_DEBUG_MAIN (1) << "  socket_update_screen...\n";

    kapp->lock();
    emit( updateScreenReq( screen ) );
    kapp->unlock();
}
void SocketServerThread::slot_update_factory_info(const PanelFactoryInfo &info) {
    SCIM_DEBUG_MAIN (1) << "  socket_update_factory_info...\n";

    kapp->lock();
    emit( updateFactoryInfoReq( info ) );
    kapp->unlock();
}

void SocketServerThread::slot_show_help(const String &help) {
    kapp->lock();
    emit( showHelp( QString::fromUtf8( help.c_str() ) ) );
    kapp->unlock();
}

void SocketServerThread::slot_show_factory_menu (const std::vector <scim::PanelFactoryInfo> &menu) {
    SCIM_DEBUG_MAIN (1) << "  socket_show_factory_menu...\n";

    kapp->lock();
    emit( showFactoryMenu( menu ) );
    kapp->unlock();
}

void SocketServerThread::slot_update_preedit_caret (int caret) {
//     SCIM_DEBUG_MAIN (1) << "  Update preedit caret...\n";
    kapp->lock();
    emit( updatePreeditCaretReq( caret ) );
    kapp->unlock();
}

void SocketServerThread::slot_update_aux_string(const String &str, const AttributeList &attrs) {
    SCIM_DEBUG_MAIN (1) << "  Update aux string...\n";

    kapp->lock();
    emit( updateAuxStringReq( QString::fromUtf8(str.c_str()), attrs ) );
    kapp->unlock();
}

void SocketServerThread::slot_update_lookup_table(const LookupTable &table) {
    SCIM_DEBUG_MAIN (1) << "  Update lookup table...\n";

    size_t i;
    size_t item_num = table.get_current_page_size ();

    String         mbs;
    WideString     wcs, label;
    AttributeList  attrs;
    QValueList<AttributeList> attrslist;
    QStringList    items, labels;
    for (i = 0; i < SCIM_LOOKUP_TABLE_MAX_PAGESIZE; ++ i) {
        if (i < item_num) {
            mbs = String ();

            wcs = table.get_candidate_in_current_page (i);

            label =  table.get_candidate_label (i);

            labels <<  QString::fromUtf8( utf8_wcstombs (label).c_str() );

            mbs = utf8_wcstombs (wcs);

            // Update attributes;
            attrs = table.get_attributes_in_current_page (i);
            attrslist.push_back(attrs);
            items << QString::fromUtf8( mbs.c_str() );
        }
    }

    kapp->lock();
    emit( updateLookupTableReq( labels, items, attrslist, 
        table.is_cursor_visible () ? table.get_cursor_pos_in_current_page () : -1,
        table.get_current_page_start (), table.number_of_candidates (), item_num ) );
    kapp->unlock();
    if ( item_num < (uint)table.get_current_page_size ())
        _panel_agent->update_lookup_table_page_size (item_num);
}

void SocketServerThread::reloadScimConfig () {
    if(!_panel_agent)
        return;

    _panel_agent->reload_config ();

     if (!scim_config.null ()) scim_config->reload ();
}

SocketServerThread::~SocketServerThread () {
    scim_config.reset ();

    if (config_module) {
        SCIM_DEBUG_FRONTEND(2) << " Deleting _config_module...\n";
        delete config_module;
        config_module = 0;
    }

    delete panel_agent_lock;
}

void SocketServerThread::shutdown () {
    _panel_agent->exit();
}

void SocketServerThread::selectLookupTableItem(int item) {
    _panel_agent->select_candidate((uint32)item);
}

void SocketServerThread::lookupTablePageDown() {
    _panel_agent->lookup_table_page_down ();
}
void SocketServerThread::lookupTablePageUp() {
    _panel_agent->lookup_table_page_up ();
}
void SocketServerThread::activateProperty(int client, const scim::String & property_key)
{
    if(client < 0) {  //this is a frontend property
        _panel_agent->trigger_property (property_key);
    } else {
        _panel_agent->trigger_helper_property (client, property_key);
    }
}
void SocketServerThread::getFactoryList() {
    _panel_agent->request_factory_menu ();
}

void SocketServerThread::changeFactory(const QString &uuid) {
    _panel_agent->change_factory (String(uuid.latin1()));
}

void SocketServerThread::getHelp() {
    if (!_panel_agent->request_help ()) {
        QString help 
             =  i18n("SKIM: KDE frontend for SCIM %1\n").arg(VERSION) +
                i18n("(C) 2003-2005 LiuCougar <liuspider@users.sourceforge.net>");
        kapp->lock();
        emit showHelp(help);
        kapp->unlock();
    }
}
void SocketServerThread::setOverloadedModules(QStringList & l)
{
    if(overloadedScimModules != l) {
        overloadedScimModules = l;
        emit standaloneHelpersChanged();
    }
}

void SocketServerThread::getStandaloneHelperList(std::vector<HelperInfo> & stdal) const
{
    stdal.clear();
    for (size_t i = 0; i < _helper_list.size (); ++i) {
        if ((_helper_list [i].option & scim::SCIM_HELPER_STAND_ALONE) != 0 &&
            (_helper_list [i].option & scim::SCIM_HELPER_AUTO_START) == 0 &&
            //if this scim module is not overloaded by a skim one
            !overloadedScimModules.contains(QString(_helper_list [i].uuid.c_str())) ) {
            stdal.push_back(_helper_list [i]);
        }
    }
}

