/** @file
  ACPI Sdt Protocol Driver

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

**/

#include "AcpiTable.h"

/**
  Check if it is AML Root name

  @param[in]    Buffer AML path.

  @retval       TRUE  AML path is root.
  @retval       FALSE AML path is not root.
**/
BOOLEAN
AmlIsRootPath (
  IN UINT8              *Buffer
  )
{
  if ((Buffer[0] == AML_ROOT_CHAR) && (Buffer[1] == 0)) {
    return TRUE;
  } else {
    return FALSE;
  }
}

/**
  Check if it is AML LeadName.

  @param[in]    Ch   Char.

  @retval       TRUE  Char is AML LeadName.
  @retval       FALSE Char is not AML LeadName.
**/
BOOLEAN
AmlIsLeadName (
  IN CHAR8 Ch
  )
{
  if ((Ch == '_') || (Ch >= 'A' && Ch <= 'Z')) {
    return TRUE;
  } else {
    return FALSE;
  }
}

/**
  Check if it is AML Name.

  @param[in]    Ch   Char.

  @retval       TRUE  Char is AML Name.
  @retval       FALSE Char is not AML Name.
**/
BOOLEAN
AmlIsName (
  IN CHAR8 Ch
  )
{
  if (AmlIsLeadName (Ch) || (Ch >= '0' && Ch <= '9')) {
    return TRUE;
  } else {
    return FALSE;
  }
}

/**
  Return is buffer is AML NameSeg.

  @param[in]    Buffer     AML NameSement.

  @retval       TRUE       It is AML NameSegment.
  @retval       FALSE      It is not AML NameSegment.
**/
BOOLEAN
AmlIsNameSeg (
  IN  UINT8              *Buffer
  )
{
  UINTN  Index;
  if (!AmlIsLeadName (Buffer[0])) {
    return FALSE;
  }
  for (Index = 1; Index < AML_NAME_SEG_SIZE; Index++) {
    if (!AmlIsName (Buffer[Index])) {
      return FALSE;
    }
  }
  return TRUE;
}

/**
  Get AML NameString size.

  @param[in]    Buffer     AML NameString.
  @param[out]   BufferSize AML NameString size

  @retval       EFI_SUCCESS           Success.
  @retval       EFI_INVALID_PARAMETER Buffer does not refer to a valid AML NameString.
**/
EFI_STATUS
AmlGetNameStringSize (
  IN  UINT8              *Buffer,
  OUT UINTN              *BufferSize
  )
{
  UINTN                 SegCount;
  UINTN                 Length;
  UINTN                 Index;

  Length = 0;

  //
  // Parse root or parent prefix
  //
  if (*Buffer == AML_ROOT_CHAR) {
    Buffer ++;
    Length ++;
  } else if (*Buffer == AML_PARENT_PREFIX_CHAR) {
    do {
      Buffer ++;
      Length ++;
    } while (*Buffer == AML_PARENT_PREFIX_CHAR);
  }

  //
  // Parse name segment
  //
  if (*Buffer == AML_DUAL_NAME_PREFIX) {
    Buffer ++;
    Length ++;
    SegCount = 2;
  } else if (*Buffer == AML_MULTI_NAME_PREFIX) {
    Buffer ++;
    Length ++;
    SegCount = *Buffer;
    Buffer ++;
    Length ++;
  } else if (*Buffer == 0) {
    //
    // NULL Name, only for Root
    //
    SegCount = 0;
    Buffer --;
    if ((Length == 1) && (*Buffer == AML_ROOT_CHAR)) {
      *BufferSize = 2;
      return EFI_SUCCESS;
    } else {
      return EFI_INVALID_PARAMETER;
    }
  } else {
    //
    // NameSeg
    //
    SegCount = 1;
  }

  Index = 0;
  do {
    if (!AmlIsNameSeg (Buffer)) {
      return EFI_INVALID_PARAMETER;
    }
    Buffer += AML_NAME_SEG_SIZE;
    Length += AML_NAME_SEG_SIZE;
    Index ++;
  } while (Index < SegCount);

  *BufferSize = Length;
  return EFI_SUCCESS;
}

/**
  Check if it is ASL LeadName.

  @param[in]    Ch   Char.

  @retval       TRUE  Char is ASL LeadName.
  @retval       FALSE Char is not ASL LeadName.
**/
BOOLEAN
AmlIsAslLeadName (
  IN CHAR8 Ch
  )
{
  if (AmlIsLeadName (Ch) || (Ch >= 'a' && Ch <= 'z')) {
    return TRUE;
  } else {
    return FALSE;
  }
}

/**
  Check if it is ASL Name.

  @param[in]    Ch   Char.

  @retval       TRUE  Char is ASL Name.
  @retval       FALSE Char is not ASL Name.
**/
BOOLEAN
AmlIsAslName (
  IN CHAR8 Ch
  )
{
  if (AmlIsAslLeadName (Ch) || (Ch >= '0' && Ch <= '9')) {
    return TRUE;
  } else {
    return FALSE;
  }
}

/**
  Get ASL NameString size.

  @param[in]    Buffer   ASL NameString.

  @return       ASL NameString size.
**/
UINTN
AmlGetAslNameSegLength (
  IN UINT8 *Buffer
  )
{
  UINTN Length;
  UINTN Index;

  if (*Buffer == 0) {
    return 0;
  }

  Length = 0;
  //
  // 1st
  //
  if (AmlIsAslLeadName (*Buffer)) {
    Length ++;
    Buffer ++;
  }
  if ((*Buffer == 0) || (*Buffer == '.')) {
    return Length;
  }
  //
  // 2, 3, 4 name char
  //
  for (Index = 0; Index < 3; Index++) {
    if (AmlIsAslName (*Buffer)) {
      Length ++;
      Buffer ++;
    }
    if ((*Buffer == 0) || (*Buffer == '.')) {
      return Length;
    }
  }

  //
  // Invalid ASL name
  //
  return 0;
}

/**
  Get ASL NameString size.

  @param[in]    Buffer   ASL NameString.
  @param[out]   Root     On return, points to Root char number.
  @param[out]   Parent   On return, points to Parent char number.
  @param[out]   SegCount On return, points to Segment count.

  @return       ASL NameString size.
**/
UINTN
AmlGetAslNameStringSize (
  IN UINT8              *Buffer,
  OUT UINTN             *Root,
  OUT UINTN             *Parent,
  OUT UINTN             *SegCount
  )
{
  UINTN NameLength;
  UINTN TotalLength;

  *Root   = 0;
  *Parent = 0;
  *SegCount = 0;
  TotalLength = 0;
  NameLength = 0;
  if (*Buffer == AML_ROOT_CHAR) {
    *Root = 1;
    Buffer ++;
  } else if (*Buffer == AML_PARENT_PREFIX_CHAR) {
    do {
      Buffer ++;
      (*Parent) ++;
    } while (*Buffer == AML_PARENT_PREFIX_CHAR);
  }

  //
  // Now parse name
  //
  while (*Buffer != 0) {
    NameLength = AmlGetAslNameSegLength (Buffer);
    if ((NameLength == 0) || (NameLength > AML_NAME_SEG_SIZE)) {
      return 0;
    }
    (*SegCount) ++;
    Buffer += NameLength;
    if (*Buffer == 0) {
      break;
    }
    Buffer ++;
  }

  //
  // Check SegCoount
  //
  if (*SegCount > 0xFF) {
    return 0;
  }

  //
  // Calculate total length
  //
  TotalLength = *Root + *Parent + (*SegCount) * AML_NAME_SEG_SIZE;
  if (*SegCount > 2) {
    TotalLength += 2;
  } else if (*SegCount == 2) {
    TotalLength += 1;
  }

  //
  // Add NULL char
  //
  TotalLength ++;

  return TotalLength;
}

/**
  Copy mem, and cast all the char in dest to be upper case.

  @param[in]    DstBuffer   Destination buffer.
  @param[in]    SrcBuffer   Source buffer.
  @param[in]    Length      Buffer length.
**/
VOID
AmlUpperCaseCopyMem (
  IN UINT8 *DstBuffer,
  IN UINT8 *SrcBuffer,
  IN UINTN Length
  )
{
  UINTN Index;

  for (Index = 0; Index < Length; Index++) {
    if (SrcBuffer[Index] >= 'a' && SrcBuffer[Index] <= 'z') {
      DstBuffer[Index] = (UINT8)(SrcBuffer[Index] - 'a' + 'A');
    } else {
      DstBuffer[Index] = SrcBuffer[Index];
    }
  }
}

/**
  Return AML name according to ASL name.
  The caller need free the AmlName returned.

  @param[in]    AslPath     ASL name.

  @return AmlName
**/
UINT8 *
AmlNameFromAslName (
  IN UINT8 *AslPath
  )
{
  UINTN Root;
  UINTN Parent;
  UINTN SegCount;
  UINTN TotalLength;
  UINTN NameLength;
  UINT8 *Buffer;
  UINT8 *AmlPath;
  UINT8 *AmlBuffer;

  TotalLength = AmlGetAslNameStringSize (AslPath, &Root, &Parent, &SegCount);
  if (TotalLength == 0) {
    return NULL;
  }

  AmlPath = AllocatePool (TotalLength);
  ASSERT (AmlPath != NULL);

  AmlBuffer = AmlPath;
  Buffer = AslPath;

  //
  // Handle Root and Parent
  //
  if (Root == 1) {
    *AmlBuffer = AML_ROOT_CHAR;
    AmlBuffer ++;
    Buffer ++;
  } else if (Parent > 0) {
    SetMem (AmlBuffer, Parent, AML_PARENT_PREFIX_CHAR);
    AmlBuffer += Parent;
    Buffer += Parent;
  }

  //
  // Handle SegCount
  //
  if (SegCount > 2) {
    *AmlBuffer = AML_MULTI_NAME_PREFIX;
    AmlBuffer ++;
    *AmlBuffer = (UINT8)SegCount;
    AmlBuffer ++;
  } else if (SegCount == 2) {
    *AmlBuffer = AML_DUAL_NAME_PREFIX;
    AmlBuffer ++;
  }

  //
  // Now to name
  //
  while (*Buffer != 0) {
    NameLength = AmlGetAslNameSegLength (Buffer);
    ASSERT ((NameLength != 0) && (NameLength <= AML_NAME_SEG_SIZE));
    AmlUpperCaseCopyMem (AmlBuffer, Buffer, NameLength);
    SetMem (AmlBuffer + NameLength, AML_NAME_SEG_SIZE - NameLength, AML_NAME_CHAR__);
    Buffer += NameLength;
    AmlBuffer += AML_NAME_SEG_SIZE;
    if (*Buffer == 0) {
      break;
    }
    Buffer ++;
  }

  //
  // Add NULL
  //
  AmlPath[TotalLength - 1] = 0;

  return AmlPath;
}

/**
  Print AML NameSeg.

  @param[in] Buffer AML NameSeg.
**/
VOID
AmlPrintNameSeg (
  IN UINT8              *Buffer
  )
{
  DEBUG ((EFI_D_ERROR, "%c", Buffer[0]));
  if ((Buffer[1] == '_') && (Buffer[2] == '_') && (Buffer[3] == '_')) {
    return ;
  }
  DEBUG ((EFI_D_ERROR, "%c", Buffer[1]));
  if ((Buffer[2] == '_') && (Buffer[3] == '_')) {
    return ;
  }
  DEBUG ((EFI_D_ERROR, "%c", Buffer[2]));
  if (Buffer[3] == '_') {
    return ;
  }
  DEBUG ((EFI_D_ERROR, "%c", Buffer[3]));
  return ;
}

/**
  Print AML NameString.

  @param[in] Buffer AML NameString.
**/
VOID
AmlPrintNameString (
  IN UINT8              *Buffer
  )
{
  UINT8                 SegCount;
  UINT8                 Index;

  if (*Buffer == AML_ROOT_CHAR) {
    //
    // RootChar
    //
    Buffer ++;
    DEBUG ((EFI_D_ERROR, "\\"));
  } else if (*Buffer == AML_PARENT_PREFIX_CHAR) {
    //
    // ParentPrefixChar
    //
    do {
      Buffer ++;
      DEBUG ((EFI_D_ERROR, "^"));
    } while (*Buffer == AML_PARENT_PREFIX_CHAR);
  }

  if (*Buffer == AML_DUAL_NAME_PREFIX) {
    //
    // DualName
    //
    Buffer ++;
    SegCount = 2;
  } else if (*Buffer == AML_MULTI_NAME_PREFIX) {
    //
    // MultiName
    //
    Buffer ++;
    SegCount = *Buffer;
    Buffer ++;
  } else if (*Buffer == 0) {
    //
    // NULL Name
    //
    return ;
  } else {
    //
    // NameSeg
    //
    SegCount = 1;
  }

  AmlPrintNameSeg (Buffer);
  Buffer += AML_NAME_SEG_SIZE;
  for (Index = 0; Index < SegCount - 1; Index++) {
    DEBUG ((EFI_D_ERROR, "."));
    AmlPrintNameSeg (Buffer);
    Buffer += AML_NAME_SEG_SIZE;
  }

  return ;
}
