/** @file
  Implementation of translation upon VT-UTF8.

Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "Terminal.h"

/**
  Translate all VT-UTF8 characters in the Raw FIFI into unicode characters,
  and insert them into Unicode FIFO.

  @param TerminalDevice          The terminal device.

**/
VOID
VTUTF8RawDataToUnicode (
  IN  TERMINAL_DEV  *TerminalDevice
  )
{
  UTF8_CHAR  Utf8Char;
  UINT8      ValidBytes;
  UINT16     UnicodeChar;

  ValidBytes = 0;
  //
  // pop the raw data out from the raw fifo,
  // and translate it into unicode, then push
  // the unicode into unicode fifo, until the raw fifo is empty.
  //
  while (!IsRawFiFoEmpty (TerminalDevice) && !IsUnicodeFiFoFull (TerminalDevice)) {
    GetOneValidUtf8Char (TerminalDevice, &Utf8Char, &ValidBytes);

    if ((ValidBytes < 1) || (ValidBytes > 3)) {
      continue;
    }

    Utf8ToUnicode (Utf8Char, ValidBytes, (CHAR16 *)&UnicodeChar);

    UnicodeFiFoInsertOneKey (TerminalDevice, UnicodeChar);
  }
}

/**
  Get one valid VT-UTF8 characters set from Raw Data FIFO.

  @param  Utf8Device          The terminal device.
  @param  Utf8Char            Returned valid VT-UTF8 characters set.
  @param  ValidBytes          The count of returned VT-VTF8 characters.
                              If ValidBytes is zero, no valid VT-UTF8 returned.

**/
VOID
GetOneValidUtf8Char (
  IN  TERMINAL_DEV  *Utf8Device,
  OUT UTF8_CHAR     *Utf8Char,
  OUT UINT8         *ValidBytes
  )
{
  UINT8    Temp;
  UINT8    Index;
  BOOLEAN  FetchFlag;

  Temp      = 0;
  Index     = 0;
  FetchFlag = TRUE;

  //
  // if no valid Utf8 char is found in the RawFiFo,
  // then *ValidBytes will be zero.
  //
  *ValidBytes = 0;

  while (!IsRawFiFoEmpty (Utf8Device)) {
    RawFiFoRemoveOneKey (Utf8Device, &Temp);

    switch (*ValidBytes) {
      case 0:
        if ((Temp & 0x80) == 0) {
          //
          // one-byte utf8 char
          //
          *ValidBytes = 1;

          Utf8Char->Utf8_1 = Temp;

          FetchFlag = FALSE;
        } else if ((Temp & 0xe0) == 0xc0) {
          //
          // two-byte utf8 char
          //
          *ValidBytes = 2;

          Utf8Char->Utf8_2[1] = Temp;
        } else if ((Temp & 0xf0) == 0xe0) {
          //
          // three-byte utf8 char
          //
          *ValidBytes = 3;

          Utf8Char->Utf8_3[2] = Temp;

          Index++;
        } else {
          //
          // reset *ValidBytes to zero, let valid utf8 char search restart
          //
          *ValidBytes = 0;
        }

        break;

      case 2:
        //
        // two-byte utf8 char go on
        //
        if ((Temp & 0xc0) == 0x80) {
          Utf8Char->Utf8_2[0] = Temp;

          FetchFlag = FALSE;
        } else {
          *ValidBytes = 0;
        }

        break;

      case 3:
        //
        // three-byte utf8 char go on
        //
        if ((Temp & 0xc0) == 0x80) {
          if (Index == 1) {
            Utf8Char->Utf8_3[1] = Temp;
            Index++;
          } else {
            Utf8Char->Utf8_3[0] = Temp;
            FetchFlag           = FALSE;
          }
        } else {
          //
          // reset *ValidBytes and Index to zero, let valid utf8 char search restart
          //
          *ValidBytes = 0;
          Index       = 0;
        }

        break;

      default:
        break;
    }

    if (!FetchFlag) {
      break;
    }
  }

  return;
}

/**
  Translate VT-UTF8 characters into one Unicode character.

  UTF8 Encoding Table
  Bits per Character | Unicode Character Range | Unicode Binary  Encoding |  UTF8 Binary Encoding
        0-7           |     0x0000 - 0x007F     |     00000000 0xxxxxxx    |   0xxxxxxx
        8-11          |     0x0080 - 0x07FF     |     00000xxx xxxxxxxx     |   110xxxxx 10xxxxxx
       12-16         |     0x0800 - 0xFFFF     |     xxxxxxxx xxxxxxxx    |   1110xxxx 10xxxxxx 10xxxxxx


  @param  Utf8Char         VT-UTF8 character set needs translating.
  @param  ValidBytes       The count of valid VT-UTF8 characters.
  @param  UnicodeChar      Returned unicode character.

**/
VOID
Utf8ToUnicode (
  IN  UTF8_CHAR  Utf8Char,
  IN  UINT8      ValidBytes,
  OUT CHAR16     *UnicodeChar
  )
{
  UINT8  UnicodeByte0;
  UINT8  UnicodeByte1;
  UINT8  Byte0;
  UINT8  Byte1;
  UINT8  Byte2;

  *UnicodeChar = 0;

  //
  // translate utf8 code to unicode, in terminal standard,
  // up to 3 bytes utf8 code is supported.
  //
  switch (ValidBytes) {
    case 1:
      //
      // one-byte utf8 code
      //
      *UnicodeChar = (UINT16)Utf8Char.Utf8_1;
      break;

    case 2:
      //
      // two-byte utf8 code
      //
      Byte0 = Utf8Char.Utf8_2[0];
      Byte1 = Utf8Char.Utf8_2[1];

      UnicodeByte0 = (UINT8)((Byte1 << 6) | (Byte0 & 0x3f));
      UnicodeByte1 = (UINT8)((Byte1 >> 2) & 0x07);
      *UnicodeChar = (UINT16)(UnicodeByte0 | (UnicodeByte1 << 8));
      break;

    case 3:
      //
      // three-byte utf8 code
      //
      Byte0 = Utf8Char.Utf8_3[0];
      Byte1 = Utf8Char.Utf8_3[1];
      Byte2 = Utf8Char.Utf8_3[2];

      UnicodeByte0 = (UINT8)((Byte1 << 6) | (Byte0 & 0x3f));
      UnicodeByte1 = (UINT8)((Byte2 << 4) | ((Byte1 >> 2) & 0x0f));
      *UnicodeChar = (UINT16)(UnicodeByte0 | (UnicodeByte1 << 8));

    default:
      break;
  }

  return;
}

/**
  Translate one Unicode character into VT-UTF8 characters.

  UTF8 Encoding Table
  Bits per Character | Unicode Character Range | Unicode Binary  Encoding |  UTF8 Binary Encoding
        0-7           |     0x0000 - 0x007F     |     00000000 0xxxxxxx    |   0xxxxxxx
        8-11          |     0x0080 - 0x07FF     |     00000xxx xxxxxxxx     |   110xxxxx 10xxxxxx
       12-16         |     0x0800 - 0xFFFF     |     xxxxxxxx xxxxxxxx    |   1110xxxx 10xxxxxx 10xxxxxx


  @param  Unicode          Unicode character need translating.
  @param  Utf8Char         Return VT-UTF8 character set.
  @param  ValidBytes       The count of valid VT-UTF8 characters. If
                           ValidBytes is zero, no valid VT-UTF8 returned.

**/
VOID
UnicodeToUtf8 (
  IN  CHAR16     Unicode,
  OUT UTF8_CHAR  *Utf8Char,
  OUT UINT8      *ValidBytes
  )
{
  UINT8  UnicodeByte0;
  UINT8  UnicodeByte1;

  //
  // translate unicode to utf8 code
  //
  UnicodeByte0 = (UINT8)Unicode;
  UnicodeByte1 = (UINT8)(Unicode >> 8);

  if (Unicode < 0x0080) {
    Utf8Char->Utf8_1 = (UINT8)(UnicodeByte0 & 0x7f);
    *ValidBytes      = 1;
  } else if (Unicode < 0x0800) {
    //
    // byte sequence: high -> low
    //                Utf8_2[0], Utf8_2[1]
    //
    Utf8Char->Utf8_2[1] = (UINT8)((UnicodeByte0 & 0x3f) + 0x80);
    Utf8Char->Utf8_2[0] = (UINT8)((((UnicodeByte1 << 2) + (UnicodeByte0 >> 6)) & 0x1f) + 0xc0);

    *ValidBytes = 2;
  } else {
    //
    // byte sequence: high -> low
    //                Utf8_3[0], Utf8_3[1], Utf8_3[2]
    //
    Utf8Char->Utf8_3[2] = (UINT8)((UnicodeByte0 & 0x3f) + 0x80);
    Utf8Char->Utf8_3[1] = (UINT8)((((UnicodeByte1 << 2) + (UnicodeByte0 >> 6)) & 0x3f) + 0x80);
    Utf8Char->Utf8_3[0] = (UINT8)(((UnicodeByte1 >> 4) & 0x0f) + 0xe0);

    *ValidBytes = 3;
  }
}

/**
  Check if input string is valid VT-UTF8 string.

  @param  TerminalDevice          The terminal device.
  @param  WString                 The input string.

  @retval EFI_SUCCESS             If all input characters are valid.

**/
EFI_STATUS
VTUTF8TestString (
  IN  TERMINAL_DEV  *TerminalDevice,
  IN  CHAR16        *WString
  )
{
  //
  // to utf8, all kind of characters are supported.
  //
  return EFI_SUCCESS;
}
