#include <config.h>
#include <stdio.h>
#include <vector>
#include "IIIMP_ICState.hh"
#include "IIIMPTrans.hh"
#include "IIIMPUtil.hh"
#include "IMHotkeyPrimitive.hh"
#include "IIIMP_hotkey.hh"
#include "IIIMP_hotkey_profile.hh"

IIIMP_hotkey::IIIMP_hotkey(
    IIIMP_ICState *hics
) 
{
    hotkey_mode = false;
    lang_selection = false;
    forward_event = false;
    first_candidate = -1;
    last_candidate = -1;
    current = -1;
    curr_hotkeyid = -1;
    curr_langid = 0;
}

IIIMP_hotkey::~IIIMP_hotkey()
{   
}

bool
IIIMP_hotkey::process_hotkey(
    IIIMP_ICState *hics,
    int hotkey_id,
    int index
)
{
    bool result = false;
    bool flag;
    IIIMP_message *p;

    curr_hotkeyid = hotkey_id;

    if (hotkey_id == (hics->get_hotkey_profile()->get_default_hotkey_lang_switch_id())) {
	if (get_lang_selection()) {
	    set_lang_selection(false);
	    result = stop_lang_selection_window(hics);
	} else {
	    set_lang_selection(true);
	    result = start_lang_selection_window(hics);
	}
    } else if (hotkey_id == hics->get_hotkey_profile()->get_default_hotkey_trigger_notify_id()) {
	if (hics->pshared->conversion_mode)
	    flag = false;
	else 
	    flag = true;
	result = hics->get_ichandler()->toggle_conversion(hics->get_imlexec(), flag);
        hics->pshared->conversion_mode = flag;
        if ((hics->start_request()->dealing()) && result) {
	    if (!hics->pshared->conversion_mode) {
		p = iiimp_hotkey_state_notify_new(hics->get_iiimptrans()->get_data_s(), hics->get_im_id(), hics->get_ic_id(), hotkey_id, EVENT_FORWARDING_OFF);
		if (!hics->send(p, true)) return false;
		return true;
	    } 
	    return result;
        }
    } else if (hotkey_id == hics->get_hotkey_profile()->get_default_hotkey_cycle_lang_switch_id()) {
	set_lang_selection(true);
	result = do_cycle_lang_selection(hics, 1);
    } else if (hotkey_id == hics->get_hotkey_profile()->get_default_hotkey_reverse_cycle_lang_switch_id()) {
	set_lang_selection(true);
	result = do_cycle_lang_selection(hics, -1);
    }else {
        if (forward_hotkey_to_LE(hics, hotkey_id, index)) {
	  // if (!hics->pshared->conversion_mode)
	    // hics->get_ichandler()->toggle_conversion(hics->get_imlexec(), false);
          result = hics->start_request()->dealing();
	  p = iiimp_hotkey_state_notify_new(hics->get_iiimptrans()->get_data_s(), hics->get_im_id(), hics->get_ic_id(), hotkey_id, EVENT_FORWARDING_OFF);
	  if (!hics->send(p, true)) return false;
        }
    }
    return result;
}

bool
IIIMP_hotkey::forward_hotkey_to_LE(
    IIIMP_ICState *hics,
    int hotkey_id,
    int index
)
{
    bool result = false;

    const IMHotkeyProfileList *hkplist = hics->get_hotkey_profile()->get_profile_list();
    for (IMHotkeyProfileList::const_iterator it = hkplist->begin();
         it != hkplist->end();
         ++it) {
        const IMHotkeyList *phklist = it->get_hotkey_list();
	IMHotkeyList::const_iterator it2 = phklist->begin();
        for (; it2 != phklist->end(); ++it2) {
	    if (hotkey_id == it2->get_hotkey_id()) {
                const IMKeySpecList *keylist = it2->get_keylist();
		IMKeySpecList::const_iterator it3 = keylist->begin();
		// vector<IMKeyEventStruct> keyvec(keylist->size());
		vector<IMKeyEventStruct> keyvec;
		IMInputEvent ev;
		IMKeyEventStruct kev;

		for (int i=0; it3 != keylist->end(); ++it3, ++i) {
		    if (i == index) {
			kev.keyCode = it3->get_keycode();
			kev.keyChar = it3->get_keychar();
			kev.modifier = it3->get_modifier();
			kev.time_stamp = it3->get_timestamp();
			keyvec.push_back(kev);
    
			IMKeyListEvent *pimkey = &ev.keylist;
			memset(pimkey, 0,sizeof(*pimkey));
			pimkey->type = IM_HotkeyEvent;
			pimkey->n_operation = hotkey_id;  // Use the existing IMKeyListEvent
                                                          // to store the Hotkey details
			pimkey->n_key = index;            // pimkey->n_operation holds hotkey_id 
                                                          // pimkey->n_key holds the Index
			pimkey->keylist = &keyvec[0];
  
			result = hics->get_ichandler()->send_event(hics->get_imlexec(), &ev); 
			return result;
		    }
                }
	    }
	}
    }
    return result;
}

bool
IIIMP_hotkey::send_hotkey_reply(
    IIIMP_ICState *hics
)
{
    IIIMP_message *p;
    p = iiimp_hotkey_notify_reply_new(hics->get_iiimptrans()->get_data_s(), hics->get_im_id(), hics->get_ic_id());   
    set_hotkey_mode(false);
    if (!hics->send(p, true)) return false;
    return true;
}

bool
IIIMP_hotkey::start_lang_selection_window(
    IIIMP_ICState *hics
)
{
    IMLangList *planglist = hics->get_langlist(hics->get_ichandler()->get_current_desktop());
    IIIMP_message *p;

    p = iiimp_lookup_choice_start_new(hics->get_iiimptrans()->get_data_s(),
				      hics->get_im_id(),
				      hics->get_ic_id(),
				      IM_LOOKUP_CHOICE_START_SERVER_IS_MASTER,
				      planglist->size(),
				      planglist->size(),
				      1,
				      IM_LOOKUP_CHOICE_START_DRAWING_UP_HORIZONTALLY,
				      IM_LOOKUP_CHOICE_START_SERVER_OWNS_LABEL);
    if (!hics->send(p, true)) return false;
   
    hics->create_langimlist();

    return true;
}

void
IIIMP_hotkey::print_LangIMList(
    IIIMP_ICState *hics
)
{
    LangIMList::iterator it;
    LangIMList *plangimlist = hics->get_langimlist();

    for (it = plangimlist->begin (); it != plangimlist->end (); it++){
	u16string imname = it->get_hrn();
        if (imname.get_charstr() == NULL) {
	    imname  = it->get_imname();
        }
	LOG_DEBUG("Index [%d], Lang_ID [%s], IMName [%s]\n",it->get_index(), it->get_id(), imname.get_charstr());
    }
}

bool
IIIMP_hotkey::stop_lang_selection_window(
    IIIMP_ICState *hics
)
{
    IIIMP_message *p;
    p = iiimp_lookup_choice_done_new(hics->get_iiimptrans()->get_data_s(),
				     hics->get_im_id(),
				     hics->get_ic_id());
    if (!hics->send(p, true)) return false;
    return true;
}

int
IIIMP_hotkey::UTFCHARLen(
    UTFCHAR * p
)
{
    int i;
    for (i = 0; *p; i++)
	p++;
    return i;
}

int
IIIMP_hotkey::UTFCHARLen(
    const char *p
)
{
    int i;
    for (i = 0; *p; i++)
	p++;
    return i;
}

int
IIIMP_hotkey::UTFCHARCpy(
    UTFCHAR * dest,
    const char *original
)
{
    int i;
    for (i = 0; *original; i++) {
	*dest++ = *original++;
    }
    *dest = 0;
    return i;
}

int
IIIMP_hotkey::UTFCHARCpy(
    UTFCHAR * dest,
    UTFCHAR * original
)
{
    int i;
    for (i = 0; *original; i++) {
	*dest++ = *original++;
    }
    *dest = 0;
    return i;
}

int 
IIIMP_hotkey::UTFCHARCat(
    UTFCHAR *dest,
    UTFCHAR *str1,
    UTFCHAR *str2
)
{
    int i;
    for (i = 0; *str1; i++) {
	*dest++ = *str1++;
    }
    for (i = 0; *str2; i++) {
	*dest++ = *str2++;
    }
    *dest = 0;
    return i;
}

int
IIIMP_hotkey::UTFCHARCat(
    UTFCHAR *dest, 
    const char *str1, 
    const char *str2,
    const char *str3,
    const char *str4
)
{
    int i;
    for (i = 0; *str1; i++) { 
	*dest++ = *str1++;
    }
    for (i = 0; *str2; i++) {
	*dest++ = *str2++;
    }
    for (i = 0; *str3; i++) {
	*dest++ = *str3++;
    }
    for (i = 0; *str4; i++) {
	*dest++ = *str4++;
    }
    *dest = 0;
    return i;
} 

IMFeedbackList *
IIIMP_hotkey::create_feedback(
    int size
)
{
    int i;
    IMFeedbackList *feedback, *fbl;

    feedback = (IMFeedbackList *) calloc(size, sizeof(IMFeedbackList));
    for (i = 0; i < size; i++) {
	fbl = &feedback[i];
	fbl->count_feedbacks = 1;
	fbl->feedbacks = (IMFeedback *) calloc(4, sizeof(IMFeedback));
	memset(fbl->feedbacks, 0, sizeof(IMFeedback) * 4);
    }
    return feedback;
}

void
IIIMP_hotkey::free_feedback(
    IMFeedbackList *feedback,
    int size
)
{
    int i;
    IMFeedbackList *fbl;

    if (feedback == NULL) return;

    for (i = 0; i < size; i++) {
	fbl = &feedback[i];
	if (fbl->feedbacks != NULL)
	    free(fbl->feedbacks);
    }
    free(feedback);
}

bool
IIIMP_hotkey::process_lang_selection_window(
    IIIMP_ICState *hics,
    IIIMP_message *pmes
)
{
    IIIMP_contents *pc = pmes->v.forward_event.contents;
    IIIMP_keyevent *piiimpkey = pc->value.keyevent_list->keyevent;
    int keycode = piiimpkey->keycode;
    int index = 0;
    IIIMP_message *p;
    int hotkey_id = 0;

    hotkey_id = check_for_hotkey(hics, piiimpkey);

    if (hotkey_id) process_hotkey(hics, hotkey_id, 0);

    // print_LangIMList(hics);

    switch(keycode) {
      case IM_VK_PAGE_UP:
      case IM_VK_PAGE_DOWN:
      case IM_VK_SPACE:
      case IM_VK_DOWN:
      case IM_VK_UP:
      case IM_VK_LEFT:
      case IM_VK_RIGHT:
       draw_lang_selection_window(hics, keycode);
       break;

      case IM_VK_ENTER:
       index = current + 1;
       if (switch_language(hics, index, curr_hotkeyid)) return true;
       break;
      
      case IM_VK_1:
      case IM_VK_2:
      case IM_VK_3:
      case IM_VK_4:
      case IM_VK_5:
      case IM_VK_6:
      case IM_VK_7:
      case IM_VK_8:
      case IM_VK_9:
       index = first_candidate + ((piiimpkey->keychar - '0') % 10);
       if (switch_language(hics, index, curr_hotkeyid)) return true;
       break;
      case IM_VK_ESCAPE:
       if (get_lang_selection()) {
	   set_lang_selection(false);
	   stop_lang_selection_window(hics);
       } 
       break;
    }
    p = iiimp_forward_event_reply_new(hics->get_iiimptrans()->get_data_s(),
				      hics->get_im_id(),
				      hics->get_ic_id());
    if (!hics->send(p, true)) return false;
    set_forward_event(false);
    return true;
}

int
IIIMP_hotkey::check_for_hotkey(
    IIIMP_ICState *hics,
    IIIMP_keyevent *kev
)
{
    int hotkey_id = 0;
    size_t i,j;

    const IMHotkeyProfileList *hkplist = hics->get_hotkey_profile()->get_profile_list();
    IMHotkeyProfileList::const_iterator it1 = hkplist->begin();
    for (it1 = hkplist->begin(); it1 != hkplist->end(); it1++) {
	const IMHotkeyList *phklist = it1->get_hotkey_list();
        IMHotkeyList::const_iterator it2 = phklist->begin();
	for (i = 0; it2 != phklist->end(), i < phklist->size(); ++it2, ++i) {
	    const IMKeySpecList *keylist = it2->get_keylist();
            IMKeySpecList::const_iterator it3 = keylist->begin();
	    for (j = 0; it3 != keylist->end(), j < keylist->size(); ++it3, ++j) {
		if ((kev->keycode == it3->get_keycode()) && (kev->modifier == it3->get_modifier())) {
		    return it2->get_hotkey_id();
		}
	    }
	}
    } 
    return hotkey_id;
}

bool
IIIMP_hotkey::switch_language(
    IIIMP_ICState *hics,
    int index,
    int hotkey_id
)
{
    IIIMP_message *p;
    LangIMList::iterator it;
    LangIMList *plangimlist = hics->get_langimlist();
    ICAttribute attr;
    bool result = false;

    hics->get_ichandler()->toggle_conversion(hics->get_imlexec(), false);
    hics->pshared->conversion_mode = false;

    for (it = plangimlist->begin (); it != plangimlist->end (); it++){
	if (index == it->get_index()) {
	    u16string imname = it->get_imname();

            // Select the Client's profile to the selected LE's profile
            IIIMP_card16 scope_and_profile_id = 0;
	    const IMHotkeyProfileStruct *hkps = it->get_hotkey_profile();

	    // safe enough to use default profile anyway
	    int p_id = hics->get_hotkey_profile()->get_default_hotkey_profile_id();
            if (hkps != NULL) {
		IMHotkeyProfileList::iterator it1;
		IMHotkeyProfileList *hkplist = const_cast<IMHotkeyProfileList*>(hics->get_hotkey_profile()->get_profile_list());
		for (it1 = hkplist->begin(); it1 != hkplist->end(); it1++) {
		    if (it1->get_hotkey_profile_struct()->name == hkps->name) { // crack, crack
			p_id = it1->get_profile_id();
			break;
		    }
		}
	    }
	    LOG_DEBUG("IIIMP_hotkey:Switching profile to [%d] for [%s]\n",p_id, imname.get_charstr());
            scope_and_profile_id = (GLOBAL_HOTKEY << 15) | p_id;
	    p = iiimp_select_hotkey_profile_new(hics->get_iiimptrans()->get_data_s(), hics->get_im_id(), scope_and_profile_id);
	    if (!hics->send(p, true)) return false;
            
	    attr.set_inputlanguage(it->get_id());
	    attr.set_inputmethod(imname);
	    hics->get_ichandler()->set_icattr(attr);
	    if (get_lang_selection()) {
		set_lang_selection(false);
		result = hics->get_ichandler()->toggle_conversion(hics->get_imlexec(), true);
		hics->pshared->conversion_mode = true;
		if ((hics->start_request()->dealing()) && result) {
		    stop_lang_selection_window(hics);
		    p = iiimp_hotkey_state_notify_new(hics->get_iiimptrans()->get_data_s(), hics->get_im_id(), hics->get_ic_id(), hotkey_id, EVENT_FORWARDING_ON);
		    if (!hics->send(p, true)) return false;
		    return true;
		}
	    } 
	}
    }
    return false;
}

bool
IIIMP_hotkey::draw_lang_selection_window(
    IIIMP_ICState *hics,
    int kc
)
{
    IIIMP_message *p;
    IIIMP_text *pchoiceh, *pchoice = NULL;
    IIIMP_text *plabelh, *plabel = NULL;
    IIIMP_text *ptitle;
    IIIMP_text *pcur;
    IMText *title;
    IMText **vt;             /* for value */
    IMText **lt;             /* for label */
    LangIMList::iterator it;
    LangIMList *plangimlist;
    UTFCHAR title_string[] = {'S','W','I','T','C','H',' ','L','A','N','G',' ',' ',0};
    UTFCHAR *tmp_str;
    char label[32];
    char pos[32];
    int idx_first, idx_last, size;
    size_t i;
    int j, k, l;
    bool flag = false;

    idx_first = idx_last = size = 0;
    pchoiceh = plabelh = ptitle = NULL;

    plangimlist = hics->get_langimlist();
    size = plangimlist->size();

    if (kc == 0) {
        first_candidate = 0;
        last_candidate = 8;
        if (size < last_candidate)
          last_candidate = size -1;
        current = 0;
        k = current;
    } 

    switch(kc) {
      case IM_VK_UP:
       if (last_candidate > size)
	   last_candidate = size;
       if (current == first_candidate) {
	   current = last_candidate;
	   flag = true;
       } else
	   current -= 1;
       break;
      case IM_VK_DOWN:
      case IM_VK_SPACE:
       if (current == last_candidate)
	   current = first_candidate;
       else
	   current += 1;
       break;
      case IM_VK_PAGE_UP:
      case IM_VK_LEFT:
       idx_first = first_candidate - 9;
       if (idx_first >= 0) {
	   last_candidate = first_candidate -1;
	   first_candidate = idx_first;
	   current = first_candidate;
       }
       break;
      case IM_VK_PAGE_DOWN:
      case IM_VK_RIGHT:
       idx_first = first_candidate + 9;
       idx_last = last_candidate + 9;
       if (idx_first < size) {
	   first_candidate = idx_first;
	   current = first_candidate;
       }
       if (idx_last <= size)
	   last_candidate = idx_last;
       else if (idx_last > size) 
	   last_candidate = size;
       break;
    }

    title = (IMText *)calloc(1, sizeof(IMText));
    (void)memset(pos, 0, 32);

    if (current < size) 
	k = current + 1;
    else if (current >= size) {
	if (!flag)
	    current = first_candidate;
	else 
	    current -= 1;
	k = current + 1;
    }
    l = size;
    snprintf((char *)pos,sizeof(pos),"%c%d%c%d%c",'[', k, '/', l, ']');
    title->char_length = UTFCHARLen(title_string) + strlen(pos);
    title->text.utf_chars = (UTFCHAR *) calloc((title->char_length + 1), sizeof(UTFCHAR));
    tmp_str = (UTFCHAR *) calloc ((strlen(pos) + 1), sizeof(UTFCHAR));
    UTFCHARCpy(tmp_str, pos);
    UTFCHARCat(title->text.utf_chars, title_string, tmp_str);
    title->feedback = create_feedback(title->char_length);
    ptitle = convert_IMText_to_iiimp_text(hics->get_iiimptrans()->get_data_s(), title);
  
    if (title) free(title);
    if (tmp_str) free(tmp_str);

    vt = (IMText **)calloc(size, sizeof(IMText *));
    lt = (IMText **)calloc(size, sizeof(IMText *));

    for (i = 0, it = plangimlist->begin(); i < plangimlist->size(); it++,i++) {
        u16string imname  = it->get_hrn();

        if (imname.get_charstr() == NULL) {
	    imname  = it->get_imname();
        }

        vt[i] = (IMText *)calloc(1, sizeof(IMText));
        vt[i]->char_length = UTFCHARLen(imname.get_charstr()) + 3 + UTFCHARLen(it->get_id());
        vt[i]->text.utf_chars = (UTFCHAR *) calloc((vt[i]->char_length + 1), sizeof(UTFCHAR));

        UTFCHARCat(vt[i]->text.utf_chars, it->get_id(), " (", imname.get_charstr(), ")");
        vt[i]->feedback = create_feedback(vt[i]->char_length);

        // Value
        pcur = convert_IMText_to_iiimp_text(hics->get_iiimptrans()->get_data_s(), vt[i]);
        if (!pchoiceh) {
	    pchoiceh = pcur;
        }else{
	    pchoice->next = pcur;
        }
        pchoice = pcur;

        lt[i] = (IMText *)calloc(1, sizeof(IMText));
        (void)memset(label, 0, 32);
        lt[i]->char_length = 3;
        lt[i]->text.utf_chars = (UTFCHAR *) calloc((lt[i]->char_length + 1), sizeof(UTFCHAR));
        if (i > 8) {
	    j = i % 9;
	    snprintf((char *)label,sizeof(label),"%d%c%c",j+1,'.',' ');
        } else {
	    snprintf((char *)label,sizeof(label),"%d%c%c",i+1,'.',' ');
        }
        UTFCHARCpy(lt[i]->text.utf_chars, label);
        lt[i]->feedback = create_feedback(lt[i]->char_length);

        // Label
        pcur = convert_IMText_to_iiimp_text(hics->get_iiimptrans()->get_data_s(), lt[i]);
        if (!plabelh) {
            plabelh = pcur;
        } else {
            plabel->next = pcur;
        }
        plabel = pcur;
    }
 
    p = iiimp_lookup_choice_draw_new(hics->get_iiimptrans()->get_data_s(),
				     hics->get_im_id(),
				     hics->get_ic_id(),
				     first_candidate,
				     last_candidate,
				     current,
				     pchoiceh,
				     plabelh,
				     ptitle);
    if (!hics->send(p, true)) return false;
    return true;
}

bool
IIIMP_hotkey::do_cycle_lang_selection(
    IIIMP_ICState *hics,
    int orientation
)
{
    LangIMList *plangimlist = hics->get_langimlist();

    if (plangimlist->size() == 0)
	hics->create_langimlist();

    print_LangIMList(hics);
    curr_langid += orientation;
    if (orientation > 0 && curr_langid > plangimlist->size())
	curr_langid = 1;
    if (orientation < 0 && curr_langid <= 0)
	curr_langid = plangimlist->size();

    return switch_language(hics, curr_langid, curr_hotkeyid);
}

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