// Common/Lang.cpp

#include "StdAfx.h"

#include "Lang.h"
#include "StringToInt.h"
#include "UTFConvert.h"

#include "../Windows/FileIO.h"

void CLang::Clear() throw()
{
  _ids.Clear();
  _offsets.Clear();
  Comments.Clear();
  delete []_text;
  _text = NULL;
}

static const char * const kLangSignature = ";!@Lang2@!UTF-8!\n";

bool CLang::OpenFromString(const AString &s2)
{
  UString su;
  if (!ConvertUTF8ToUnicode(s2, su))
    return false;
  if (su.IsEmpty())
    return false;
  const wchar_t *s = su;
  const wchar_t *sLim = s + su.Len();
  if (*s == 0xFEFF)
    s++;
  for (const char *p = kLangSignature;; s++)
  {
    const Byte c = (Byte)(*p++);
    if (c == 0)
      break;
    if (*s != c)
      return false;
  }

  wchar_t *text = new wchar_t[(size_t)(sLim - s) + 1];
  _text = text;

  UString comment;
  Int32 id = -1024;
  unsigned pos = 0;

  while (s != sLim)
  {
    const unsigned start = pos;
    do
    {
      wchar_t c = *s++;
      if (c == '\n')
        break;
      if (c == '\\')
      {
        if (s == sLim)
          return false;
        c = *s++;
        switch (c)
        {
          case '\n': return false;
          case 'n': c = '\n'; break;
          case 't': c = '\t'; break;
          case '\\': /* c = '\\'; */ break;
          default: text[pos++] = L'\\'; break;
        }
      }
      text[pos++] = c;
    }
    while (s != sLim);

    {
      unsigned j = start;
      for (; j < pos; j++)
        if (text[j] != ' ' && text[j] != '\t')
          break;
      if (j == pos)
      {
        id++;
        pos = start;
        continue;
      }
    }

    // start != pos
    text[pos++] = 0;

    if (text[start] == ';')
    {
      comment = text + start;
      comment.TrimRight();
      if (comment.Len() != 1)
        Comments.Add(comment);
      id++;
      pos = start;
      continue;
    }

    const wchar_t *end;
    const UInt32 id32 = ConvertStringToUInt32(text + start, &end);
    if (*end == 0)
    {
      if (id32 > ((UInt32)1 << 30) || (Int32)id32 < id)
        return false;
      id = (Int32)id32;
      pos = start;
      continue;
    }

    if (id < 0)
      return false;
    _ids.Add((UInt32)id++);
    _offsets.Add(start);
  }

  return true;
}

bool CLang::Open(CFSTR fileName, const char *id)
{
  Clear();
  NWindows::NFile::NIO::CInFile file;
  if (!file.Open(fileName))
    return false;
  UInt64 length;
  if (!file.GetLength(length))
    return false;
  if (length > (1 << 20))
    return false;
  
  AString s;
  const unsigned len = (unsigned)length;
  char *p = s.GetBuf(len);
  size_t processed;
  if (!file.ReadFull(p, len, processed))
    return false;
  file.Close();
  if (len != processed)
    return false;

  char *p2 = p;
  for (unsigned i = 0; i < len; i++)
  {
    const char c = p[i];
    if (c == 0)
      break;
    if (c != 0x0D)
      *p2++ = c;
  }
  *p2 = 0;
  s.ReleaseBuf_SetLen((unsigned)(p2 - p));
  
  if (OpenFromString(s))
  {
    const wchar_t *name = Get(0);
    if (name && StringsAreEqual_Ascii(name, id))
      return true;
  }
  
  Clear();
  return false;
}

const wchar_t *CLang::Get(UInt32 id) const throw()
{
  const int index = _ids.FindInSorted(id);
  if (index < 0)
    return NULL;
  return _text + (size_t)_offsets[(unsigned)index];
}
