
typedef struct {
  u32 x,y,w,h;
} TOSK_Rect;

enum EOrgType {EOT_Single,EOT_Shift,EOT_Multi5};

typedef struct {
  bool Enabled;
  TOSK_Rect Rect;
  UnicodeChar *pShow;
  EOrgType OrgType;
  UnicodeChar *pOrgChars;
} TOSK;

static const u32 OSKMaxCount=128;
static TOSK OSK[OSKMaxCount];
static u32 OSKCount;

static char section[128];
static u32 readline;

static void readsection(const char *str)
{
  str++;
  
  u32 ofs;
  
  ofs=0;
  while(*str!=']'){
    if((128<=ofs)||(*str==0)) StopFatalError(0,"line%d error.\nThe section name doesn't end correctly.\n",readline);
    section[ofs]=*str;
    str++;
    ofs++;
  }
  section[ofs]=0;
  
  if(strcmp(section,"NewKey")==0){
    if(OSKCount==OSKMaxCount) StopFatalError(0,"KeyMap buffer overflow.");
    OSKCount++;
    TOSK *ps=&OSK[OSKCount-1];
    ps->Enabled=false;
    ps->Rect.x=0;
    ps->Rect.y=0;
    ps->Rect.w=0;
    ps->Rect.h=0;
    ps->pShow=NULL;
    ps->OrgType=EOT_Single;
    ps->pOrgChars=NULL;
  }
}

static TOSK_Rect GetRectFromStr(const char *pstr)
{
  TOSK_Rect res={0,0,0,0};
  
  {
    u32 value=0;
    while(1){
      char c=*pstr++;
      if((c==0)||(c==',')) break;
      value=(value*10)+((u32)(c-'0'));
    }
    res.x=value;
  }
  
  {
    u32 value=0;
    while(1){
      char c=*pstr++;
      if((c==0)||(c==',')) break;
      value=(value*10)+((u32)(c-'0'));
    }
    res.y=value;
  }
  
  {
    u32 value=0;
    while(1){
      char c=*pstr++;
      if((c==0)||(c==',')) break;
      value=(value*10)+((u32)(c-'0'));
    }
    res.w=value;
  }
  
  {
    u32 value=0;
    while(1){
      char c=*pstr++;
      if((c==0)||(c==',')) break;
      value=(value*10)+((u32)(c-'0'));
    }
    res.h=value;
  }
  
  return(res);
}

static UnicodeChar* GetSpetialString(TMM *pMM,const char *pstr)
{
  UnicodeChar wc=0;
  if(strcmp(pstr,"[BackSpace]")==0) wc=WC_BackSpace;
  if(strcmp(pstr,"[Enter]")==0) wc=WC_Enter;
  if(strcmp(pstr,"[Space]")==0) wc=WC_Space;
  if(strcmp(pstr,"[Change]")==0) wc=WC_Change;
  if(strcmp(pstr,"[Shift]")==0) wc=WC_Shift;
  if(strcmp(pstr,"[CapsLock]")==0) wc=WC_CapsLock;
  if(strcmp(pstr,"[HW_Symbol]")==0) wc=WC_HW_Symbol;
  if(strcmp(pstr,"[HW_Number]")==0) wc=WC_HW_Number;
  if(strcmp(pstr,"[HW_ENG_Large]")==0) wc=WC_HW_ENG_Large;
  if(strcmp(pstr,"[HW_ENG_Small]")==0) wc=WC_HW_ENG_Small;
  if(strcmp(pstr,"[HW_JPN_Hiragana]")==0) wc=WC_HW_JPN_Hiragana;
  if(strcmp(pstr,"[HW_JPN_Katakana]")==0) wc=WC_HW_JPN_Katakana;
  if(wc!=0){
    const UnicodeChar tmp[2]={wc,0};
    return(Unicode_AllocateCopy(pMM,tmp));
  }
  
  return(Unicode_AllocateCopyFromUTF8(pMM,pstr));
}

static void readkey(const char *str)
{
  if(section[0]==0){
    _consolePrintf("line%d error.\nThere is a key ahead of the section name.\n",readline);
//    ShowLogHalt();
    return;
  }
  
  char key[128],value[256];
  
  u32 ofs;
  
  ofs=0;
  while(*str!='='){
    if((128<=ofs)||(*str==0)) StopFatalError(0,"line%d error.\nThe key name doesn't end correctly.\n",readline);
    key[ofs]=*str;
    str++;
    ofs++;
  }
  key[ofs]=0;
  
  str++;
  
  ofs=0;
  while(*str!=0){
    if(128<=ofs) StopFatalError(0,"line%d error.\nThe value doesn't end correctly.\n",readline);
    value[ofs]=*str;
    str++;
    ofs++;
  }
  value[ofs]=0;
  
  s32 ivalue=atoi(value);
  bool bvalue;
  
  if(ivalue==0){
    bvalue=false;
    }else{
    bvalue=true;
  }
  
  if(strcmp(section,"Global")==0){
    TOSK_GlobalInfo *ps=&OSK_GlobalInfo;
    
    if(strcmp(key,"Title")==0){
      if(ps->pTitle!=NULL){
        safefree(&MM_SKKOSK,ps->pTitle); ps->pTitle=NULL;
        _consolePrintf("Warrning: line%d duplicated item. [%s] key=%s\n",readline,section,key);
      }
      ps->pTitle=Unicode_AllocateCopyFromUTF8(&MM_SKKOSK,value);
      return;
    }
    if(strcmp(key,"Maker")==0){
      if(ps->pMaker!=NULL){
        safefree(&MM_SKKOSK,ps->pMaker); ps->pMaker=NULL;
        _consolePrintf("Warrning: line%d duplicated item. [%s] key=%s\n",readline,section,key);
      }
      ps->pMaker=Unicode_AllocateCopyFromUTF8(&MM_SKKOSK,value);
      return;
    }
    if(strcmp(key,"TargetCodePage")==0){
      ps->TargetCodePage=ivalue;
      return;
    }
    if(strcmp(key,"IME")==0){
      if(ps->pIME!=NULL){
        safefree(&MM_SKKOSK,ps->pIME); ps->pIME=NULL;
        _consolePrintf("Warrning: line%d duplicated item. [%s] key=%s\n",readline,section,key);
      }
      ps->pIME=str_AllocateCopy(&MM_SKKOSK,value);
      return;
    }
    if(strcmp(key,"OSK")==0){
      if(ps->pOSK!=NULL){
        safefree(&MM_SKKOSK,ps->pOSK); ps->pOSK=NULL;
        _consolePrintf("Warrning: line%d duplicated item. [%s] key=%s\n",readline,section,key);
      }
      ps->pOSK=str_AllocateCopy(&MM_SKKOSK,value);
      return;
    }
    
  }
  
  if(strcmp(section,"NewKey")==0){
    if(OSKCount==0) StopFatalError(0,"Internal error.");
    TOSK *ps=&OSK[OSKCount-1];
    
    if(strcmp(key,"Rect")==0){
      ps->Rect=GetRectFromStr(value);
      return;
    }
    if(strcmp(key,"Show")==0){
      if(ps->pShow!=NULL){
        safefree(&MM_SKKOSK,ps->pShow); ps->pShow=NULL;
        _consolePrintf("Warrning: line%d duplicated item. [%s] key=%s\n",readline,section,key);
      }
      if(value[0]!=0) ps->pShow=GetSpetialString(&MM_SKKOSK,value);
      return;
    }
    if(strcmp(key,"OrgType")==0){
      if(strcmp(value,"Single")==0){
        ps->OrgType=EOT_Single;
        value[0]=0;
      }
      if(strcmp(value,"Shift")==0){
        ps->OrgType=EOT_Shift;
        value[0]=0;
      }
      if(strcmp(value,"Multi5")==0){
        ps->OrgType=EOT_Multi5;
        value[0]=0;
      }
      if(value[0]!=0) StopFatalError(0,"Unknown OrgType. [%s]",value);
      return;
    }
    if(strcmp(key,"OrgChars")==0){
      if(ps->pOrgChars!=NULL){
        safefree(&MM_SKKOSK,ps->pOrgChars); ps->pOrgChars=NULL;
        _consolePrintf("Warrning: line%d duplicated item. [%s] key=%s\n",readline,section,key);
      }
      if(value[0]!=0) ps->pOrgChars=GetSpetialString(&MM_SKKOSK,value);
      return;
    }
    
  }
  
  StopFatalError(0,"line%d error.\ncurrent section [%s] unknown key=%s value=%s\n",readline,section,key,value);
}

static void LoadINI_ins_loadbody(char *pini,u32 inisize,bool LoadHeaderOnly)
{
  section[0]=0;
  readline=0;
  
  u32 iniofs=0;
  
  while(iniofs<inisize){
    
    readline++;
    
    u32 linelen=0;
    
    // Calc Line Length
    {
      char *s=&pini[iniofs];
      
      while(0x20<=*s){
        linelen++;
        s++;
        if(inisize<=(iniofs+linelen)) break;
      }
      *s=0;
    }
    
    if(linelen!=0){
      char c=pini[iniofs];
      if((c==';')||(c=='/')||(c=='!')){
        // comment line
        }else{
        if(c=='['){
          readsection(&pini[iniofs]);
          }else{
          readkey(&pini[iniofs]);
        }
      }
    }
    
    iniofs+=linelen;
    
    // skip NULL,CR,LF
    {
      char *s=&pini[iniofs];
      
      while(*s<0x20){
        iniofs++;
        s++;
        if(inisize<=iniofs) break;
      }
    }
    
    if(LoadHeaderOnly==true){
      if(OSKCount!=0) break;
    }
  }
}

static void OSK_KeyMap_Load(const char *pafn,bool LoadHeaderOnly)
{
  if(OSKCount!=0) StopFatalError(0,"Already loaded keymap.");
  
  {
    TOSK_GlobalInfo *ps=&OSK_GlobalInfo;
    ps->pTitle=NULL;
    ps->pMaker=NULL;
    ps->TargetCodePage=0;
    ps->pIME=NULL;
    ps->pOSK=NULL;
  }
  
  FAT_FILE *pf=Shell_FAT_fopen_KeyMaps(pafn);
  u32 inisize=FAT2_GetFileSize(pf);
  char *pini=(char*)safemalloc(&MM_Temp,inisize);
  FAT2_fread(pini,1,inisize,pf);
  FAT2_fclose(pf);
  
  LoadINI_ins_loadbody(pini,inisize,LoadHeaderOnly);
  
  if(pini!=NULL){
    safefree(&MM_Temp,pini); pini=NULL;
  }
  
  if(LoadHeaderOnly==false){
    for(u32 idx=0;idx<OSKCount;idx++){
      TOSK *ps=&OSK[idx];
      if((ps->pShow!=NULL)&&(ps->pOrgChars!=NULL)){
        if(Unicode_isEqual(ps->pShow,ps->pOrgChars)==true){
          _consolePrintf("Warrning: KeyID:%d Found equal string. '%s'.",idx,StrConvert_Unicode2Ank_Test(ps->pShow));
        }
      }
      if((ps->pShow==NULL)&&(ps->pOrgChars!=NULL)) ps->pShow=Unicode_AllocateCopy(&MM_SKKOSK,ps->pOrgChars);
      if(ps->pShow==NULL) StopFatalError(0,"KeyID:%d No set Show string.",idx);
      if(ps->pOrgChars==NULL) StopFatalError(0,"KeyID:%d No set OrgChars string.",idx);
    }
  }
}

static void OSK_KeyMap_Free(void)
{
  {
    TOSK_GlobalInfo *pgi=&OSK_GlobalInfo;
    if(pgi->pTitle!=NULL){
      safefree(&MM_SKKOSK,pgi->pTitle); pgi->pTitle=NULL;
    }
    if(pgi->pMaker!=NULL){
      safefree(&MM_SKKOSK,pgi->pMaker); pgi->pMaker=NULL;
    }
    pgi->TargetCodePage=0;
    if(pgi->pIME!=NULL){
      safefree(&MM_SKKOSK,pgi->pIME); pgi->pIME=NULL;
    }
    if(pgi->pOSK!=NULL){
      safefree(&MM_SKKOSK,pgi->pOSK); pgi->pOSK=NULL;
    }
  }
  
  for(u32 idx=0;idx<OSKCount;idx++){
    TOSK *pkm=&OSK[idx];
    
    pkm->Enabled=false;
    if(pkm->pShow!=NULL){
      safefree(&MM_SKKOSK,pkm->pShow); pkm->pShow=NULL;
    }
    if(pkm->pOrgChars!=NULL){
      safefree(&MM_SKKOSK,pkm->pOrgChars); pkm->pOrgChars=NULL;
    }
  }
  
  OSKCount=0;
}

