
typedef struct {
  u8 *pbuf;
  u32 bufsize;
  u32 pos;
} TReadBuf;

static TReadBuf ReadBuf;

static void ReadBuf_Start(u32 SizeLimit)
{
  const TExtLinkBody *pelb=GetFileBody_From_MoonShell2_ExtLink();
  
  const char *pafn=ConvertFull_Unicode2Alias(pelb->DataPathUnicode,pelb->DataFilenameUnicode);
  FAT_FILE *pf=FAT2_fopen_AliasForRead(pafn);
  
  TReadBuf *prb=&ReadBuf;
  prb->bufsize=FAT2_GetFileSize(pf);
  if(prb->bufsize==0) StopFatalError(0,"This file is empty.");
  
  if(SizeLimit!=(u32)-1){
    if(SizeLimit<prb->bufsize) prb->bufsize=SizeLimit;
  }
  
  prb->pbuf=(u8*)safemalloc(&MM_Temp,prb->bufsize);
  prb->pos=0;
  
  FAT2_fread(prb->pbuf,1,prb->bufsize,pf);
  
  FAT2_fclose(pf); pf=NULL;
}

static inline void ReadBuf_SetToTop(void)
{
  TReadBuf *prb=&ReadBuf;
  prb->pos=0;
}

static inline bool ReadBuf_EOF(void)
{
  TReadBuf *prb=&ReadBuf;
  if(prb->bufsize<=prb->pos) return(true);
  return(false);
}

static inline void ReadBuf_Back8(void)
{
  TReadBuf *prb=&ReadBuf;
  prb->pos-=1;
}

static inline void ReadBuf_Back16(void)
{
  TReadBuf *prb=&ReadBuf;
  prb->pos-=2;
}

static inline u8 ReadBuf_u8(void)
{
  TReadBuf *prb=&ReadBuf;
  return(prb->pbuf[prb->pos++]);
}

static inline u16 ReadBuf_u16(void)
{
  TReadBuf *prb=&ReadBuf;
  u16 *p=(u16*)&prb->pbuf[prb->pos];
  u16 res=*p;
  prb->pos+=2;
  return(res);
}

static void ReadBuf_End(void)
{
  TReadBuf *prb=&ReadBuf;
  prb->bufsize=0;
  if(prb->pbuf!=NULL){
    safefree(&MM_Temp,prb->pbuf); prb->pbuf=NULL;
  }
}

static u32 TextEdit_LoadFromFile_UTF8(CTextEditLine *pCurLine)
{
  u32 ErrorCount=0;
  
  UTF8Header[0]=ReadBuf_u8();
  UTF8Header[1]=ReadBuf_u8();
  UTF8Header[2]=ReadBuf_u8();
  if((UTF8Header[0]!=0xEF)||(UTF8Header[1]!=0xBB)||(UTF8Header[2]!=0xBF)){
    for(u32 idx=0;idx<3;idx++){
      UTF8Header[idx]=0;
      ReadBuf_Back8();
    }
  }
  
  u32 wbuflen=0;
  UnicodeChar *pwbuf=NULL;
  u32 wbufpos=0;
  bool firstwrite=true;
  
  while(1){
    if(ReadBuf_EOF()==true) break;
    UnicodeChar wc;
    u32 b0=ReadBuf_u8();
    if(b0<0x80){
      wc=(UnicodeChar)b0;
      }else{
      if((b0&0xe0)==0xc0){ // 0b 110. ....
        u32 b1=ReadBuf_u8();
        wc=(UnicodeChar)(((b0&~0xe0)<<6)+((b1&~0xc0)<<0));
        }else{
        if((b0&0xf0)==0xe0){ // 0b 1110 ....
          u32 b1=ReadBuf_u8();
          u32 b2=ReadBuf_u8();
          wc=(UnicodeChar)(((b0&~0xf0)<<12)+((b1&~0xc0)<<6)+((b2&~0xc0)<<0));
          }else{
          u32 b1=ReadBuf_u8();
          u32 b2=ReadBuf_u8();
          u32 b3=ReadBuf_u8();
          wc=(UnicodeChar)'?';
        }
      }
    }
    if((wc==WC_CR)||(wc==WC_LF)){
      if(pCurLine!=NULL){
        if(wbufpos==0){
          pCurLine->AddNewLine();
          pCurLine=pCurLine->pNext;
          }else{
          if(firstwrite==true){
            firstwrite=false;
            pwbuf[wbufpos]=0;
            pCurLine->AddStr((u32)-1,pwbuf);
            }else{
            pCurLine->AddNewLineWithSet(pwbuf,wbufpos);
            pCurLine=pCurLine->pNext;
          }
          wbufpos=0;
        }
      }
      if(wc==WC_CR){
        ReturnCode=ERC_CRLF;
        if(ReadBuf_u8()!=WC_LF){
          ReadBuf_Back8();
          ReturnCode=ERC_CR;
        }
        }else{
        ReturnCode=ERC_LFCR;
        if(ReadBuf_u8()!=WC_CR){
          ReadBuf_Back8();
          ReturnCode=ERC_LF;
        }
      }
      }else{
      if(pCurLine==NULL){
        if(pTestFont->isExists(wc)==false) ErrorCount++;
        }else{
        if(wbuflen<=(wbufpos+2)){
          wbuflen+=128;
          pwbuf=(UnicodeChar*)saferealloc(&MM_Temp,pwbuf,(wbuflen+2)*sizeof(UnicodeChar));
        }
        pwbuf[wbufpos++]=wc;
      }
    }
  }
  
  if(wbufpos!=0){
    if(pCurLine!=NULL) pCurLine->AddNewLineWithSet(pwbuf,wbufpos);
  }
  
  if(pwbuf!=NULL){
    safefree(&MM_Temp,pwbuf); pwbuf=NULL;
  }
  
  if(UTF8Header[0]!=0) ErrorCount=0; // UTF-8ヘッダがあるときは、無条件でUTF-8を選ぶ。
  
  return(ErrorCount);
}

static u32 TextEdit_LoadFromFile_UTF16(CTextEditLine *pCurLine)
{
  u32 ErrorCount=0;
  
  UTF16Header=ReadBuf_u16();
  if(UTF16Header!=0xfeff){
    UTF16Header=0;
    ReadBuf_Back16();
  }
  
  u32 wbuflen=0;
  UnicodeChar *pwbuf=NULL;
  u32 wbufpos=0;
  bool firstwrite=true;
  
  while(1){
    if(ReadBuf_EOF()==true) break;
    UnicodeChar wc=ReadBuf_u16();
    if((wc==WC_CR)||(wc==WC_LF)){
      if(pCurLine!=NULL){
        if(wbufpos==0){
          pCurLine->AddNewLine();
          pCurLine=pCurLine->pNext;
          }else{
          if(firstwrite==true){
            firstwrite=false;
            pwbuf[wbufpos]=0;
            pCurLine->AddStr((u32)-1,pwbuf);
            }else{
            pCurLine->AddNewLineWithSet(pwbuf,wbufpos);
            pCurLine=pCurLine->pNext;
          }
          wbufpos=0;
        }
      }
      if(wc==WC_CR){
        ReturnCode=ERC_CRLF;
        if(ReadBuf_u16()!=WC_LF){
          ReadBuf_Back16();
          ReturnCode=ERC_CR;
        }
        }else{
        ReturnCode=ERC_LFCR;
        if(ReadBuf_u16()!=WC_CR){
          ReadBuf_Back16();
          ReturnCode=ERC_LF;
        }
      }
      }else{
      if(pCurLine==NULL){
        if(pTestFont->isExists(wc)==false) ErrorCount++;
        }else{
        if(wbuflen<=(wbufpos+2)){
          wbuflen+=128;
          pwbuf=(UnicodeChar*)saferealloc(&MM_Temp,pwbuf,(wbuflen+2)*sizeof(UnicodeChar));
        }
        pwbuf[wbufpos++]=wc;
      }
    }
  }
  
  if(wbufpos!=0){
    if(pCurLine!=NULL) pCurLine->AddNewLineWithSet(pwbuf,wbufpos);
  }
  
  if(pwbuf!=NULL){
    safefree(&MM_Temp,pwbuf); pwbuf=NULL;
  }
  
  if(UTF16Header!=0) ErrorCount=0; // UTF-16ヘッダがあるときは、無条件でUTF-16を選ぶ。
  
  return(ErrorCount);
}

typedef struct {
  void *pBinary;
  s32 BinarySize;
  const u8 *panktbl;
  const u16 *ps2utbl;
  u16 *pu2stbl;
} TEUC2Unicode;

static TEUC2Unicode EUC2Unicode;

static void EUC2Unicode_Init(void)
{
  TEUC2Unicode *ps2u=&EUC2Unicode;
  
  ps2u->pBinary=NULL;
  ps2u->BinarySize=0;
  ps2u->panktbl=NULL;
  ps2u->ps2utbl=NULL;
  ps2u->pu2stbl=NULL;
}

static void EUC2Unicode_Free(void)
{
  TEUC2Unicode *ps2u=&EUC2Unicode;
  
  if(ps2u->pBinary!=NULL){
    safefree(&MM_Temp,ps2u->pBinary); ps2u->pBinary=NULL;
  }
  
  if(ps2u->pu2stbl!=NULL){
    safefree(&MM_Temp,ps2u->pu2stbl); ps2u->pu2stbl=NULL;
  }
  
  EUC2Unicode_Init();
}

static void EUC2Unicode_Load(bool UseInversTable)
{
  TEUC2Unicode *ps2u=&EUC2Unicode;
  
  FAT_FILE *pf=Shell_FAT_fopen_Root("SJISUNIC.TBL");
  if(pf==NULL) StopFatalError(0,"Not found convert table.");
  
  ps2u->BinarySize=FAT2_GetFileSize(pf);
  ps2u->pBinary=safemalloc_chkmem(&MM_Temp,ps2u->BinarySize);
  FAT2_fread_fast(ps2u->pBinary,1,ps2u->BinarySize,pf);
  FAT2_fclose(pf);
  
  ps2u->panktbl=(const u8*)ps2u->pBinary;
  ps2u->ps2utbl=(const u16*)&ps2u->panktbl[256];
  
  if(UseInversTable==false){
    ps2u->pu2stbl=NULL;
    }else{
    ps2u->pu2stbl=(u16*)safemalloc_chkmem(&MM_Temp,0x10000*2);
    MemSet32CPU(0,ps2u->pu2stbl,0x10000*2);
    bool jpnmode=true; // Shell_isJPNmode();
    for(u32 idx=0;idx<0x10000;idx++){
      UnicodeChar wc=0;
      if(idx<0x100){
        if(ps2u->panktbl[idx&0xff]==true){
          wc=idx;
          if(jpnmode==true){
            if((0xa0<=wc)&&(wc<0xe0)) wc=0xff60+(wc-0xa0);
          }
        }
        }else{
        if(ps2u->panktbl[idx>>8]==false){
          wc=ps2u->ps2utbl[idx];
        }
      }
      if(wc!=0){
        if(ps2u->pu2stbl[wc]!=0){
//          _consolePrintf("Duplicate: %04x,%04x,%04x.\n",idx,wc,ps2u->pu2stbl[wc]);
          }else{
          ps2u->pu2stbl[wc]=idx;
        }
      }
    }
  }
}

static u32 TextEdit_LoadFromFile_SJIS(CTextEditLine *pCurLine)
{
  EUC2Unicode_Init();
  EUC2Unicode_Load(false);
  
  TEUC2Unicode *ps2u=&EUC2Unicode;
  
  u32 ErrorCount=0;
  
  u32 wbuflen=0;
  UnicodeChar *pwbuf=NULL;
  u32 wbufpos=0;
  bool firstwrite=true;
  
  bool jpnmode=true; // Shell_isJPNmode();
  
  while(1){
    if(ReadBuf_EOF()==true) break;
    UnicodeChar wc;
    u32 b0=ReadBuf_u8();
    if(ps2u->panktbl[b0]==true){
      if(jpnmode==true){
        if((0xa0<=b0)&&(b0<0xe0)) b0=0xff60+(b0-0xa0);
      }
      wc=b0;
      }else{
      u32 b1=ReadBuf_u8();
      if(b1==0) break;
      u32 euc=(b0<<8)|b1;
      wc=ps2u->ps2utbl[euc];
    }
    if((wc==WC_CR)||(wc==WC_LF)){
      if(pCurLine!=NULL){
        if(wbufpos==0){
          pCurLine->AddNewLine();
          pCurLine=pCurLine->pNext;
          }else{
          if(firstwrite==true){
            firstwrite=false;
            pwbuf[wbufpos]=0;
            pCurLine->AddStr((u32)-1,pwbuf);
            }else{
            pCurLine->AddNewLineWithSet(pwbuf,wbufpos);
            pCurLine=pCurLine->pNext;
          }
          wbufpos=0;
        }
      }
      if(wc==WC_CR){
        ReturnCode=ERC_CRLF;
        if(ReadBuf_u8()!=WC_LF){
          ReadBuf_Back8();
          ReturnCode=ERC_CR;
        }
        }else{
        ReturnCode=ERC_LFCR;
        if(ReadBuf_u8()!=WC_CR){
          ReadBuf_Back8();
          ReturnCode=ERC_LF;
        }
      }
      }else{
      if(pCurLine==NULL){
        if(pTestFont->isExists(wc)==false) ErrorCount++;
        }else{
        if(wbuflen<=(wbufpos+2)){
          wbuflen+=128;
          pwbuf=(UnicodeChar*)saferealloc(&MM_Temp,pwbuf,(wbuflen+2)*sizeof(UnicodeChar));
        }
        pwbuf[wbufpos++]=wc;
      }
    }
  }
  
  if(wbufpos!=0){
    if(pCurLine!=NULL) pCurLine->AddNewLineWithSet(pwbuf,wbufpos);
  }
  
  if(pwbuf!=NULL){
    safefree(&MM_Temp,pwbuf); pwbuf=NULL;
  }
  
  EUC2Unicode_Free();
  
  return(ErrorCount);
}

void TextEdit_LoadFromFile(void)
{
  TextEdit_ClearAllText();
  
  TextEdit_ChangedFlag=true;
  
  ReadBuf_Start(4*1024);
  
  ReadBuf_SetToTop();
  u32 ErrCnt_UTF8=TextEdit_LoadFromFile_UTF8(NULL);
  _consolePrintf("ErrCnt_UTF8=%d.\n",ErrCnt_UTF8);
  
  ReadBuf_SetToTop();
  u32 ErrCnt_UTF16=TextEdit_LoadFromFile_UTF16(NULL);
  _consolePrintf("ErrCnt_UTF16=%d.\n",ErrCnt_UTF16);
  
  ReadBuf_SetToTop();
  u32 ErrCnt_SJIS=TextEdit_LoadFromFile_SJIS(NULL);
  _consolePrintf("ErrCnt_SJIS=%d.\n",ErrCnt_SJIS);
  
  u32 maxerr=0xffffffff;
  
  if(ErrCnt_UTF8<maxerr) maxerr=ErrCnt_UTF8;
  if(ErrCnt_UTF16<maxerr) maxerr=ErrCnt_UTF16;
  if(ErrCnt_SJIS<maxerr) maxerr=ErrCnt_SJIS;
  
  if(maxerr==ErrCnt_UTF8){
    _consolePrintf("Selected test encode is UTF-8.\n");
    TextEncode=ETE_UTF8;
    }else{
    if(maxerr==ErrCnt_UTF16){
      _consolePrintf("Selected test encode is UTF-16.\n");
      TextEncode=ETE_UTF16;
      }else{
      if(maxerr==ErrCnt_SJIS){
        _consolePrintf("Selected test encode is S-JIS.\n");
        TextEncode=ETE_SJIS;
        }else{
        StopFatalError(0,"Internal error: Not select text encoder.");
      }
    }
  }
  
  if(maxerr!=0){
    SelectEncode_Execute();
    switch(TextEncode){
      case ETE_UTF8: _consolePrintf("User set encode type: UTF-8.\n"); break;
      case ETE_UTF16: _consolePrintf("User set encode type: UTF-16.\n"); break;
      case ETE_SJIS: _consolePrintf("User set encode type: S-JIS.\n"); break;
    }
  }
  
  ReadBuf_End();
  
  ReturnCode=ERC_CRLF;
  
  ReadBuf_Start((u32)-1);
  ReadBuf_SetToTop();
  switch(TextEncode){
    case ETE_UTF8: TextEdit_LoadFromFile_UTF8(pTextEdit->pTopLine); break;
    case ETE_UTF16: TextEdit_LoadFromFile_UTF16(pTextEdit->pTopLine); break;
    case ETE_SJIS: TextEdit_LoadFromFile_SJIS(pTextEdit->pTopLine); break;
  }
  ReadBuf_End();
}

