/** @file
  The implementation of EDKII Redfish Platform Config Protocol.

  (C) Copyright 2021-2022 Hewlett Packard Enterprise Development LP<BR>
  Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
  Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#include "RedfishPlatformConfigDxe.h"
#include "RedfishPlatformConfigImpl.h"

extern REDFISH_PLATFORM_CONFIG_PRIVATE  *mRedfishPlatformConfigPrivate;

/**
  Debug dump HII string.

  @param[in]  HiiHandle   HII handle instance
  @param[in]  StringId    HII string to dump

  @retval EFI_SUCCESS       Dump HII string successfully
  @retval Others            Errors occur

**/
EFI_STATUS
DumpHiiString (
  IN EFI_HII_HANDLE  HiiHandle,
  IN EFI_STRING_ID   StringId
  )
{
  EFI_STRING  String;

  if ((HiiHandle == NULL) || (StringId == 0)) {
    DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "???"));
    return EFI_INVALID_PARAMETER;
  }

  String = HiiGetString (HiiHandle, StringId, NULL);
  if (String == NULL) {
    return EFI_NOT_FOUND;
  }

  DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%s", String));
  FreePool (String);

  return EFI_SUCCESS;
}

/**
  Debug dump HII form-set data.

  @param[in]  FormsetPrivate    HII form-set private instance.

  @retval EFI_SUCCESS       Dump form-set successfully
  @retval Others            Errors occur

**/
EFI_STATUS
DumpFormset (
  IN REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate
  )
{
  LIST_ENTRY                                 *HiiFormLink;
  LIST_ENTRY                                 *HiiNextFormLink;
  REDFISH_PLATFORM_CONFIG_FORM_PRIVATE       *HiiFormPrivate;
  LIST_ENTRY                                 *HiiStatementLink;
  LIST_ENTRY                                 *HiiNextStatementLink;
  REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE  *HiiStatementPrivate;
  UINTN                                      Index;

  if (FormsetPrivate == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Index       = 0;
  HiiFormLink = GetFirstNode (&FormsetPrivate->HiiFormList);
  while (!IsNull (&FormsetPrivate->HiiFormList, HiiFormLink)) {
    HiiFormPrivate  = REDFISH_PLATFORM_CONFIG_FORM_FROM_LINK (HiiFormLink);
    HiiNextFormLink = GetNextNode (&FormsetPrivate->HiiFormList, HiiFormLink);

    DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "  [%d] form: %d title: ", ++Index, HiiFormPrivate->Id));
    DumpHiiString (FormsetPrivate->HiiHandle, HiiFormPrivate->Title);
    DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "\n"));

    HiiStatementLink = GetFirstNode (&HiiFormPrivate->StatementList);
    while (!IsNull (&HiiFormPrivate->StatementList, HiiStatementLink)) {
      HiiStatementPrivate  = REDFISH_PLATFORM_CONFIG_STATEMENT_FROM_LINK (HiiStatementLink);
      HiiNextStatementLink = GetNextNode (&HiiFormPrivate->StatementList, HiiStatementLink);

      DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "    QID: 0x%x Prompt: ", HiiStatementPrivate->QuestionId));
      DumpHiiString (FormsetPrivate->HiiHandle, HiiStatementPrivate->Description);
      DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "\n"));

      HiiStatementLink = HiiNextStatementLink;
    }

    HiiFormLink = HiiNextFormLink;
  }

  return EFI_SUCCESS;
}

/**
  Debug dump HII form-set list.

  @param[in]  FormsetList   Form-set list instance

  @retval EFI_SUCCESS       Dump list successfully
  @retval Others            Errors occur

**/
EFI_STATUS
DumpFormsetList (
  IN  LIST_ENTRY  *FormsetList
  )
{
  LIST_ENTRY                                *HiiFormsetLink;
  LIST_ENTRY                                *HiiFormsetNextLink;
  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *HiiFormsetPrivate;
  UINTN                                     Index;

  if (FormsetList == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (IsListEmpty (FormsetList)) {
    DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%a: Empty formset list\n", __func__));
    return EFI_SUCCESS;
  }

  Index          = 0;
  HiiFormsetLink = GetFirstNode (FormsetList);
  while (!IsNull (FormsetList, HiiFormsetLink)) {
    HiiFormsetNextLink = GetNextNode (FormsetList, HiiFormsetLink);
    HiiFormsetPrivate  = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink);

    DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "[%d] HII Handle: 0x%x formset: %g at %s\n", ++Index, HiiFormsetPrivate->HiiHandle, &HiiFormsetPrivate->Guid, HiiFormsetPrivate->DevicePathStr));
    DumpFormset (HiiFormsetPrivate);

    HiiFormsetLink = HiiFormsetNextLink;
  }

  return EFI_SUCCESS;
}

/**
  Return the HII string length. We don't check word alignment
  of the input string as same as the checking in StrLen
  function, because the HII string in the database is compact
  at the byte alignment.

  @param[in]  String  Input UCS format string.

  @retval Length of the string.

**/
UINTN
EFIAPI
HiiStrLen (
  IN  CONST CHAR16  *String
  )
{
  UINTN  Length;

  ASSERT (String != NULL);

  for (Length = 0; *String != L'\0'; String++, Length++) {
  }

  return Length;
}

/**
  Return the HII string size. We don't check word alignment
  of the input string as same as the checking in StrLen
  function, because the HII string in the database is compact
  at the byte alignment.

  @param[in]  String  Input UCS format string.

  @retval Size of the string.

**/
UINTN
EFIAPI
HiiStrSize (
  IN      CONST CHAR16  *String
  )
{
  return (HiiStrLen (String) + 1) * sizeof (*String);
}

/**
  Compare two HII strings. We don't check word alignment
  of the input string as same as the checking in StrLen
  function, because the HII string in the database is compact
  at the byte alignment.

  @param[in]  FirstString   Input UCS format of string to search.
  @param[in]  SecondString  Input UCS format of string to look for in
                            FirstString;

  @retval 0   The strings are identical.
          !0  The strings are not identical.

**/
INTN
EFIAPI
HiiStrCmp (
  IN      CONST CHAR16  *FirstString,
  IN      CONST CHAR16  *SecondString
  )
{
  //
  // ASSERT both strings are less long than PcdMaximumUnicodeStringLength
  //
  ASSERT (HiiStrSize (FirstString) != 0);
  ASSERT (HiiStrSize (SecondString) != 0);

  while ((*FirstString != L'\0') && (*FirstString == *SecondString)) {
    FirstString++;
    SecondString++;
  }

  return *FirstString - *SecondString;
}

/**
  Delete a string from HII Package List by given HiiHandle.

  @param[in]  StringId           Id of the string in HII database.
  @param[in]  HiiHandle          The HII package list handle.

  @retval EFI_SUCCESS            The string was deleted successfully.
  @retval EFI_INVALID_PARAMETER  StringId is zero.

**/
EFI_STATUS
HiiDeleteString (
  IN  EFI_STRING_ID   StringId,
  IN  EFI_HII_HANDLE  HiiHandle
  )
{
  CHAR16  NullChar;

  if (StringId == 0x00) {
    return EFI_INVALID_PARAMETER;
  }

  NullChar = CHAR_NULL;
  HiiSetString (HiiHandle, StringId, &NullChar, NULL);

  return EFI_SUCCESS;
}

/**
  Retrieves a unicode string from a string package in a given language. The
  returned string is allocated using AllocatePool().  The caller is responsible
  for freeing the allocated buffer using FreePool().

  If HiiHandle is NULL, then ASSERT().
  If StringId is 0, then ASSET.

  @param[in]  HiiHandle         A handle that was previously registered in the HII Database.
  @param[in]  Language          The specified configure language to get string.
  @param[in]  StringId          The identifier of the string to retrieved from the string
                                package associated with HiiHandle.

  @retval NULL   The string specified by StringId is not present in the string package.
  @retval Other  The string was returned.

**/
EFI_STRING
HiiGetRedfishString (
  IN EFI_HII_HANDLE  HiiHandle,
  IN CHAR8           *Language,
  IN EFI_STRING_ID   StringId
  )
{
  EFI_STATUS  Status;
  UINTN       StringSize;
  CHAR16      TempString;
  EFI_STRING  String;

  if ((mRedfishPlatformConfigPrivate->HiiString == NULL) || (HiiHandle == NULL) || (StringId == 0) || IS_EMPTY_STRING (Language)) {
    ASSERT (FALSE);
    return NULL;
  }

  //
  // Retrieve the size of the string in the string package for the BestLanguage
  //
  StringSize = 0;
  Status     = mRedfishPlatformConfigPrivate->HiiString->GetString (
                                                           mRedfishPlatformConfigPrivate->HiiString,
                                                           Language,
                                                           HiiHandle,
                                                           StringId,
                                                           &TempString,
                                                           &StringSize,
                                                           NULL
                                                           );
  //
  // If GetString() returns EFI_SUCCESS for a zero size,
  // then there are no supported languages registered for HiiHandle.  If GetString()
  // returns an error other than EFI_BUFFER_TOO_SMALL, then HiiHandle is not present
  // in the HII Database
  //
  if (Status != EFI_BUFFER_TOO_SMALL) {
    return NULL;
  }

  //
  // Allocate a buffer for the return string
  //
  String = AllocateZeroPool (StringSize);
  if (String == NULL) {
    return NULL;
  }

  //
  // Retrieve the string from the string package
  //
  Status = mRedfishPlatformConfigPrivate->HiiString->GetString (
                                                       mRedfishPlatformConfigPrivate->HiiString,
                                                       Language,
                                                       HiiHandle,
                                                       StringId,
                                                       String,
                                                       &StringSize,
                                                       NULL
                                                       );
  if (EFI_ERROR (Status)) {
    //
    // Free the buffer and return NULL if the supported languages can not be retrieved.
    //
    FreePool (String);
    String = NULL;
  }

  //
  // Return the Null-terminated Unicode string
  //
  return String;
}

/**
  Retrieves a ASCII string from a string package in a given language. The
  returned string is allocated using AllocatePool().  The caller is responsible
  for freeing the allocated buffer using FreePool().

  If HiiHandle is NULL, then ASSERT().
  If StringId is 0, then ASSET.

  @param[in]  HiiHandle         A handle that was previously registered in the HII Database.
  @param[in]  Language          The specified configure language to get string.
  @param[in]  StringId          The identifier of the string to retrieved from the string
                                package associated with HiiHandle.

  @retval NULL   The string specified by StringId is not present in the string package.
  @retval Other  The string was returned.

**/
CHAR8 *
HiiGetRedfishAsciiString (
  IN EFI_HII_HANDLE  HiiHandle,
  IN CHAR8           *Language,
  IN EFI_STRING_ID   StringId
  )
{
  EFI_STRING  HiiString;
  CHAR8       *AsciiString;

  HiiString = HiiGetRedfishString (HiiHandle, Language, StringId);
  if (HiiString == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: Can not find string ID: 0x%x with %a\n", __func__, StringId, Language));
    return NULL;
  }

  AsciiString = StrToAsciiStr (HiiString);
  FreePool (HiiString);

  return AsciiString;
}

/**
  Get ASCII string from HII database in English language. The returned string is allocated
  using AllocatePool(). The caller is responsible for freeing the allocated buffer using
  FreePool().

  @param[in]  HiiHandle         A handle that was previously registered in the HII Database.
  @param[in]  StringId          The identifier of the string to retrieved from the string
                                package associated with HiiHandle.

  @retval NULL   The string specified by StringId is not present in the string package.
  @retval Other  The string was returned.

**/
CHAR8 *
HiiGetEnglishAsciiString (
  IN EFI_HII_HANDLE  HiiHandle,
  IN EFI_STRING_ID   StringId
  )
{
  EFI_STRING  HiiString;
  CHAR8       *AsciiString;

  HiiString = HiiGetRedfishString (HiiHandle, ENGLISH_LANGUAGE_CODE, StringId);
  if (HiiString == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: Can not find string ID: 0x%x with %a\n", __func__, StringId, ENGLISH_LANGUAGE_CODE));
    return NULL;
  }

  AsciiString = StrToAsciiStr (HiiString);
  FreePool (HiiString);

  return AsciiString;
}

/**
  Check and see if this is supported schema or not.

  @param[in]  SupportedSchema   The list of supported schema.
  @param[in]  Schema            Schema string to be checked.

  @retval BOOLEAN               TRUE if this is supported schema. FALSE otherwise.

**/
BOOLEAN
CheckSupportedSchema (
  IN REDFISH_PLATFORM_CONFIG_SCHEMA  *SupportedSchema,
  IN CHAR8                           *Schema
  )
{
  UINTN  Index;

  if ((SupportedSchema == NULL) || IS_EMPTY_STRING (Schema)) {
    return FALSE;
  }

  if (SupportedSchema->Count == 0) {
    return FALSE;
  }

  for (Index = 0; Index < SupportedSchema->Count; Index++) {
    if (AsciiStrCmp (SupportedSchema->SchemaList[Index], Schema) == 0) {
      return TRUE;
    }
  }

  return FALSE;
}

/**
  Get the list of supported schema from the given HII handle.

  @param[in]  HiiHandle         HII handle instance.
  @param[out] SupportedSchema   Supported schema on this HII handle.

  @retval EFI_SUCCESS           Schema list is returned.
  @retval EFI_INVALID_PARAMETER HiiHandle is NULL or SupportedSchema is NULL.
  @retval EFI_NOT_FOUND         No supported schema found.
  @retval EFI_OUT_OF_RESOURCES  System is out of memory.

**/
EFI_STATUS
GetSupportedSchema (
  IN  EFI_HII_HANDLE                  HiiHandle,
  OUT REDFISH_PLATFORM_CONFIG_SCHEMA  *SupportedSchema
  )
{
  CHAR8  *SupportedLanguages;
  UINTN  Index;
  UINTN  LangIndex;
  UINTN  Count;
  UINTN  StrSize;
  UINTN  ListIndex;

  if ((HiiHandle == NULL) || (SupportedSchema == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  SupportedSchema->Count = 0;

  SupportedLanguages = HiiGetSupportedLanguages (HiiHandle);
  if (SupportedLanguages == NULL) {
    return EFI_NOT_FOUND;
  }

  Index     = 0;
  LangIndex = 0;
  Count     = 0;
  while (TRUE) {
    if ((SupportedLanguages[Index] == ';') || (SupportedLanguages[Index] == '\0')) {
      if (AsciiStrnCmp (&SupportedLanguages[LangIndex], X_UEFI_SCHEMA_PREFIX, AsciiStrLen (X_UEFI_SCHEMA_PREFIX)) == 0) {
        ++Count;
      }

      LangIndex = Index + 1;
    }

    if (SupportedLanguages[Index] == '\0') {
      break;
    }

    ++Index;
  }

  if (Count == 0) {
    return EFI_NOT_FOUND;
  }

  SupportedSchema->Count      = Count;
  SupportedSchema->SchemaList = AllocatePool (sizeof (CHAR8 *) * Count);
  if (SupportedSchema->SchemaList == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Index     = 0;
  LangIndex = 0;
  ListIndex = 0;
  while (TRUE) {
    if ((SupportedLanguages[Index] == ';') || (SupportedLanguages[Index] == '\0')) {
      if (AsciiStrnCmp (&SupportedLanguages[LangIndex], X_UEFI_SCHEMA_PREFIX, AsciiStrLen (X_UEFI_SCHEMA_PREFIX)) == 0) {
        StrSize                                         = Index - LangIndex;
        SupportedSchema->SchemaList[ListIndex]          = AllocateCopyPool ((StrSize + 1), &SupportedLanguages[LangIndex]);
        SupportedSchema->SchemaList[ListIndex][StrSize] = '\0';
        ++ListIndex;
      }

      LangIndex = Index + 1;
    }

    if (SupportedLanguages[Index] == '\0') {
      break;
    }

    ++Index;
  }

  return EFI_SUCCESS;
}

/**
  Search and find statement private instance by given regular expression pattern
  which describes the Configure Language.

  @param[in]  RegularExpressionProtocol   Regular express protocol.
  @param[in]  FormsetList                 Form-set list to search.
  @param[in]  Schema                      Schema to be matched.
  @param[in]  Pattern                     Regular expression pattern.
  @param[out] StatementList               Statement list that match above pattern.

  @retval EFI_SUCCESS             Statement list is returned.
  @retval EFI_INVALID_PARAMETER   Input parameter is NULL.
  @retval EFI_NOT_READY           Regular express protocol is NULL.
  @retval EFI_NOT_FOUND           No statement is found.
  @retval EFI_OUT_OF_RESOURCES    System is out of memory.

**/
EFI_STATUS
GetStatementPrivateByConfigureLangRegex (
  IN  EFI_REGULAR_EXPRESSION_PROTOCOL                 *RegularExpressionProtocol,
  IN  LIST_ENTRY                                      *FormsetList,
  IN  CHAR8                                           *Schema,
  IN  EFI_STRING                                      Pattern,
  OUT REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_LIST  *StatementList
  )
{
  LIST_ENTRY                                     *HiiFormsetLink;
  LIST_ENTRY                                     *HiiFormsetNextLink;
  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE       *HiiFormsetPrivate;
  LIST_ENTRY                                     *HiiFormLink;
  LIST_ENTRY                                     *HiiNextFormLink;
  REDFISH_PLATFORM_CONFIG_FORM_PRIVATE           *HiiFormPrivate;
  LIST_ENTRY                                     *HiiStatementLink;
  LIST_ENTRY                                     *HiiNextStatementLink;
  REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE      *HiiStatementPrivate;
  EFI_STRING                                     TmpString;
  UINTN                                          CaptureCount;
  BOOLEAN                                        IsMatch;
  EFI_STATUS                                     Status;
  REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_REF  *StatementRef;

  if ((FormsetList == NULL) || IS_EMPTY_STRING (Schema) || IS_EMPTY_STRING (Pattern) || (StatementList == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if (RegularExpressionProtocol == NULL) {
    return EFI_NOT_READY;
  }

  StatementList->Count = 0;
  InitializeListHead (&StatementList->StatementList);

  if (IsListEmpty (FormsetList)) {
    return EFI_NOT_FOUND;
  }

  HiiFormsetLink = GetFirstNode (FormsetList);
  while (!IsNull (FormsetList, HiiFormsetLink)) {
    HiiFormsetNextLink = GetNextNode (FormsetList, HiiFormsetLink);
    HiiFormsetPrivate  = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink);

    //
    // Performance check.
    // If there is no desired Redfish schema found, skip this formset.
    //
    if (!CheckSupportedSchema (&HiiFormsetPrivate->SupportedSchema, Schema)) {
      HiiFormsetLink = HiiFormsetNextLink;
      continue;
    }

    HiiFormLink = GetFirstNode (&HiiFormsetPrivate->HiiFormList);
    while (!IsNull (&HiiFormsetPrivate->HiiFormList, HiiFormLink)) {
      HiiNextFormLink = GetNextNode (&HiiFormsetPrivate->HiiFormList, HiiFormLink);
      HiiFormPrivate  = REDFISH_PLATFORM_CONFIG_FORM_FROM_LINK (HiiFormLink);

      HiiStatementLink = GetFirstNode (&HiiFormPrivate->StatementList);
      while (!IsNull (&HiiFormPrivate->StatementList, HiiStatementLink)) {
        HiiNextStatementLink = GetNextNode (&HiiFormPrivate->StatementList, HiiStatementLink);
        HiiStatementPrivate  = REDFISH_PLATFORM_CONFIG_STATEMENT_FROM_LINK (HiiStatementLink);

        if ((HiiStatementPrivate->Description != 0) &&
            (RedfishPlatformConfigFeatureProp (REDFISH_PLATFORM_CONFIG_ALLOW_SUPPRESSED) || !HiiStatementPrivate->Suppressed))
        {
          TmpString = HiiStatementPrivate->XuefiRedfishStr;
          if (TmpString != NULL) {
            Status = RegularExpressionProtocol->MatchString (
                                                  RegularExpressionProtocol,
                                                  TmpString,
                                                  Pattern,
                                                  &gEfiRegexSyntaxTypePerlGuid,
                                                  &IsMatch,
                                                  NULL,
                                                  &CaptureCount
                                                  );
            if (EFI_ERROR (Status)) {
              DEBUG ((DEBUG_ERROR, "%a: MatchString \"%s\" failed: %r\n", __func__, Pattern, Status));
              ASSERT (FALSE);
              return Status;
            }

            //
            // Found
            //
            if (IsMatch) {
              StatementRef = AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_REF));
              if (StatementRef == NULL) {
                return EFI_OUT_OF_RESOURCES;
              }

              StatementRef->Statement = HiiStatementPrivate;
              InsertTailList (&StatementList->StatementList, &StatementRef->Link);
              ++StatementList->Count;
            }
          } else {
            if (!RedfishPlatformConfigFeatureProp (REDFISH_PLATFORM_CONFIG_BUILD_MENU_PATH)) {
              DEBUG ((DEBUG_ERROR, "%a: HiiStatementPrivate->XuefiRedfishStr is NULL, x-UEFI-string has something wrong.\n", __func__));
              ASSERT (FALSE);
            }
          }
        }

        HiiStatementLink = HiiNextStatementLink;
      }

      HiiFormLink = HiiNextFormLink;
    }

    HiiFormsetLink = HiiFormsetNextLink;
  }

  return EFI_SUCCESS;
}

/**
  Get statement private instance by the given configure language.

  @param[in]  FormsetList                 Form-set list to search.
  @param[in]  Schema                      Schema to be matched.
  @param[in]  ConfigureLang               Configure language.

  @retval REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *   Pointer to statement private instance.

**/
REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE *
GetStatementPrivateByConfigureLang (
  IN  LIST_ENTRY  *FormsetList,
  IN  CHAR8       *Schema,
  IN  EFI_STRING  ConfigureLang
  )
{
  LIST_ENTRY                                 *HiiFormsetLink;
  LIST_ENTRY                                 *HiiFormsetNextLink;
  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE   *HiiFormsetPrivate;
  LIST_ENTRY                                 *HiiFormLink;
  LIST_ENTRY                                 *HiiNextFormLink;
  REDFISH_PLATFORM_CONFIG_FORM_PRIVATE       *HiiFormPrivate;
  LIST_ENTRY                                 *HiiStatementLink;
  LIST_ENTRY                                 *HiiNextStatementLink;
  REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE  *HiiStatementPrivate;
  EFI_STRING                                 TmpString;
  UINTN                                      Index;

  if ((FormsetList == NULL) || IS_EMPTY_STRING (Schema) || IS_EMPTY_STRING (ConfigureLang)) {
    return NULL;
  }

  if (IsListEmpty (FormsetList)) {
    return NULL;
  }

  Index          = 0;
  HiiFormsetLink = GetFirstNode (FormsetList);
  while (!IsNull (FormsetList, HiiFormsetLink)) {
    HiiFormsetNextLink = GetNextNode (FormsetList, HiiFormsetLink);
    HiiFormsetPrivate  = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink);

    //
    // Performance check.
    // If there is no desired Redfish schema found, skip this formset.
    //
    if (!CheckSupportedSchema (&HiiFormsetPrivate->SupportedSchema, Schema)) {
      HiiFormsetLink = HiiFormsetNextLink;
      continue;
    }

    HiiFormLink = GetFirstNode (&HiiFormsetPrivate->HiiFormList);
    while (!IsNull (&HiiFormsetPrivate->HiiFormList, HiiFormLink)) {
      HiiNextFormLink = GetNextNode (&HiiFormsetPrivate->HiiFormList, HiiFormLink);
      HiiFormPrivate  = REDFISH_PLATFORM_CONFIG_FORM_FROM_LINK (HiiFormLink);

      HiiStatementLink = GetFirstNode (&HiiFormPrivate->StatementList);
      while (!IsNull (&HiiFormPrivate->StatementList, HiiStatementLink)) {
        HiiNextStatementLink = GetNextNode (&HiiFormPrivate->StatementList, HiiStatementLink);
        HiiStatementPrivate  = REDFISH_PLATFORM_CONFIG_STATEMENT_FROM_LINK (HiiStatementLink);

        if ((HiiStatementPrivate->Description != 0) &&
            (RedfishPlatformConfigFeatureProp (REDFISH_PLATFORM_CONFIG_ALLOW_SUPPRESSED) || !HiiStatementPrivate->Suppressed))
        {
          TmpString = HiiStatementPrivate->XuefiRedfishStr;
          if (TmpString != NULL) {
            Index++;
            DEBUG_REDFISH_THIS_MODULE (
              REDFISH_PLATFORM_CONFIG_DEBUG_CONFIG_LANG_SEARCH,
              "%a: [%d] check %s in QID: 0x%x form: 0x%x formset: %g\n",
              __func__,
              Index,
              ConfigureLang,
              HiiStatementPrivate->QuestionId,
              HiiFormPrivate->Id,
              &HiiFormsetPrivate->Guid
              );
            if (HiiStrCmp (TmpString, ConfigureLang) == 0) {
              return HiiStatementPrivate;
            }
          } else {
            if (!RedfishPlatformConfigFeatureProp (REDFISH_PLATFORM_CONFIG_BUILD_MENU_PATH)) {
              DEBUG ((DEBUG_ERROR, "%a: HiiStatementPrivate->XuefiRedfishStr is NULL, x-UEFI-string has something wrong.\n", __func__));
              ASSERT (FALSE);
            }
          }
        }

        HiiStatementLink = HiiNextStatementLink;
      }

      HiiFormLink = HiiNextFormLink;
    }

    HiiFormsetLink = HiiFormsetNextLink;
  }

  return NULL;
}

/**
  Get form-set private instance by the given HII handle.

  @param[in]  HiiHandle       HII handle instance.
  @param[in]  FormsetList     Form-set list to search.

  @retval REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *   Pointer to form-set private instance.

**/
REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *
GetFormsetPrivateByHiiHandle (
  IN  EFI_HII_HANDLE  HiiHandle,
  IN  LIST_ENTRY      *FormsetList
  )
{
  LIST_ENTRY                                *HiiFormsetLink;
  LIST_ENTRY                                *HiiFormsetNextLink;
  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *HiiFormsetPrivate;

  if ((HiiHandle == NULL) || (FormsetList == NULL)) {
    return NULL;
  }

  if (IsListEmpty (FormsetList)) {
    return NULL;
  }

  HiiFormsetLink = GetFirstNode (FormsetList);
  while (!IsNull (FormsetList, HiiFormsetLink)) {
    HiiFormsetNextLink = GetNextNode (FormsetList, HiiFormsetLink);
    HiiFormsetPrivate  = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink);

    if (HiiFormsetPrivate->HiiHandle == HiiHandle) {
      return HiiFormsetPrivate;
    }

    HiiFormsetLink = HiiFormsetNextLink;
  }

  return NULL;
}

/**
  Release x-UEFI-string related information.

  @param[in]      FormsetPrivate Pointer to HII form-set private instance.

  @retval         EFI_STATUS

**/
EFI_STATUS
ReleaseXuefiStringDatabase (
  IN REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate
  )
{
  REDFISH_X_UEFI_STRING_DATABASE  *ThisDatabase;
  REDFISH_X_UEFI_STRING_DATABASE  *PreDatabase;
  REDFISH_X_UEFI_STRINGS_ARRAY    *ThisStringArray;
  REDFISH_X_UEFI_STRINGS_ARRAY    *PreStringArray;
  BOOLEAN                         EndDatabase;
  BOOLEAN                         EndArray;

  if (FormsetPrivate->HiiPackageListHeader != NULL) {
    FreePool (FormsetPrivate->HiiPackageListHeader);
  }

  // Walk through x-UEFI-redfish string database.
  if (!IsListEmpty (&FormsetPrivate->XuefiRedfishStringDatabase)) {
    EndDatabase  = FALSE;
    ThisDatabase = (REDFISH_X_UEFI_STRING_DATABASE *)GetFirstNode (&FormsetPrivate->XuefiRedfishStringDatabase);
    while (!EndDatabase) {
      // Walk through string arrays.
      if (!IsListEmpty (&ThisDatabase->XuefiRedfishStringArrays)) {
        EndArray        = FALSE;
        ThisStringArray = (REDFISH_X_UEFI_STRINGS_ARRAY *)GetFirstNode (&ThisDatabase->XuefiRedfishStringArrays);
        while (!EndArray) {
          // Remove this array
          FreePool (ThisStringArray->ArrayEntryAddress);
          EndArray       = IsNodeAtEnd (&ThisDatabase->XuefiRedfishStringArrays, &ThisStringArray->NextArray);
          PreStringArray = ThisStringArray;
          if (!EndArray) {
            ThisStringArray = (REDFISH_X_UEFI_STRINGS_ARRAY *)GetNextNode (&ThisDatabase->XuefiRedfishStringArrays, &ThisStringArray->NextArray);
          }

          RemoveEntryList (&PreStringArray->NextArray);
          FreePool (PreStringArray);
        }
      }

      //
      // Remove this database
      //
      EndDatabase = IsNodeAtEnd (&FormsetPrivate->XuefiRedfishStringDatabase, &ThisDatabase->NextXuefiRedfishLanguage);
      PreDatabase = ThisDatabase;
      if (!EndDatabase) {
        ThisDatabase = (REDFISH_X_UEFI_STRING_DATABASE *)GetNextNode (&FormsetPrivate->XuefiRedfishStringDatabase, &ThisDatabase->NextXuefiRedfishLanguage);
      }

      RemoveEntryList (&PreDatabase->NextXuefiRedfishLanguage);
      FreePool (PreDatabase);
    }
  }

  return EFI_SUCCESS;
}

/**
  Release formset and all the forms and statements that belong to this formset.

  @param[in]      FormsetPrivate Pointer to HII form-set private instance.

  @retval         EFI_STATUS

**/
EFI_STATUS
ReleaseFormset (
  IN REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate
  )
{
  LIST_ENTRY                                 *HiiFormLink;
  LIST_ENTRY                                 *HiiNextFormLink;
  REDFISH_PLATFORM_CONFIG_FORM_PRIVATE       *HiiFormPrivate;
  LIST_ENTRY                                 *HiiStatementLink;
  LIST_ENTRY                                 *HiiNextStatementLink;
  REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE  *HiiStatementPrivate;
  UINTN                                      Index;

  if (FormsetPrivate == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  HiiFormLink = GetFirstNode (&FormsetPrivate->HiiFormList);
  while (!IsNull (&FormsetPrivate->HiiFormList, HiiFormLink)) {
    HiiFormPrivate  = REDFISH_PLATFORM_CONFIG_FORM_FROM_LINK (HiiFormLink);
    HiiNextFormLink = GetNextNode (&FormsetPrivate->HiiFormList, HiiFormLink);

    HiiStatementLink = GetFirstNode (&HiiFormPrivate->StatementList);
    while (!IsNull (&HiiFormPrivate->StatementList, HiiStatementLink)) {
      HiiStatementPrivate  = REDFISH_PLATFORM_CONFIG_STATEMENT_FROM_LINK (HiiStatementLink);
      HiiNextStatementLink = GetNextNode (&HiiFormPrivate->StatementList, HiiStatementLink);

      //
      // HiiStatementPrivate->HiiStatement will be released in DestroyFormSet().
      //
      RemoveEntryList (&HiiStatementPrivate->Link);
      FreePool (HiiStatementPrivate);
      HiiStatementLink = HiiNextStatementLink;
    }

    //
    // HiiStatementPrivate->HiiForm will be released in DestroyFormSet().
    //

    RemoveEntryList (&HiiFormPrivate->Link);
    FreePool (HiiFormPrivate);
    HiiFormLink = HiiNextFormLink;
  }

  if (FormsetPrivate->HiiFormSet != NULL) {
    DestroyFormSet (FormsetPrivate->HiiFormSet);
    FormsetPrivate->HiiFormSet = NULL;
  }

  if (FormsetPrivate->DevicePathStr != NULL) {
    FreePool (FormsetPrivate->DevicePathStr);
  }

  //
  // Release schema list
  //
  if (FormsetPrivate->SupportedSchema.SchemaList != NULL) {
    for (Index = 0; Index < FormsetPrivate->SupportedSchema.Count; Index++) {
      FreePool (FormsetPrivate->SupportedSchema.SchemaList[Index]);
    }

    FreePool (FormsetPrivate->SupportedSchema.SchemaList);
    FormsetPrivate->SupportedSchema.SchemaList = NULL;
    FormsetPrivate->SupportedSchema.Count      = 0;
  }

  ReleaseXuefiStringDatabase (FormsetPrivate);

  return EFI_SUCCESS;
}

/**
  Create new form-set instance.

  @retval REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE *   Pointer to newly created form-set private instance.

**/
REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *
NewFormsetPrivate (
  VOID
  )
{
  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *NewFormsetPrivate;

  NewFormsetPrivate = AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE));
  if (NewFormsetPrivate == NULL) {
    return NULL;
  }

  //
  // Initial newly created formset private data.
  //
  InitializeListHead (&NewFormsetPrivate->HiiFormList);
  InitializeListHead (&NewFormsetPrivate->XuefiRedfishStringDatabase);

  return NewFormsetPrivate;
}

/**
  Create new x-UEFI-redfish string array.

  @param[in]      XuefiRedfishStringDatabase  The x-UEFI-redfish string database.

  @retval         EFI_OUT_OF_RESOURCES  Not enough memory for creating a new array.
                  EFI_SUCCESS           New array is created successfully.

**/
EFI_STATUS
NewRedfishXuefiStringArray (
  IN  REDFISH_X_UEFI_STRING_DATABASE  *XuefiRedfishStringDatabase
  )
{
  REDFISH_X_UEFI_STRINGS_ARRAY  *ArrayAddress;

  // Initial first REDFISH_X_UEFI_STRINGS_ARRAY memory.
  ArrayAddress = (REDFISH_X_UEFI_STRINGS_ARRAY *)AllocateZeroPool (sizeof (REDFISH_X_UEFI_STRINGS_ARRAY));
  if (ArrayAddress == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: Failed to allocate REDFISH_X_UEFI_STRINGS_ARRAY.\n", __func__));
    return EFI_OUT_OF_RESOURCES;
  }

  InitializeListHead (&ArrayAddress->NextArray);

  // Allocate memory buffer for REDFISH_X_UEFI_STRINGS_ARRAY_ELEMENT elements.
  ArrayAddress->ArrayEntryAddress = \
    (REDFISH_X_UEFI_STRINGS_ARRAY_ELEMENT *)AllocateZeroPool (sizeof (REDFISH_X_UEFI_STRINGS_ARRAY_ELEMENT) * X_UEFI_REDFISH_STRING_ARRAY_ENTRY_NUMBER);
  if (ArrayAddress->ArrayEntryAddress == NULL) {
    FreePool (ArrayAddress);
    DEBUG ((DEBUG_ERROR, "%a: Failed to allocate array for REDFISH_X_UEFI_STRINGS_ARRAY_ELEMENTs.\n", __func__));
    return EFI_OUT_OF_RESOURCES;
  }

  XuefiRedfishStringDatabase->StringsArrayBlocks++;
  InsertTailList (&XuefiRedfishStringDatabase->XuefiRedfishStringArrays, &ArrayAddress->NextArray);
  return EFI_SUCCESS;
}

/**
  Get the pointer of x-UEFI-redfish database or create a new database.

  @param[in]      FormsetPrivate          Pointer to HII form-set private instance.
  @param[in]      HiiStringPackageHeader  HII string package header.

  @retval         Pointer to REDFISH_X_UEFI_STRING_DATABASE.
                  If NULL, it fails to obtain x-UEFI-redfish database.

**/
REDFISH_X_UEFI_STRING_DATABASE *
GetExistOrCreateXuefiStringDatabase (
  IN  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate,
  IN  EFI_HII_STRING_PACKAGE_HDR                *HiiStringPackageHeader
  )
{
  EFI_STATUS                      Status;
  BOOLEAN                         CreateNewOne;
  REDFISH_X_UEFI_STRING_DATABASE  *XuefiRedfishStringDatabase;

  CreateNewOne               = TRUE;
  XuefiRedfishStringDatabase = NULL;
  if (!IsListEmpty (&FormsetPrivate->XuefiRedfishStringDatabase)) {
    XuefiRedfishStringDatabase = (REDFISH_X_UEFI_STRING_DATABASE *)GetFirstNode (&FormsetPrivate->XuefiRedfishStringDatabase);

    while (TRUE) {
      if (AsciiStriCmp (XuefiRedfishStringDatabase->XuefiRedfishLanguage, HiiStringPackageHeader->Language) == 0) {
        CreateNewOne = FALSE;
        break;
      }

      if (IsNodeAtEnd (&FormsetPrivate->XuefiRedfishStringDatabase, &XuefiRedfishStringDatabase->NextXuefiRedfishLanguage)) {
        break;
      }

      XuefiRedfishStringDatabase = \
        (REDFISH_X_UEFI_STRING_DATABASE *)GetNextNode (&FormsetPrivate->XuefiRedfishStringDatabase, &XuefiRedfishStringDatabase->NextXuefiRedfishLanguage);
    }
  }

  if (CreateNewOne) {
    DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "  Creating x-UEFI-redfish (%a) string database...\n", HiiStringPackageHeader->Language));
    XuefiRedfishStringDatabase = (REDFISH_X_UEFI_STRING_DATABASE *)AllocateZeroPool (sizeof (REDFISH_X_UEFI_STRING_DATABASE));
    if (XuefiRedfishStringDatabase == NULL) {
      DEBUG ((DEBUG_ERROR, "  Failed to allocate REDFISH_X_UEFI_STRING_DATABASE.\n"));
      return NULL;
    }

    InitializeListHead (&XuefiRedfishStringDatabase->NextXuefiRedfishLanguage);
    InitializeListHead (&XuefiRedfishStringDatabase->XuefiRedfishStringArrays);
    XuefiRedfishStringDatabase->StringsArrayBlocks   = 0;
    XuefiRedfishStringDatabase->XuefiRedfishLanguage = HiiStringPackageHeader->Language;

    Status = NewRedfishXuefiStringArray (XuefiRedfishStringDatabase);
    if (EFI_ERROR (Status)) {
      FreePool (XuefiRedfishStringDatabase);
      return NULL;
    }

    DEBUG ((
      DEBUG_REDFISH_PLATFORM_CONFIG,
      "  x-UEFI-redfish (%a):\n    String array is added to XuefiRedfishStringDatabase, total %d arrays now.\n",
      XuefiRedfishStringDatabase->XuefiRedfishLanguage,
      XuefiRedfishStringDatabase->StringsArrayBlocks
      ));

    // Link string database to FormsetPrivate.
    InsertTailList (&FormsetPrivate->XuefiRedfishStringDatabase, &XuefiRedfishStringDatabase->NextXuefiRedfishLanguage);
  }

  return XuefiRedfishStringDatabase;
}

/**
  Check and allocate a new x-UEFI-redfish array if it is insufficient for the
  newly added x-UEFI-redfish string.

  @param[in]      FormsetPrivate              Pointer to HII form-set private instance.
  @param[in]      XuefiRedfishStringDatabase  Pointer to the x-UEFI-redfish database.
  @param[in]      StringId                    String ID added to database.

  @retval         EFI_SUCCESS                 The size of x-UEFI-string array is adjusted or
                                              is not required to be adjusted.
                  Otherwise, refer to the error code returned from NewRedfishXuefiStringArray().

**/
EFI_STATUS
RedfishXuefiStringAdjustArrays (
  IN  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate,
  IN  REDFISH_X_UEFI_STRING_DATABASE            *XuefiRedfishStringDatabase,
  IN  EFI_STRING_ID                             StringId
  )
{
  EFI_STATUS  Status;

  while (((StringId + X_UEFI_REDFISH_STRING_ARRAY_ENTRY_NUMBER) / X_UEFI_REDFISH_STRING_ARRAY_ENTRY_NUMBER) > (UINT16)XuefiRedfishStringDatabase->StringsArrayBlocks) {
    Status = NewRedfishXuefiStringArray (XuefiRedfishStringDatabase);
    if (EFI_ERROR (Status)) {
      DEBUG ((DEBUG_ERROR, "%a: Failed to adjust x-UEFI-string array", __func__));
      return Status;
    }
  }

  return EFI_SUCCESS;
}

/**
  Insert a x-UEFI-redfish string to database.

  @param[in]      FormsetPrivate          Pointer to HII form-set private instance.
  @param[in]      HiiStringPackageHeader  Pointer to HII string package.
  @param[in]      StringId                The HII string ID
  @param[in]      StringTextPtr           Pointer to HII string text.

  @retval         EFI_SUCCESS             The HII string is added to database.
                  EFI_LOAD_ERROR          Something wrong when insert an HII string
                                          to database.

**/
EFI_STATUS
RedfishXuefiStringInsertDatabase (
  IN  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate,
  IN  EFI_HII_STRING_PACKAGE_HDR                *HiiStringPackageHeader,
  IN  EFI_STRING_ID                             StringId,
  IN  CHAR16                                    *StringTextPtr
  )
{
  EFI_STATUS                      Status;
  UINTN                           StringIdOffset;
  REDFISH_X_UEFI_STRING_DATABASE  *XuefiRedfishStringDatabase;
  REDFISH_X_UEFI_STRINGS_ARRAY    *ThisArray;

  XuefiRedfishStringDatabase = GetExistOrCreateXuefiStringDatabase (FormsetPrivate, HiiStringPackageHeader);
  if (XuefiRedfishStringDatabase == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: Failed to get REDFISH_X_UEFI_STRING_DATABASE of x-UEFI-redfish language %a.\n", __func__, HiiStringPackageHeader->Language));
    ReleaseXuefiStringDatabase (FormsetPrivate);
    return EFI_LOAD_ERROR;
  }

  Status = RedfishXuefiStringAdjustArrays (FormsetPrivate, XuefiRedfishStringDatabase, StringId);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "%a: Failed to adjust x-UEFI-redfish string array.\n", __func__));
    ReleaseXuefiStringDatabase (FormsetPrivate);
    return EFI_LOAD_ERROR;
  }

  // Insert string to x-UEFI-redfish string array.
  StringIdOffset = (UINTN)StringId;
  ThisArray      = (REDFISH_X_UEFI_STRINGS_ARRAY *)GetFirstNode (&XuefiRedfishStringDatabase->XuefiRedfishStringArrays);
  while (StringIdOffset >= X_UEFI_REDFISH_STRING_ARRAY_ENTRY_NUMBER) {
    ThisArray       = (REDFISH_X_UEFI_STRINGS_ARRAY *)GetNextNode (&XuefiRedfishStringDatabase->XuefiRedfishStringArrays, &ThisArray->NextArray);
    StringIdOffset -= X_UEFI_REDFISH_STRING_ARRAY_ENTRY_NUMBER;
  }

  // Insert string
  (ThisArray->ArrayEntryAddress + StringIdOffset)->StringId  = StringId;
  (ThisArray->ArrayEntryAddress + StringIdOffset)->UcsString = StringTextPtr;

  DEBUG_REDFISH_THIS_MODULE (
    REDFISH_PLATFORM_CONFIG_DEBUG_STRING_DATABASE,
    "  Insert string ID: (%d) to database\n    x-UEFI-string: \"%s\"\n    Language: %a.\n",
    StringId,
    StringTextPtr,
    HiiStringPackageHeader->Language
    );
  return EFI_SUCCESS;
}

/**
  Get x-UEFI-redfish string and language by string ID.

  @param[in]       FormsetPrivate          Pointer to HII form-set private instance.
  @param[in]       HiiStringPackageHeader  HII string package header.
  @param[out]      TotalStringAdded        Return the total strings added to database.

  @retval  TRUE   x-UEFI-redfish string and ID map is inserted to database.
           FALSE  Something is wrong when insert x-UEFI-redfish string and ID map.

**/
BOOLEAN
CreateXuefiLanguageStringIdMap (
  IN   REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate,
  IN   EFI_HII_STRING_PACKAGE_HDR                *HiiStringPackageHeader,
  OUT  UINTN                                     *TotalStringAdded
  )
{
  EFI_STATUS               Status;
  UINT8                    *BlockHdr;
  EFI_STRING_ID            CurrentStringId;
  UINTN                    BlockSize;
  UINTN                    Index;
  UINT8                    *StringTextPtr;
  UINTN                    Offset;
  UINT16                   StringCount;
  UINT16                   SkipCount;
  UINT8                    Length8;
  EFI_HII_SIBT_EXT2_BLOCK  Ext2;
  UINT32                   Length32;
  UINT8                    *StringBlockInfo;
  UINTN                    StringsAdded;

  StringsAdded = 0;

  //
  // Parse the string blocks to get the string text and font.
  //
  StringBlockInfo = (UINT8 *)((UINTN)HiiStringPackageHeader + HiiStringPackageHeader->StringInfoOffset);
  BlockHdr        = StringBlockInfo;
  BlockSize       = 0;
  Offset          = 0;
  CurrentStringId = 1;
  while (*BlockHdr != EFI_HII_SIBT_END) {
    switch (*BlockHdr) {
      case EFI_HII_SIBT_STRING_SCSU:
        Offset        = sizeof (EFI_HII_STRING_BLOCK);
        StringTextPtr = BlockHdr + Offset;
        BlockSize    += Offset + AsciiStrSize ((CHAR8 *)StringTextPtr);
        CurrentStringId++;
        break;

      case EFI_HII_SIBT_STRING_SCSU_FONT:
        Offset        = sizeof (EFI_HII_SIBT_STRING_SCSU_FONT_BLOCK) - sizeof (UINT8);
        StringTextPtr = BlockHdr + Offset;
        BlockSize    += Offset + AsciiStrSize ((CHAR8 *)StringTextPtr);
        CurrentStringId++;
        break;

      case EFI_HII_SIBT_STRINGS_SCSU:
        CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16));
        StringTextPtr = (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_SIBT_STRINGS_SCSU_BLOCK) - sizeof (UINT8));
        BlockSize    += StringTextPtr - BlockHdr;

        for (Index = 0; Index < StringCount; Index++) {
          BlockSize    += AsciiStrSize ((CHAR8 *)StringTextPtr);
          StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *)StringTextPtr);
          CurrentStringId++;
        }

        break;

      case EFI_HII_SIBT_STRINGS_SCSU_FONT:
        CopyMem (
          &StringCount,
          (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)),
          sizeof (UINT16)
          );
        StringTextPtr = (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_SIBT_STRINGS_SCSU_FONT_BLOCK) - sizeof (UINT8));
        BlockSize    += StringTextPtr - BlockHdr;

        for (Index = 0; Index < StringCount; Index++) {
          BlockSize    += AsciiStrSize ((CHAR8 *)StringTextPtr);
          StringTextPtr = StringTextPtr + AsciiStrSize ((CHAR8 *)StringTextPtr);
          CurrentStringId++;
        }

        break;

      case EFI_HII_SIBT_STRING_UCS2:
        Offset        = sizeof (EFI_HII_STRING_BLOCK);
        StringTextPtr = BlockHdr + Offset;

        // x-UEFI-redfish string is always encoded as UCS and started with '/'.
        if (*StringTextPtr == (UINT16)'/') {
          Status = RedfishXuefiStringInsertDatabase (
                     FormsetPrivate,
                     HiiStringPackageHeader,
                     CurrentStringId,
                     (CHAR16 *)StringTextPtr
                     );
          if (EFI_ERROR (Status)) {
            DEBUG ((DEBUG_ERROR, "%a: Failed to insert x-UEFI-redfish string %s.\n", __func__, StringTextPtr));
            return FALSE;
          }

          StringsAdded++;
        }

        BlockSize += (Offset + HiiStrSize ((CHAR16 *)StringTextPtr));
        CurrentStringId++;
        break;

      case EFI_HII_SIBT_STRING_UCS2_FONT:
        Offset        = sizeof (EFI_HII_SIBT_STRING_UCS2_FONT_BLOCK)  - sizeof (CHAR16);
        StringTextPtr = BlockHdr + Offset;
        BlockSize    += (Offset + HiiStrSize ((CHAR16 *)StringTextPtr));
        CurrentStringId++;
        break;

      case EFI_HII_SIBT_STRINGS_UCS2:
        Offset        = sizeof (EFI_HII_SIBT_STRINGS_UCS2_BLOCK) - sizeof (CHAR16);
        StringTextPtr = BlockHdr + Offset;
        BlockSize    += Offset;
        CopyMem (&StringCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16));
        for (Index = 0; Index < StringCount; Index++) {
          BlockSize    += HiiStrSize ((CHAR16 *)StringTextPtr);
          StringTextPtr = StringTextPtr + HiiStrSize ((CHAR16 *)StringTextPtr);
          CurrentStringId++;
        }

        break;

      case EFI_HII_SIBT_STRINGS_UCS2_FONT:
        Offset        = sizeof (EFI_HII_SIBT_STRINGS_UCS2_FONT_BLOCK) - sizeof (CHAR16);
        StringTextPtr = BlockHdr + Offset;
        BlockSize    += Offset;
        CopyMem (
          &StringCount,
          (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)),
          sizeof (UINT16)
          );
        for (Index = 0; Index < StringCount; Index++) {
          BlockSize    += HiiStrSize ((CHAR16 *)StringTextPtr);
          StringTextPtr = StringTextPtr + HiiStrSize ((CHAR16 *)StringTextPtr);
          CurrentStringId++;
        }

        break;

      case EFI_HII_SIBT_DUPLICATE:
        BlockSize += sizeof (EFI_HII_SIBT_DUPLICATE_BLOCK);
        CurrentStringId++;
        break;

      case EFI_HII_SIBT_SKIP1:
        SkipCount       = (UINT16)(*(UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK)));
        CurrentStringId = (UINT16)(CurrentStringId + SkipCount);
        BlockSize      +=  sizeof (EFI_HII_SIBT_SKIP1_BLOCK);
        break;

      case EFI_HII_SIBT_SKIP2:
        CopyMem (&SkipCount, BlockHdr + sizeof (EFI_HII_STRING_BLOCK), sizeof (UINT16));
        CurrentStringId = (UINT16)(CurrentStringId + SkipCount);
        BlockSize      +=  sizeof (EFI_HII_SIBT_SKIP2_BLOCK);
        break;

      case EFI_HII_SIBT_EXT1:
        CopyMem (
          &Length8,
          (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)),
          sizeof (UINT8)
          );
        BlockSize += Length8;
        break;

      case EFI_HII_SIBT_EXT2:
        CopyMem (&Ext2, BlockHdr, sizeof (EFI_HII_SIBT_EXT2_BLOCK));
        BlockSize += Ext2.Length;
        break;

      case EFI_HII_SIBT_EXT4:
        CopyMem (
          &Length32,
          (UINT8 *)((UINTN)BlockHdr + sizeof (EFI_HII_STRING_BLOCK) + sizeof (UINT8)),
          sizeof (UINT32)
          );

        BlockSize += Length32;
        break;

      default:
        break;
    }

    BlockHdr = (UINT8 *)(StringBlockInfo + BlockSize);
  }

  *TotalStringAdded = StringsAdded;
  return TRUE;
}

/**
  Get x-UEFI-redfish string and language by string ID.

  @param[in]      FormsetPrivate       Pointer to HII form-set private instance.
  @param[in]      StringId             The HII string ID.
  @param[out]     String               Optionally return USC string.
  @param[out]     Language             Optionally return x-UEFI-redfish language.
  @param[out]     XuefiStringDatabase  Optionally return x-UEFI-redfish database.

  @retval  EFI_SUCCESS            String information is returned.
           EFI_INVALID_PARAMETER  One of the given parameters to this function is
                                  invalid.
           EFI_NOT_FOUND          String is not found.

**/
EFI_STATUS
GetXuefiStringAndLangByStringId (
  IN   REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate,
  IN   EFI_STRING_ID                             StringId,
  OUT  CHAR16                                    **String OPTIONAL,
  OUT  CHAR8                                     **Language OPTIONAL,
  OUT  REDFISH_X_UEFI_STRING_DATABASE            **XuefiStringDatabase OPTIONAL
  )
{
  REDFISH_X_UEFI_STRING_DATABASE  *XuefiRedfishStringDatabase;
  REDFISH_X_UEFI_STRINGS_ARRAY    *StringArray;
  UINT16                          StringIndex;

  if ((String == NULL) && (Language == NULL) && (XuefiStringDatabase == NULL)) {
    DEBUG ((DEBUG_ERROR, "%a: Invalid parameters for this function.\n", __func__));
    return EFI_INVALID_PARAMETER;
  }

  if (IsListEmpty (&FormsetPrivate->XuefiRedfishStringDatabase)) {
    return EFI_NOT_FOUND;
  }

  XuefiRedfishStringDatabase = (REDFISH_X_UEFI_STRING_DATABASE *)GetFirstNode (&FormsetPrivate->XuefiRedfishStringDatabase);
  while (TRUE) {
    if (Language != NULL) {
      *Language = XuefiRedfishStringDatabase->XuefiRedfishLanguage;
    }

    StringArray = (REDFISH_X_UEFI_STRINGS_ARRAY *)GetFirstNode (&XuefiRedfishStringDatabase->XuefiRedfishStringArrays);

    // Loop to the correct string array.
    StringIndex = StringId;
    while (StringIndex >= X_UEFI_REDFISH_STRING_ARRAY_ENTRY_NUMBER) {
      if (IsNodeAtEnd (&XuefiRedfishStringDatabase->XuefiRedfishStringArrays, &StringArray->NextArray)) {
        goto ErrorExit;
      }

      StringArray  = (REDFISH_X_UEFI_STRINGS_ARRAY *)GetNextNode (&XuefiRedfishStringDatabase->XuefiRedfishStringArrays, &StringArray->NextArray);
      StringIndex -= X_UEFI_REDFISH_STRING_ARRAY_ENTRY_NUMBER;
    }

    //
    // NOTE: The string ID in the formset is a unique number.
    //       If the string in the array is NULL, then the matched string ID
    //       should be in another x-UEFI-redfish database.
    //
    if ((StringArray->ArrayEntryAddress + StringIndex)->UcsString != NULL) {
      //
      // String ID is belong to this x-uef-redfish language database.
      //
      if (String != NULL) {
        *String = (StringArray->ArrayEntryAddress + StringIndex)->UcsString;
      }

      if (XuefiStringDatabase != NULL) {
        *XuefiStringDatabase = XuefiRedfishStringDatabase;
      }

      return EFI_SUCCESS;
    }

    if (IsNodeAtEnd (&FormsetPrivate->XuefiRedfishStringDatabase, &XuefiRedfishStringDatabase->NextXuefiRedfishLanguage)) {
      return EFI_NOT_FOUND;
    }

    XuefiRedfishStringDatabase = (REDFISH_X_UEFI_STRING_DATABASE *)GetNextNode (
                                                                     &FormsetPrivate->XuefiRedfishStringDatabase,
                                                                     &XuefiRedfishStringDatabase->NextXuefiRedfishLanguage
                                                                     );
  }

ErrorExit:;
  DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%a: String ID (%d) is not in any x-uef-redfish string databases.\n", __func__, StringId));
  return EFI_NOT_FOUND;
}

/**
  Build a x-UEFI-redfish database for the newly added x-UEFI-redfish language.

  @param[in]      FormsetPrivate          Pointer to HII form-set private instance.

**/
VOID
BuildXUefiRedfishStringDatabase (
  IN  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate
  )
{
  EFI_STATUS                  Status;
  UINTN                       BufferSize;
  EFI_HII_PACKAGE_HEADER      *PackageHeader;
  UINTN                       EndingPackageAddress;
  EFI_HII_STRING_PACKAGE_HDR  *HiiStringPackageHeader;
  UINTN                       SupportedSchemaLangCount;
  CHAR8                       **SupportedSchemaLang;
  BOOLEAN                     StringIdMapIsBuilt;
  UINTN                       TotalStringsAdded;
  UINTN                       NumberPackageStrings;

  DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%a: Building x-UEFI-redfish string database, HII Formset GUID - %g.\n", __func__, FormsetPrivate->Guid));

  BufferSize = 0;
  Status     = mRedfishPlatformConfigPrivate->HiiDatabase->ExportPackageLists (
                                                             mRedfishPlatformConfigPrivate->HiiDatabase,
                                                             FormsetPrivate->HiiHandle,
                                                             &BufferSize,
                                                             FormsetPrivate->HiiPackageListHeader
                                                             );
  if (Status != EFI_BUFFER_TOO_SMALL) {
    DEBUG ((DEBUG_ERROR, "  Failed to export package list.\n"));
    return;
  }

  FormsetPrivate->HiiPackageListHeader = (EFI_HII_PACKAGE_LIST_HEADER *)AllocateZeroPool (BufferSize);
  if (FormsetPrivate->HiiPackageListHeader == NULL) {
    DEBUG ((DEBUG_ERROR, "  Failed to allocate memory for the exported package list.\n"));
    return;
  }

  Status = mRedfishPlatformConfigPrivate->HiiDatabase->ExportPackageLists (
                                                         mRedfishPlatformConfigPrivate->HiiDatabase,
                                                         FormsetPrivate->HiiHandle,
                                                         &BufferSize,
                                                         FormsetPrivate->HiiPackageListHeader
                                                         );
  if (EFI_ERROR (Status)) {
    FreePool (FormsetPrivate->HiiPackageListHeader);
    FormsetPrivate->HiiPackageListHeader = NULL;
    return;
  }

  TotalStringsAdded = 0;
  //
  // Finding the string package.
  //
  EndingPackageAddress = (UINTN)FormsetPrivate->HiiPackageListHeader + FormsetPrivate->HiiPackageListHeader->PackageLength;
  PackageHeader        = (EFI_HII_PACKAGE_HEADER *)(FormsetPrivate->HiiPackageListHeader + 1);
  SupportedSchemaLang  = FormsetPrivate->SupportedSchema.SchemaList;
  while ((UINTN)PackageHeader < EndingPackageAddress) {
    switch (PackageHeader->Type) {
      case EFI_HII_PACKAGE_STRINGS:
        StringIdMapIsBuilt     = FALSE;
        HiiStringPackageHeader = (EFI_HII_STRING_PACKAGE_HDR *)PackageHeader;

        // Check if this is the string package for x-UEFI-redfish
        for (SupportedSchemaLangCount = 0;
             SupportedSchemaLangCount < FormsetPrivate->SupportedSchema.Count;
             SupportedSchemaLangCount++
             )
        {
          if (AsciiStrnCmp (
                *(SupportedSchemaLang + SupportedSchemaLangCount),
                HiiStringPackageHeader->Language,
                AsciiStrLen (HiiStringPackageHeader->Language)
                ) == 0)
          {
            StringIdMapIsBuilt = CreateXuefiLanguageStringIdMap (FormsetPrivate, HiiStringPackageHeader, &NumberPackageStrings);
            if (StringIdMapIsBuilt) {
              TotalStringsAdded += NumberPackageStrings;
            }

            break;
          }
        }

        if (StringIdMapIsBuilt == FALSE) {
          if (AsciiStrStr (HiiStringPackageHeader->Language, X_UEFI_SCHEMA_PREFIX) == NULL) {
            DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "  No need to build x-UEFI-redfish string ID map for HII language %a\n", HiiStringPackageHeader->Language));
          } else {
            DEBUG ((DEBUG_ERROR, "  Failed to build x-UEFI-redfish string ID map of HII language %a\n", HiiStringPackageHeader->Language));
          }
        }

      default:
        PackageHeader = (EFI_HII_PACKAGE_HEADER *)((UINTN)PackageHeader + PackageHeader->Length);
    }
  }

  DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "  Total %d x-UEFI-redfish config language are added.\n", TotalStringsAdded));
}

/**
  Load the HII formset from the given HII handle.

  @param[in]  HiiHandle       Target HII handle to load.
  @param[out] FormsetPrivate  The formset private data.

  @retval EFI_STATUS          The formset is loaded successfully.
  @retval EFI_UNSUPPORTED     This formset doesn't have any x-UEFI-redfish configuration.

**/
EFI_STATUS
LoadFormset (
  IN  EFI_HII_HANDLE                            HiiHandle,
  OUT REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate
  )
{
  EFI_STATUS                                 Status;
  HII_FORMSET                                *HiiFormSet;
  HII_FORM                                   *HiiForm;
  LIST_ENTRY                                 *HiiFormLink;
  REDFISH_PLATFORM_CONFIG_FORM_PRIVATE       *HiiFormPrivate;
  HII_STATEMENT                              *HiiStatement;
  LIST_ENTRY                                 *HiiStatementLink;
  REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE  *HiiStatementPrivate;
  EFI_GUID                                   ZeroGuid;
  EXPRESS_RESULT                             ExpressionResult;
  CHAR16                                     *String;

  if ((HiiHandle == NULL) || (FormsetPrivate == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  HiiFormSet = AllocateZeroPool (sizeof (HII_FORMSET));
  if (HiiFormSet == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: No memory resource for HII_FORMSET - %g\n", __func__, FormsetPrivate->Guid));
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Find HII formset by the given HII handle.
  //
  ZeroMem (&ZeroGuid, sizeof (ZeroGuid));
  Status = CreateFormSetFromHiiHandle (HiiHandle, &ZeroGuid, HiiFormSet);
  if (EFI_ERROR (Status) || IsListEmpty (&HiiFormSet->FormListHead)) {
    DEBUG ((DEBUG_ERROR, "%a: Formset not found by HII handle - %g\n", __func__, FormsetPrivate->Guid));
    Status = EFI_NOT_FOUND;
    goto ErrorExit;
  }

  //
  // Initialize formset
  //
  InitializeFormSet (HiiFormSet);

  //
  // Initialize formset private data.
  //
  FormsetPrivate->HiiFormSet = HiiFormSet;
  FormsetPrivate->HiiHandle  = HiiHandle;
  CopyGuid (&FormsetPrivate->Guid, &HiiFormSet->Guid);
  FormsetPrivate->DevicePathStr = ConvertDevicePathToText (HiiFormSet->DevicePath, FALSE, FALSE);
  Status                        = GetSupportedSchema (FormsetPrivate->HiiHandle, &FormsetPrivate->SupportedSchema);
  if (EFI_ERROR (Status)) {
    if (!RedfishPlatformConfigFeatureProp (REDFISH_PLATFORM_CONFIG_BUILD_MENU_PATH)) {
      DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%a: No x-UEFI-redfish configuration found on the formset - %g\n", __func__, FormsetPrivate->Guid));
      //
      // If there is no x-UEFI-redfish language in this form-set, we don't add formset
      // since we don't need to build menu path for attribute registry.
      //
      return EFI_UNSUPPORTED;
    }
  } else {
    // Building x-UEFI-redfish string database
    BuildXUefiRedfishStringDatabase (FormsetPrivate);
  }

  HiiFormLink = GetFirstNode (&HiiFormSet->FormListHead);
  while (!IsNull (&HiiFormSet->FormListHead, HiiFormLink)) {
    HiiForm = HII_FORM_FROM_LINK (HiiFormLink);

    HiiFormPrivate = AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_FORM_PRIVATE));
    if (HiiFormPrivate == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      DEBUG ((DEBUG_ERROR, "%a: No memory resource for REDFISH_PLATFORM_CONFIG_FORM_PRIVATE.\n", __func__));
      goto ErrorExit;
    }

    //
    // Initialize form private data.
    //
    HiiFormPrivate->HiiForm       = HiiForm;
    HiiFormPrivate->Id            = HiiForm->FormId;
    HiiFormPrivate->Title         = HiiForm->FormTitle;
    HiiFormPrivate->ParentFormset = FormsetPrivate;
    HiiFormPrivate->Suppressed    = FALSE;
    InitializeListHead (&HiiFormPrivate->StatementList);

    if ((HiiForm->SuppressExpression != NULL) &&
        (EvaluateExpressionList (HiiForm->SuppressExpression, TRUE, HiiFormSet, HiiForm) == ExpressSuppress))
    {
      HiiFormPrivate->Suppressed = TRUE;
    }

    HiiStatementLink = GetFirstNode (&HiiForm->StatementListHead);
    while (!IsNull (&HiiForm->StatementListHead, HiiStatementLink)) {
      HiiStatement = HII_STATEMENT_FROM_LINK (HiiStatementLink);

      HiiStatementPrivate = AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE));
      if (HiiStatementPrivate == NULL) {
        DEBUG ((DEBUG_ERROR, "%a: No memory resource for REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE.\n", __func__));
        Status = EFI_OUT_OF_RESOURCES;
        goto ErrorExit;
      }

      //
      // Initialize statement private data.
      //
      HiiStatementPrivate->HiiStatement             = HiiStatement;
      HiiStatementPrivate->QuestionId               = HiiStatement->QuestionId;
      HiiStatementPrivate->Description              = HiiStatement->Prompt;
      HiiStatementPrivate->Help                     = HiiStatement->Help;
      HiiStatementPrivate->ParentForm               = HiiFormPrivate;
      HiiStatementPrivate->Flags                    = HiiStatement->QuestionFlags;
      HiiStatementPrivate->StatementData.NumMaximum = HiiStatement->ExtraData.NumData.Maximum;
      HiiStatementPrivate->StatementData.NumMinimum = HiiStatement->ExtraData.NumData.Minimum;
      HiiStatementPrivate->StatementData.NumStep    = HiiStatement->ExtraData.NumData.Step;
      HiiStatementPrivate->StatementData.StrMaxSize = HiiStatement->ExtraData.StrData.MaxSize;
      HiiStatementPrivate->StatementData.StrMinSize = HiiStatement->ExtraData.StrData.MinSize;
      HiiStatementPrivate->Suppressed               = FALSE;
      HiiStatementPrivate->GrayedOut                = FALSE;

      //
      // Expression
      //
      if (HiiFormPrivate->Suppressed) {
        HiiStatementPrivate->Suppressed = TRUE;
      } else {
        if (HiiStatement->ExpressionList != NULL) {
          ExpressionResult =  EvaluateExpressionList (HiiStatement->ExpressionList, TRUE, HiiFormSet, HiiForm);
          if (ExpressionResult == ExpressGrayOut) {
            HiiStatementPrivate->GrayedOut = TRUE;
          } else if (ExpressionResult == ExpressSuppress) {
            HiiStatementPrivate->Suppressed = TRUE;
          }
        }
      }

      // Get x-UEFI-redfish string using String ID.
      Status = GetXuefiStringAndLangByStringId (FormsetPrivate, HiiStatementPrivate->Description, &String, NULL, NULL);
      if (!EFI_ERROR (Status)) {
        HiiStatementPrivate->XuefiRedfishStr = String;
        //
        // Attach to statement list.
        //
        InsertTailList (&HiiFormPrivate->StatementList, &HiiStatementPrivate->Link);
      } else {
        if (!RedfishPlatformConfigFeatureProp (REDFISH_PLATFORM_CONFIG_BUILD_MENU_PATH)) {
          //
          // If there is no x-UEFI-redfish language for this statement, we don't add this statement
          // since we don't need to build menu path for attribute registry.
          //
          FreePool (HiiStatementPrivate);
        } else {
          //
          // This is not x-UEFI-redfish string and we don't cache its string for searching Redfish configure language.
          // When caller wants the string, we will read English string by calling HiiGetString().
          //
          HiiStatementPrivate->XuefiRedfishStr = NULL;
          //
          // Attach to statement list.
          //
          InsertTailList (&HiiFormPrivate->StatementList, &HiiStatementPrivate->Link);
        }
      }

      HiiStatementLink = GetNextNode (&HiiForm->StatementListHead, HiiStatementLink);
    }

    //
    // Attach to form list.
    //
    InsertTailList (&FormsetPrivate->HiiFormList, &HiiFormPrivate->Link);
    HiiFormLink = GetNextNode (&HiiFormSet->FormListHead, HiiFormLink);
  }

  return EFI_SUCCESS;

ErrorExit:

  //
  // Release HiiFormSet if HiiFormSet is not linked to FormsetPrivate yet.
  //
  if ((HiiFormSet != NULL) && (FormsetPrivate->HiiFormSet != HiiFormSet)) {
    DestroyFormSet (HiiFormSet);
  }

  //
  // Release resource when error happens.
  //
  ReleaseFormset (FormsetPrivate);

  return Status;
}

/**
  Load formset list on given HII handle.

  @param[in]  HiiHandle     HII handle to load formset list.
  @param[out] FormsetList   Pointer to formset list returned on given handle.

  @retval     EFI_STATUS

**/
EFI_STATUS
LoadFormsetList (
  IN   EFI_HII_HANDLE  *HiiHandle,
  OUT  LIST_ENTRY      *FormsetList
  )
{
  EFI_STATUS                                Status;
  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate;

  if ((HiiHandle == NULL) || (FormsetList == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  FormsetPrivate = GetFormsetPrivateByHiiHandle (HiiHandle, FormsetList);
  if (FormsetPrivate != NULL) {
    return EFI_ALREADY_STARTED;
  }

  FormsetPrivate =  NewFormsetPrivate ();
  if (FormsetPrivate == NULL) {
    DEBUG ((DEBUG_ERROR, "%a: out of resource\n", __func__));
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Load formset on the given HII handle.
  //
  Status = LoadFormset (HiiHandle, FormsetPrivate);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "%a: Formset is not loaded for edk2 redfish: %r\n", __func__, Status));
    FreePool (FormsetPrivate);
    return Status;
  }

  //
  // Attach to cache list.
  //
  InsertTailList (FormsetList, &FormsetPrivate->Link);

  DEBUG_CODE (
    if (RedfishPlatformConfigDebugProp (REDFISH_PLATFORM_CONFIG_DEBUG_DUMP_FORMSET)) {
    DumpFormsetList (FormsetList);
  }

    );

  return EFI_SUCCESS;
}

/**
  Release formset list and all the forms that belong to this formset.

  @param[in]      FormsetList   Pointer to formset list that needs to be
                                released.

  @retval         EFI_STATUS

**/
EFI_STATUS
ReleaseFormsetList (
  IN  LIST_ENTRY  *FormsetList
  )
{
  LIST_ENTRY                                *HiiFormsetLink;
  LIST_ENTRY                                *HiiFormsetNextLink;
  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *HiiFormsetPrivate;

  if (FormsetList == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (IsListEmpty (FormsetList)) {
    return EFI_SUCCESS;
  }

  HiiFormsetLink = GetFirstNode (FormsetList);
  while (!IsNull (FormsetList, HiiFormsetLink)) {
    HiiFormsetNextLink = GetNextNode (FormsetList, HiiFormsetLink);
    HiiFormsetPrivate  = REDFISH_PLATFORM_CONFIG_FORMSET_FROM_LINK (HiiFormsetLink);

    //
    // Detach from list.
    //
    RemoveEntryList (&HiiFormsetPrivate->Link);
    ReleaseFormset (HiiFormsetPrivate);
    FreePool (HiiFormsetPrivate);
    HiiFormsetLink = HiiFormsetNextLink;
  }

  return EFI_SUCCESS;
}

/**
  Get all pending list.

  @param[in]  HiiHandle   HII handle instance.
  @param[in]  PendingList Pending list to keep pending data.

  @retval REDFISH_PLATFORM_CONFIG_PENDING_LIST *   Pointer to pending list data.

**/
REDFISH_PLATFORM_CONFIG_PENDING_LIST *
GetPendingList (
  IN  EFI_HII_HANDLE  *HiiHandle,
  IN  LIST_ENTRY      *PendingList
  )
{
  LIST_ENTRY                            *PendingListLink;
  REDFISH_PLATFORM_CONFIG_PENDING_LIST  *Target;

  if ((HiiHandle == NULL) || (PendingList == NULL)) {
    return NULL;
  }

  if (IsListEmpty (PendingList)) {
    return NULL;
  }

  PendingListLink = GetFirstNode (PendingList);
  while (!IsNull (PendingList, PendingListLink)) {
    Target = REDFISH_PLATFORM_CONFIG_PENDING_LIST_FROM_LINK (PendingListLink);

    if (Target->HiiHandle == HiiHandle) {
      return Target;
    }

    PendingListLink = GetNextNode (PendingList, PendingListLink);
  }

  return NULL;
}

/**
  When HII database is updated. Keep updated HII handle into pending list so
  we can process them later.

  @param[in]  HiiHandle   HII handle instance.
  @param[in]  PendingList Pending list to keep HII handle which is recently updated.

  @retval EFI_SUCCESS             HII handle is saved in pending list.
  @retval EFI_INVALID_PARAMETER   HiiHandle is NULL or PendingList is NULL.
  @retval EFI_OUT_OF_RESOURCES    System is out of memory.

**/
EFI_STATUS
NotifyFormsetUpdate (
  IN  EFI_HII_HANDLE  *HiiHandle,
  IN  LIST_ENTRY      *PendingList
  )
{
  REDFISH_PLATFORM_CONFIG_PENDING_LIST  *TargetPendingList;

  if ((HiiHandle == NULL) || (PendingList == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check and see if this HII handle is processed already.
  //
  TargetPendingList = GetPendingList (HiiHandle, PendingList);
  if (TargetPendingList != NULL) {
    TargetPendingList->IsDeleted = FALSE;
    DEBUG_CODE (
      DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%a: HII handle: 0x%x is updated\n", __func__, HiiHandle));
      );
    return EFI_SUCCESS;
  }

  TargetPendingList = AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_PENDING_LIST));
  if (TargetPendingList == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  TargetPendingList->HiiHandle = HiiHandle;
  TargetPendingList->IsDeleted = FALSE;

  InsertTailList (PendingList, &TargetPendingList->Link);

  DEBUG_CODE (
    DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%a: HII handle: 0x%x is created\n", __func__, HiiHandle));
    );

  return EFI_SUCCESS;
}

/**
  When HII database is updated and form-set is deleted. Keep deleted HII handle into pending list so
  we can process them later.

  @param[in]  HiiHandle   HII handle instance.
  @param[in]  PendingList Pending list to keep HII handle which is recently updated.

  @retval EFI_SUCCESS             HII handle is saved in pending list.
  @retval EFI_INVALID_PARAMETER   HiiHandle is NULL or PendingList is NULL.
  @retval EFI_OUT_OF_RESOURCES    System is out of memory.

**/
EFI_STATUS
NotifyFormsetDeleted (
  IN  EFI_HII_HANDLE  *HiiHandle,
  IN  LIST_ENTRY      *PendingList
  )
{
  REDFISH_PLATFORM_CONFIG_PENDING_LIST  *TargetPendingList;

  if ((HiiHandle == NULL) || (PendingList == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check and see if this HII handle is processed already.
  //
  TargetPendingList = GetPendingList (HiiHandle, PendingList);
  if (TargetPendingList != NULL) {
    TargetPendingList->IsDeleted = TRUE;
    DEBUG_CODE (
      DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%a: HII handle: 0x%x is updated and deleted\n", __func__, HiiHandle));
      );
    return EFI_SUCCESS;
  }

  TargetPendingList = AllocateZeroPool (sizeof (REDFISH_PLATFORM_CONFIG_PENDING_LIST));
  if (TargetPendingList == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  TargetPendingList->HiiHandle = HiiHandle;
  TargetPendingList->IsDeleted = TRUE;

  InsertTailList (PendingList, &TargetPendingList->Link);

  DEBUG_CODE (
    DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%a: HII handle: 0x%x is deleted\n", __func__, HiiHandle));
    );

  return EFI_SUCCESS;
}

/**
  There are HII database update and we need to process them accordingly so that we
  won't use stale data. This function will parse updated HII handle again in order
  to get updated data-set.

  @param[in]  FormsetList   List to keep HII form-set.
  @param[in]  PendingList   List to keep HII handle that is updated.

  @retval EFI_SUCCESS             HII handle is saved in pending list.
  @retval EFI_INVALID_PARAMETER   FormsetList is NULL or PendingList is NULL.

**/
EFI_STATUS
ProcessPendingList (
  IN  LIST_ENTRY  *FormsetList,
  IN  LIST_ENTRY  *PendingList
  )
{
  LIST_ENTRY                                *PendingListLink;
  LIST_ENTRY                                *PendingListNextLink;
  REDFISH_PLATFORM_CONFIG_PENDING_LIST      *Target;
  REDFISH_PLATFORM_CONFIG_FORM_SET_PRIVATE  *FormsetPrivate;
  EFI_STATUS                                Status;

  if ((FormsetList == NULL) || (PendingList == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  if (IsListEmpty (PendingList)) {
    return EFI_SUCCESS;
  }

  PendingListLink = GetFirstNode (PendingList);
  while (!IsNull (PendingList, PendingListLink)) {
    PendingListNextLink = GetNextNode (PendingList, PendingListLink);
    Target              = REDFISH_PLATFORM_CONFIG_PENDING_LIST_FROM_LINK (PendingListLink);

    if (Target->IsDeleted) {
      //
      // The HII resource on this HII handle is removed. Release the formset.
      //
      FormsetPrivate = GetFormsetPrivateByHiiHandle (Target->HiiHandle, FormsetList);
      if (FormsetPrivate != NULL) {
        DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%a: formset: %g is removed because driver release HII resource it already\n", __func__, FormsetPrivate->Guid));
        RemoveEntryList (&FormsetPrivate->Link);
        ReleaseFormset (FormsetPrivate);
        FreePool (FormsetPrivate);
      } else {
        DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%a: formset on HII handle 0x%x was removed already\n", __func__, Target->HiiHandle));
      }
    } else {
      //
      // The HII resource on this HII handle is updated/removed.
      //
      FormsetPrivate = GetFormsetPrivateByHiiHandle (Target->HiiHandle, FormsetList);
      if (FormsetPrivate != NULL) {
        //
        // HII formset already exist, release it and query again.
        //
        DEBUG ((DEBUG_REDFISH_PLATFORM_CONFIG, "%a: formset: %g is updated. Release current formset\n", __func__, &FormsetPrivate->Guid));
        RemoveEntryList (&FormsetPrivate->Link);
        ReleaseFormset (FormsetPrivate);
        FreePool (FormsetPrivate);
      }

      Status = LoadFormsetList (Target->HiiHandle, FormsetList);
      if (EFI_ERROR (Status)) {
        if (Status == EFI_UNSUPPORTED) {
          DEBUG ((DEBUG_ERROR, "  The formset has no x-UEFI-redfish configurations.\n"));
        } else {
          DEBUG ((DEBUG_ERROR, "  load formset from HII handle: 0x%x failed: %r\n", Target->HiiHandle, Status));
        }
      }
    }

    //
    // Detach it from list first.
    //
    RemoveEntryList (&Target->Link);
    FreePool (Target);

    PendingListLink = PendingListNextLink;
  }

  return EFI_SUCCESS;
}

/**
  Release all resource in statement list.

  @param[in]  StatementList   Statement list to be released.

  @retval EFI_SUCCESS             All resource are released.
  @retval EFI_INVALID_PARAMETER   StatementList is NULL.

**/
EFI_STATUS
ReleaseStatementList (
  IN  REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_LIST  *StatementList
  )
{
  REDFISH_PLATFORM_CONFIG_STATEMENT_PRIVATE_REF  *StatementRef;
  LIST_ENTRY                                     *NextLink;

  if (StatementList == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (IsListEmpty (&StatementList->StatementList)) {
    return EFI_SUCCESS;
  }

  NextLink = GetFirstNode (&StatementList->StatementList);
  while (!IsNull (&StatementList->StatementList, NextLink)) {
    StatementRef = REDFISH_PLATFORM_CONFIG_STATEMENT_REF_FROM_LINK (NextLink);
    NextLink     = GetNextNode (&StatementList->StatementList, NextLink);

    RemoveEntryList (&StatementRef->Link);
    FreePool (StatementRef);
  }

  return EFI_SUCCESS;
}
