/// Key.cpp
///
/// (c) 2001-2017 by Bastian Spiegel <bs@tkscript.de>
///     - Distributed under terms of the Lesser GNU General Public License (LGPL).
///       See COPYING and <http://www.gnu.org/licenses/licenses.html#LGPL> for further information.
///

#include <stdlib.h>

#define YAC_BIGSTRING
#include <yac.h>

#include "sdl_includes.h"

#include "Key.h"

extern YAC_Host *yac_host;


_Key::_Key(void) {
   vkey = vmod = unicode = 0;
}

_Key::_Key(sUI _vkey, sUI _vmod, sUI _unicode) {
   vkey = _vkey;
   vmod = _vmod;
}

void YAC_VCALL _Key::yacSerialize(YAC_Object *_ofs, sUI _rtti) {
   YAC_BEG_SERIALIZE();
   
   YAC_SERIALIZE_I32(vkey);
   YAC_SERIALIZE_I32(vmod);
}

sUI YAC_VCALL _Key::yacDeserialize(YAC_Object *_ifs, sUI _rtti) {
   YAC_BEG_DESERIALIZE();
   
   vkey=YAC_DESERIALIZE_I32();
   vmod=YAC_DESERIALIZE_I32();
   
   return 1;
}

void YAC_VCALL _Key::yacOperator(sSI _cmd, YAC_Object *_o, YAC_Value *_r) {
   switch(_cmd)
   {
   default:
      YAC_Object::yacOperator(_cmd, _o, _r);
      break;

   case YAC_OP_ASSIGN:
      if(YAC_CHK(_o, class_ID))
      {
         _Key *o = (_Key*)_o;
         vkey    = o->vkey;
         vmod    = o->vmod;
         unicode = o->unicode;
      }
      break;

   case YAC_OP_CEQ:
      if(YAC_CHK(_o, class_ID))
      {
         _Key *o = (_Key*)_o;
         YAC_RETI( (o->vkey==vkey) && (o->vmod==vmod) );
      }
      break;

   case YAC_OP_CNE:
      if(YAC_CHK(_o, class_ID))
      {
         _Key *o = (_Key*)_o;
         YAC_RETI( (o->vkey!=vkey) || (o->vmod!=vmod) );
      }
      break;
   }
}

sUI _Key::_getCode(void) {
   return (vkey & 0xFFFF);
}

void _Key::_setCode(sUI _c) {
   vkey = (vkey & 0xFFFF0000) | ((sUI)_c);
}

sUI _Key::_getMod(void) {
   return vmod;
}

void _Key::_setMod(sUI _c) {
   vmod = (sUI)_c;
}

sUI _Key::_getPressed(void) {
   return ((vkey & VKEY_PRESSED) ? (vkey & 0xFFFF) : 0);
}

void _Key::_setPressed(sUI _c) {
   vkey = VKEY_PRESSED | (((sUI)_c) & 0xFFFF);
}

sUI _Key::_getReleased(void) {
   return ( (!(vkey & VKEY_PRESSED)) ? (vkey & 0xFFFF) : 0);
}

void _Key::_setReleased(sUI _c) {
   vkey = (sUI) (_c & 0xFFFF);
}

sUI _Key::_getAlpha(void) {
   sUI t = vkey&0xFFFF;
   if(t >= ' ' && t <= 'z')
   {
      return t;
   }
   else
   {
      return 0;
   }
}

void _Key::_setAlpha(sUI _c) {
   if(_c >= ' ' && _c <= 'z')
   {
      vkey = _c;
   }
   else
   {
      vkey = 0;
   }
}

sUI _Key::_getUnicode(void) {
   return unicode;
}

void _Key::_setUnicode(sSI _c) {
   unicode = (sUI)_c;
}


typedef struct _tks_keyname_entry {
   sU16 code;
   const char *name;
} tks_keyname_entry ;

static tks_keyname_entry tks_keynametable[] = {
   { SDLK_BACKSPACE,   "backspace" },
   { SDLK_TAB,         "tab" },
   { SDLK_CLEAR,       "clear" },
   { SDLK_RETURN,      "return" },
   { SDLK_PAUSE,       "pause" },
   { SDLK_ESCAPE,      "escape" },
   { SDLK_SPACE,       "space" },
   { SDLK_DELETE,      "delete" },
   { SDLK_KP0,         "kp_0" },
   { SDLK_KP1,         "kp_1" },
   { SDLK_KP2,         "kp_2" },
   { SDLK_KP3,         "kp_3" },
   { SDLK_KP4,         "kp_4" },
   { SDLK_KP5,         "kp_5" },
   { SDLK_KP6,         "kp_6" },
   { SDLK_KP7,         "kp_7" },
   { SDLK_KP8,         "kp_8" },
   { SDLK_KP9,         "kp_9" },
   { SDLK_KP_PERIOD,   "kp_period" },
   { SDLK_KP_DIVIDE,   "kp_divide" },
   { SDLK_KP_MULTIPLY, "kp_multiply" },
   { SDLK_KP_MINUS,    "kp_minus" },
   { SDLK_KP_PLUS,     "kp_plus" },
   { SDLK_KP_ENTER,    "kp_enter" },
   { SDLK_KP_EQUALS,   "kp_equals" },
   { SDLK_UP,          "up" },
   { SDLK_DOWN,        "down" },
   { SDLK_RIGHT,       "right" },
   { SDLK_LEFT,        "left" },
   { SDLK_INSERT,      "insert" },
   { SDLK_HOME,        "home" },
   { SDLK_END,         "end" },
   { SDLK_PAGEUP,      "pageup" },
   { SDLK_PAGEDOWN,    "pagedown" },
   { SDLK_F1,          "f1" },
   { SDLK_F2,          "f2" },
   { SDLK_F3,          "f3" },
   { SDLK_F4,          "f4" },
   { SDLK_F5,          "f5" },
   { SDLK_F6,          "f6" },
   { SDLK_F7,          "f7" },
   { SDLK_F8,          "f8" },
   { SDLK_F9,          "f9" },
   { SDLK_F10,         "f10" },
   { SDLK_F11,         "f11" },
   { SDLK_F12,         "f12" },
   { SDLK_F13,         "f13" },
   { SDLK_F14,         "f14" },
   { SDLK_F15,         "f15" },
   { SDLK_NUMLOCK,     "numlock" },
   { SDLK_CAPSLOCK,    "capslock" },
   { SDLK_SCROLLOCK,   "scrollock" },
   { SDLK_RSHIFT,      "rshift" },
   { SDLK_LSHIFT,      "lshift" },
   { SDLK_RCTRL,       "rctrl" },
   { SDLK_LCTRL,       "lctrl" },
   { SDLK_RALT,        "ralt" },
   { SDLK_LALT,        "lalt" },
   { SDLK_RMETA,       "rmeta" },
   { SDLK_LMETA,       "lmeta" },
   { SDLK_LSUPER,      "lsuper" },
   { SDLK_RSUPER,      "rsuper" },
   { SDLK_MODE,        "mode" },
   { SDLK_COMPOSE,     "compose" },
   { SDLK_HELP,        "help" },
   { SDLK_PRINT,       "print" },
   { SDLK_SYSREQ,      "sysreq" },
   { SDLK_BREAK,       "break" },
   { SDLK_MENU,        "menu" },
   { SDLK_POWER,       "power" },
   { SDLK_EURO,        "euro" },
   { 0, 0 }
};

static tks_keyname_entry tks_keymodnametable[] = {
   { KMOD_LCTRL,  "lctrl-" },
   { KMOD_LSHIFT, "lshift-" },
   { KMOD_RSHIFT, "rshift-" },
   { KMOD_RCTRL,  "rctrl-" },
   { KMOD_LALT,   "lalt-" },
   { KMOD_RALT,   "ralt-" },
   { KMOD_LMETA,  "lmeta-" },
   { KMOD_RMETA,  "rmeta-" },
   { KMOD_NUM,    "num-" },
   { KMOD_CAPS,   "caps-" },
   { KMOD_MODE,   "mode-" },
   { 0, 0 }
};



const char *_Key::getCodeName(sUI _key) {
   tks_keyname_entry *e = tks_keynametable;
   _key = _key & 0xFFFF;

   if(_key >= ' ' && _key < 127)
   {
      static char cs[2];
      cs[0] = (char)_key;
      cs[1] = 0;
      return cs;
   }
   else
   {
      while(e->code)
      {
         if(e->code == _key)
         {
            return e->name;
         }
         e++;
      }
   }
   return 0;
}

sUI _Key::getCodeByName(YAC_String *_s) {
   tks_keyname_entry *e = tks_keynametable;
   if(2 == _s->length)
   {
      return (sUI)_s->chars[0];
   }
   else
   {
      while(e->code)
      {
         if(_s->compare((char*)e->name))
         {
            return e->code;
         }
         e++;
      }
   }
   return 0;
}

sUI _Key::getModByName(YAC_String *_s) {
   tks_keyname_entry *e=tks_keymodnametable;
   while(e->code)
   {
      if(_s->compare((char*)e->name))
      {
         return e->code;
      }
      e++;
   }
   return 0;
}

void _Key::appendModName(YAC_String *_s, sUI _mod) {
   tks_keyname_entry *e = tks_keymodnametable;
   while(e->code)
   {
      if((e->code & _mod))
      {
         YAC_String s; 
         s.visit((char*)e->name);
         _s->yacOperatorAdd(&s);
      }
      e++;
   }
}

void _Key::setModifiersByString(YAC_String *_t) {
   vmod = 0;
   if( (_t->length>1) && (_t->chars!=NULL) )
   {
      sUI i;
      sUI j = 0;
      sChar *s = (sChar*)_t->chars;

      YAC_String s_minus; 
      s_minus.visit("-");

      for(i=0; i<_t->length; i++)
      {
         if(*s == '-')
         {
            *s = 0;
            YAC_String kt;
            kt.copy(s+j);
            kt.append(&s_minus);
            vmod |= getModByName(&kt);
            *s = '-';
            j = i + 1;
         }
      }
   }
}

void _Key::_getCodeName(YAC_Value *_r) {
   YAC_String *s = YAC_New_String();
   YAC_String t; 
   t.visit((char*)getCodeName(vkey));
   s->yacCopy(&t);
   YAC_RETS(s, 1);
}

void _Key::_setCodeName(YAC_String *_s) {
   vkey = getCodeByName(_s);
}


void _Key::_getModName(YAC_Value *_r) {
   YAC_String *s = YAC_New_String();
   appendModName(s, vmod);

   YAC_RETS(s, 1);
}

void _Key::_setModName(YAC_String *_s) {
   setModifiersByString(_s);
}

void _Key::_getName(YAC_Value *_r) {
   YAC_String *s = YAC_New_String();
   appendModName(s, vmod);

   YAC_String t; 
   t.visit((char*)getCodeName(vkey));

   s->yacOperatorAdd(&t);

   YAC_RETS(s, 1);
}

void _Key::_setName(YAC_Object *_s) {
   if(YAC_BCHK(_s, YAC_CLID_STRING))
   {
      YAC_String *s = (YAC_String*)_s;
      sSI li = s->lastIndexOf('-', 0);
      if(li != -1)
      {
         // ---- get keycode ----
         YAC_String kt;
         s->substring(&kt, li+1, s->length-li-1);
         vkey = getCodeByName(&kt);

         // ---- get modifiers ----
         YAC_String t;
         s->substring(&t, 0, li);
         setModifiersByString(&t);
      }
      else
      {
         /// ---- only set codename ----
         vmod = 0;
         vkey = getCodeByName(s);
      }
   }
}


void _Key::_getNameFor(sUI _vkey, sUI _vmod, YAC_Value *_r) {
   YAC_String *s = YAC_New_String();
   appendModName(s, (sUI)_vmod);
   YAC_String t;
   t.visit((char*)getCodeName((sUI)_vkey));
   s->yacOperatorAdd(&t);
   YAC_RETS(s, 1);
}

sSI _Key::_modAlt(void) {
   return (vmod&KMOD_LALT) || (vmod&KMOD_RALT);
}

sSI _Key::_modShift(void) {
   return (vmod&KMOD_LSHIFT) || (vmod&KMOD_RSHIFT);
}

sSI _Key::_modCtrl(void) {
   return (vmod&KMOD_LCTRL) || (vmod&KMOD_RCTRL);
}

sSI _Key::_modCaps(void) {
   return (vmod&KMOD_CAPS);
}

sSI _Key::_modShiftOrCaps(void) {
   return (vmod&KMOD_LSHIFT) || (vmod&KMOD_RSHIFT) || (vmod&KMOD_CAPS);
}
