/** @file
Implementation for handling the User Interface option processing.


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

**/

#include "FormDisplay.h"

#define MAX_TIME_OUT_LEN  0x10

/**
  Concatenate a narrow string to another string.

  @param Destination The destination string.
  @param DestMax     The Max length of destination string.
  @param Source      The source string. The string to be concatenated.
                     to the end of Destination.

**/
VOID
NewStrCat (
  IN OUT CHAR16  *Destination,
  IN     UINTN   DestMax,
  IN     CHAR16  *Source
  )
{
  UINTN  Length;

  for (Length = 0; Destination[Length] != 0; Length++) {
  }

  //
  // We now have the length of the original string
  // We can safely assume for now that we are concatenating a narrow value to this string.
  // For instance, the string is "XYZ" and cat'ing ">"
  // If this assumption changes, we need to make this routine a bit more complex
  //
  Destination[Length] = NARROW_CHAR;
  Length++;

  StrCpyS (Destination + Length, DestMax - Length, Source);
}

/**
  Get UINT64 type value.

  @param  Value                  Input Hii value.

  @retval UINT64                 Return the UINT64 type value.

**/
UINT64
HiiValueToUINT64 (
  IN EFI_HII_VALUE  *Value
  )
{
  UINT64  RetVal;

  RetVal = 0;

  switch (Value->Type) {
    case EFI_IFR_TYPE_NUM_SIZE_8:
      RetVal = Value->Value.u8;
      break;

    case EFI_IFR_TYPE_NUM_SIZE_16:
      RetVal = Value->Value.u16;
      break;

    case EFI_IFR_TYPE_NUM_SIZE_32:
      RetVal = Value->Value.u32;
      break;

    case EFI_IFR_TYPE_BOOLEAN:
      RetVal = Value->Value.b;
      break;

    case EFI_IFR_TYPE_DATE:
      RetVal = *(UINT64 *)&Value->Value.date;
      break;

    case EFI_IFR_TYPE_TIME:
      RetVal = (*(UINT64 *)&Value->Value.time) & 0xffffff;
      break;

    default:
      RetVal = Value->Value.u64;
      break;
  }

  return RetVal;
}

/**
  Check whether this value type can be transfer to EFI_IFR_TYPE_BUFFER type.

  EFI_IFR_TYPE_REF, EFI_IFR_TYPE_DATE and EFI_IFR_TYPE_TIME are converted to
  EFI_IFR_TYPE_BUFFER when do the value compare.

  @param  Value                  Expression value to compare on.

  @retval TRUE                   This value type can be transter to EFI_IFR_TYPE_BUFFER type.
  @retval FALSE                  This value type can't be transter to EFI_IFR_TYPE_BUFFER type.

**/
BOOLEAN
IsTypeInBuffer (
  IN  EFI_HII_VALUE  *Value
  )
{
  switch (Value->Type) {
    case EFI_IFR_TYPE_BUFFER:
    case EFI_IFR_TYPE_DATE:
    case EFI_IFR_TYPE_TIME:
    case EFI_IFR_TYPE_REF:
      return TRUE;

    default:
      return FALSE;
  }
}

/**
  Check whether this value type can be transfer to EFI_IFR_TYPE_UINT64

  @param  Value                  Expression value to compare on.

  @retval TRUE                   This value type can be transter to EFI_IFR_TYPE_BUFFER type.
  @retval FALSE                  This value type can't be transter to EFI_IFR_TYPE_BUFFER type.

**/
BOOLEAN
IsTypeInUINT64 (
  IN  EFI_HII_VALUE  *Value
  )
{
  switch (Value->Type) {
    case EFI_IFR_TYPE_NUM_SIZE_8:
    case EFI_IFR_TYPE_NUM_SIZE_16:
    case EFI_IFR_TYPE_NUM_SIZE_32:
    case EFI_IFR_TYPE_NUM_SIZE_64:
    case EFI_IFR_TYPE_BOOLEAN:
      return TRUE;

    default:
      return FALSE;
  }
}

/**
  Return the buffer length and buffer pointer for this value.

  EFI_IFR_TYPE_REF, EFI_IFR_TYPE_DATE and EFI_IFR_TYPE_TIME are converted to
  EFI_IFR_TYPE_BUFFER when do the value compare.

  @param  Value                  Expression value to compare on.
  @param  Buf                    Return the buffer pointer.
  @param  BufLen                 Return the buffer length.

**/
VOID
GetBufAndLenForValue (
  IN  EFI_HII_VALUE  *Value,
  OUT UINT8          **Buf,
  OUT UINT16         *BufLen
  )
{
  switch (Value->Type) {
    case EFI_IFR_TYPE_BUFFER:
      *Buf    = Value->Buffer;
      *BufLen = Value->BufferLen;
      break;

    case EFI_IFR_TYPE_DATE:
      *Buf    = (UINT8 *)(&Value->Value.date);
      *BufLen = (UINT16)sizeof (EFI_HII_DATE);
      break;

    case EFI_IFR_TYPE_TIME:
      *Buf    = (UINT8 *)(&Value->Value.time);
      *BufLen = (UINT16)sizeof (EFI_HII_TIME);
      break;

    case EFI_IFR_TYPE_REF:
      *Buf    = (UINT8 *)(&Value->Value.ref);
      *BufLen = (UINT16)sizeof (EFI_HII_REF);
      break;

    default:
      *Buf    = NULL;
      *BufLen = 0;
  }
}

/**
  Compare two Hii value.

  @param  Value1                 Expression value to compare on left-hand.
  @param  Value2                 Expression value to compare on right-hand.
  @param  Result                 Return value after compare.
                                 retval 0                      Two operators equal.
                                 return Positive value if Value1 is greater than Value2.
                                 retval Negative value if Value1 is less than Value2.
  @param  HiiHandle              Only required for string compare.

  @retval other                  Could not perform compare on two values.
  @retval EFI_SUCCESS            Compare the value success.

**/
EFI_STATUS
CompareHiiValue (
  IN  EFI_HII_VALUE   *Value1,
  IN  EFI_HII_VALUE   *Value2,
  OUT INTN            *Result,
  IN  EFI_HII_HANDLE  HiiHandle OPTIONAL
  )
{
  INT64   Temp64;
  CHAR16  *Str1;
  CHAR16  *Str2;
  UINTN   Len;
  UINT8   *Buf1;
  UINT16  Buf1Len;
  UINT8   *Buf2;
  UINT16  Buf2Len;

  if ((Value1->Type == EFI_IFR_TYPE_STRING) && (Value2->Type == EFI_IFR_TYPE_STRING)) {
    if ((Value1->Value.string == 0) || (Value2->Value.string == 0)) {
      //
      // StringId 0 is reserved
      //
      return EFI_INVALID_PARAMETER;
    }

    if (Value1->Value.string == Value2->Value.string) {
      *Result = 0;
      return EFI_SUCCESS;
    }

    Str1 = GetToken (Value1->Value.string, HiiHandle);
    if (Str1 == NULL) {
      //
      // String not found
      //
      return EFI_NOT_FOUND;
    }

    Str2 = GetToken (Value2->Value.string, HiiHandle);
    if (Str2 == NULL) {
      FreePool (Str1);
      return EFI_NOT_FOUND;
    }

    *Result = StrCmp (Str1, Str2);

    FreePool (Str1);
    FreePool (Str2);

    return EFI_SUCCESS;
  }

  //
  // Take types(date, time, ref, buffer) as buffer
  //
  if (IsTypeInBuffer (Value1) && IsTypeInBuffer (Value2)) {
    GetBufAndLenForValue (Value1, &Buf1, &Buf1Len);
    GetBufAndLenForValue (Value2, &Buf2, &Buf2Len);

    Len     = Buf1Len > Buf2Len ? Buf2Len : Buf1Len;
    *Result = CompareMem (Buf1, Buf2, Len);
    if ((*Result == 0) && (Buf1Len != Buf2Len)) {
      //
      // In this case, means base on samll number buffer, the data is same
      // So which value has more data, which value is bigger.
      //
      *Result = Buf1Len > Buf2Len ? 1 : -1;
    }

    return EFI_SUCCESS;
  }

  //
  // Take remain types(integer, boolean, date/time) as integer
  //
  if (IsTypeInUINT64 (Value1) && IsTypeInUINT64 (Value2)) {
    Temp64 = HiiValueToUINT64 (Value1) - HiiValueToUINT64 (Value2);
    if (Temp64 > 0) {
      *Result = 1;
    } else if (Temp64 < 0) {
      *Result = -1;
    } else {
      *Result = 0;
    }

    return EFI_SUCCESS;
  }

  return EFI_UNSUPPORTED;
}

/**
  Search an Option of a Question by its value.

  @param  Question               The Question
  @param  OptionValue            Value for Option to be searched.

  @retval Pointer                Pointer to the found Option.
  @retval NULL                   Option not found.

**/
DISPLAY_QUESTION_OPTION *
ValueToOption (
  IN FORM_DISPLAY_ENGINE_STATEMENT  *Question,
  IN EFI_HII_VALUE                  *OptionValue
  )
{
  LIST_ENTRY               *Link;
  DISPLAY_QUESTION_OPTION  *Option;
  INTN                     Result;
  EFI_HII_VALUE            Value;

  Link = GetFirstNode (&Question->OptionListHead);
  while (!IsNull (&Question->OptionListHead, Link)) {
    Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);

    ZeroMem (&Value, sizeof (EFI_HII_VALUE));
    Value.Type = Option->OptionOpCode->Type;
    CopyMem (&Value.Value, &Option->OptionOpCode->Value, Option->OptionOpCode->Header.Length - OFFSET_OF (EFI_IFR_ONE_OF_OPTION, Value));

    if ((CompareHiiValue (&Value, OptionValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
      return Option;
    }

    Link = GetNextNode (&Question->OptionListHead, Link);
  }

  return NULL;
}

/**
  Return data element in an Array by its Index.

  @param  Array                  The data array.
  @param  Type                   Type of the data in this array.
  @param  Index                  Zero based index for data in this array.

  @retval Value                  The data to be returned

**/
UINT64
GetArrayData (
  IN VOID   *Array,
  IN UINT8  Type,
  IN UINTN  Index
  )
{
  UINT64  Data;

  ASSERT (Array != NULL);

  Data = 0;
  switch (Type) {
    case EFI_IFR_TYPE_NUM_SIZE_8:
      Data = (UINT64)*(((UINT8 *)Array) + Index);
      break;

    case EFI_IFR_TYPE_NUM_SIZE_16:
      Data = (UINT64)*(((UINT16 *)Array) + Index);
      break;

    case EFI_IFR_TYPE_NUM_SIZE_32:
      Data = (UINT64)*(((UINT32 *)Array) + Index);
      break;

    case EFI_IFR_TYPE_NUM_SIZE_64:
      Data = (UINT64)*(((UINT64 *)Array) + Index);
      break;

    default:
      break;
  }

  return Data;
}

/**
  Set value of a data element in an Array by its Index.

  @param  Array                  The data array.
  @param  Type                   Type of the data in this array.
  @param  Index                  Zero based index for data in this array.
  @param  Value                  The value to be set.

**/
VOID
SetArrayData (
  IN VOID    *Array,
  IN UINT8   Type,
  IN UINTN   Index,
  IN UINT64  Value
  )
{
  ASSERT (Array != NULL);

  switch (Type) {
    case EFI_IFR_TYPE_NUM_SIZE_8:
      *(((UINT8 *)Array) + Index) = (UINT8)Value;
      break;

    case EFI_IFR_TYPE_NUM_SIZE_16:
      *(((UINT16 *)Array) + Index) = (UINT16)Value;
      break;

    case EFI_IFR_TYPE_NUM_SIZE_32:
      *(((UINT32 *)Array) + Index) = (UINT32)Value;
      break;

    case EFI_IFR_TYPE_NUM_SIZE_64:
      *(((UINT64 *)Array) + Index) = (UINT64)Value;
      break;

    default:
      break;
  }
}

/**
  Check whether this value already in the array, if yes, return the index.

  @param  Array                  The data array.
  @param  Type                   Type of the data in this array.
  @param  Value                  The value to be find.
  @param  Index                  The index in the array which has same value with Value.

  @retval   TRUE Found the value in the array.
  @retval   FALSE Not found the value.

**/
BOOLEAN
FindArrayData (
  IN VOID    *Array,
  IN UINT8   Type,
  IN UINT64  Value,
  OUT UINTN  *Index OPTIONAL
  )
{
  UINTN   Count;
  UINT64  TmpValue;
  UINT64  ValueComp;

  ASSERT (Array != NULL);

  Count    = 0;
  TmpValue = 0;

  switch (Type) {
    case EFI_IFR_TYPE_NUM_SIZE_8:
      ValueComp = (UINT8)Value;
      break;

    case EFI_IFR_TYPE_NUM_SIZE_16:
      ValueComp = (UINT16)Value;
      break;

    case EFI_IFR_TYPE_NUM_SIZE_32:
      ValueComp = (UINT32)Value;
      break;

    case EFI_IFR_TYPE_NUM_SIZE_64:
      ValueComp = (UINT64)Value;
      break;

    default:
      ValueComp = 0;
      break;
  }

  while ((TmpValue = GetArrayData (Array, Type, Count)) != 0) {
    if (ValueComp == TmpValue) {
      if (Index != NULL) {
        *Index = Count;
      }

      return TRUE;
    }

    Count++;
  }

  return FALSE;
}

/**
  Print Question Value according to it's storage width and display attributes.

  @param  Question               The Question to be printed.
  @param  FormattedNumber        Buffer for output string.
  @param  BufferSize             The FormattedNumber buffer size in bytes.

  @retval EFI_SUCCESS            Print success.
  @retval EFI_BUFFER_TOO_SMALL   Buffer size is not enough for formatted number.

**/
EFI_STATUS
PrintFormattedNumber (
  IN FORM_DISPLAY_ENGINE_STATEMENT  *Question,
  IN OUT CHAR16                     *FormattedNumber,
  IN UINTN                          BufferSize
  )
{
  INT64            Value;
  CHAR16           *Format;
  EFI_HII_VALUE    *QuestionValue;
  EFI_IFR_NUMERIC  *NumericOp;

  if (BufferSize < (21 * sizeof (CHAR16))) {
    return EFI_BUFFER_TOO_SMALL;
  }

  QuestionValue = &Question->CurrentValue;
  NumericOp     = (EFI_IFR_NUMERIC *)Question->OpCode;

  Value = (INT64)QuestionValue->Value.u64;
  switch (NumericOp->Flags & EFI_IFR_DISPLAY) {
    case EFI_IFR_DISPLAY_INT_DEC:
      switch (QuestionValue->Type) {
        case EFI_IFR_NUMERIC_SIZE_1:
          Value = (INT64)((INT8)QuestionValue->Value.u8);
          break;

        case EFI_IFR_NUMERIC_SIZE_2:
          Value = (INT64)((INT16)QuestionValue->Value.u16);
          break;

        case EFI_IFR_NUMERIC_SIZE_4:
          Value = (INT64)((INT32)QuestionValue->Value.u32);
          break;

        case EFI_IFR_NUMERIC_SIZE_8:
        default:
          break;
      }

      if (Value < 0) {
        Value  = -Value;
        Format = L"-%ld";
      } else {
        Format = L"%ld";
      }

      break;

    case EFI_IFR_DISPLAY_UINT_DEC:
      Format = L"%ld";
      break;

    case EFI_IFR_DISPLAY_UINT_HEX:
      Format = L"%lx";
      break;

    default:
      return EFI_UNSUPPORTED;
  }

  UnicodeSPrint (FormattedNumber, BufferSize, Format, Value);

  return EFI_SUCCESS;
}

/**
  Draw a pop up windows based on the dimension, number of lines and
  strings specified.

  @param RequestedWidth  The width of the pop-up.
  @param NumberOfLines   The number of lines.
  @param Marker          The variable argument list for the list of string to be printed.

**/
VOID
CreateSharedPopUp (
  IN  UINTN    RequestedWidth,
  IN  UINTN    NumberOfLines,
  IN  VA_LIST  Marker
  )
{
  UINTN   Index;
  CHAR16  Character;
  UINTN   Start;
  UINTN   End;
  UINTN   Top;
  UINTN   Bottom;
  CHAR16  *String;
  UINTN   DimensionsWidth;
  UINTN   DimensionsHeight;

  DimensionsWidth  = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
  DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow;

  gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());

  if ((RequestedWidth + 2) > DimensionsWidth) {
    RequestedWidth = DimensionsWidth - 2;
  }

  //
  // Subtract the PopUp width from total Columns, allow for one space extra on
  // each end plus a border.
  //
  Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gStatementDimensions.LeftColumn + 1;
  End   = Start + RequestedWidth + 1;

  Top    = ((DimensionsHeight - NumberOfLines - 2) / 2) + gStatementDimensions.TopRow - 1;
  Bottom = Top + NumberOfLines + 2;

  Character = BOXDRAW_DOWN_RIGHT;
  PrintCharAt (Start, Top, Character);
  Character = BOXDRAW_HORIZONTAL;
  for (Index = Start; Index + 2 < End; Index++) {
    PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
  }

  Character = BOXDRAW_DOWN_LEFT;
  PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
  Character = BOXDRAW_VERTICAL;

  for (Index = Top; Index + 2 < Bottom; Index++) {
    String = VA_ARG (Marker, CHAR16 *);

    //
    // This will clear the background of the line - we never know who might have been
    // here before us.  This differs from the next clear in that it used the non-reverse
    // video for normal printing.
    //
    if (GetStringWidth (String) / 2 > 1) {
      ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ());
    }

    //
    // Passing in a space results in the assumption that this is where typing will occur
    //
    if (String[0] == L' ') {
      ClearLines (Start + 1, End - 1, Index + 1, Index + 1, GetPopupInverseColor ());
    }

    //
    // Passing in a NULL results in a blank space
    //
    if (String[0] == CHAR_NULL) {
      ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ());
    }

    PrintStringAt (
      ((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gStatementDimensions.LeftColumn + 1,
      Index + 1,
      String
      );
    gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
    PrintCharAt (Start, Index + 1, Character);
    PrintCharAt (End - 1, Index + 1, Character);
  }

  Character = BOXDRAW_UP_RIGHT;
  PrintCharAt (Start, Bottom - 1, Character);
  Character = BOXDRAW_HORIZONTAL;
  for (Index = Start; Index + 2 < End; Index++) {
    PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
  }

  Character = BOXDRAW_UP_LEFT;
  PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
}

/**
  Draw a pop up windows based on the dimension, number of lines and
  strings specified.

  @param RequestedWidth  The width of the pop-up.
  @param NumberOfLines   The number of lines.
  @param ...             A series of text strings that displayed in the pop-up.

**/
VOID
EFIAPI
CreateMultiStringPopUp (
  IN  UINTN  RequestedWidth,
  IN  UINTN  NumberOfLines,
  ...
  )
{
  VA_LIST  Marker;

  VA_START (Marker, NumberOfLines);

  CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker);

  VA_END (Marker);
}

/**
  Process nothing.

  @param Event    The Event need to be process
  @param Context  The context of the event.

**/
VOID
EFIAPI
EmptyEventProcess (
  IN  EFI_EVENT  Event,
  IN  VOID       *Context
  )
{
}

/**
  Process for the refresh interval statement.

  @param Event    The Event need to be process
  @param Context  The context of the event.

**/
VOID
EFIAPI
RefreshTimeOutProcess (
  IN  EFI_EVENT  Event,
  IN  VOID       *Context
  )
{
  WARNING_IF_CONTEXT  *EventInfo;
  CHAR16              TimeOutString[MAX_TIME_OUT_LEN];

  EventInfo = (WARNING_IF_CONTEXT *)Context;

  if (*(EventInfo->TimeOut) == 0) {
    gBS->CloseEvent (Event);

    gBS->SignalEvent (EventInfo->SyncEvent);
    return;
  }

  UnicodeSPrint (TimeOutString, MAX_TIME_OUT_LEN, L"%d", *(EventInfo->TimeOut));

  CreateDialog (NULL, gEmptyString, EventInfo->ErrorInfo, gPressEnter, gEmptyString, TimeOutString, NULL);

  *(EventInfo->TimeOut) -= 1;
}

/**
  Display error message for invalid password.

**/
VOID
PasswordInvalid (
  VOID
  )
{
  EFI_INPUT_KEY  Key;

  //
  // Invalid password, prompt error message
  //
  do {
    CreateDialog (&Key, gEmptyString, gPassowordInvalid, gPressEnter, gEmptyString, NULL);
  } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
}

/**
  Process password op code.

  @param  MenuOption             The menu for current password op code.

  @retval EFI_SUCCESS            Question Option process success.
  @retval Other                  Question Option process fail.

**/
EFI_STATUS
PasswordProcess (
  IN  UI_MENU_OPTION  *MenuOption
  )
{
  CHAR16                         *StringPtr;
  CHAR16                         *TempString;
  UINTN                          Maximum;
  EFI_STATUS                     Status;
  EFI_IFR_PASSWORD               *PasswordInfo;
  FORM_DISPLAY_ENGINE_STATEMENT  *Question;
  EFI_INPUT_KEY                  Key;

  Question     = MenuOption->ThisTag;
  PasswordInfo = (EFI_IFR_PASSWORD *)Question->OpCode;
  Maximum      = PasswordInfo->MaxSize;
  Status       = EFI_SUCCESS;

  StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
  ASSERT (StringPtr);

  //
  // Use a NULL password to test whether old password is required
  //
  *StringPtr = 0;
  Status     = Question->PasswordCheck (gFormData, Question, StringPtr);
  if ((Status == EFI_NOT_AVAILABLE_YET) || (Status == EFI_UNSUPPORTED)) {
    //
    // Password can't be set now.
    //
    if (Status == EFI_UNSUPPORTED) {
      do {
        CreateDialog (&Key, gEmptyString, gPasswordUnsupported, gPressEnter, gEmptyString, NULL);
      } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
    }

    FreePool (StringPtr);
    return EFI_SUCCESS;
  }

  if (EFI_ERROR (Status)) {
    //
    // Old password exist, ask user for the old password
    //
    Status = ReadString (MenuOption, gPromptForPassword, StringPtr);
    if (EFI_ERROR (Status)) {
      ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
      FreePool (StringPtr);
      return Status;
    }

    //
    // Check user input old password
    //
    Status = Question->PasswordCheck (gFormData, Question, StringPtr);
    if (EFI_ERROR (Status)) {
      if (Status == EFI_NOT_READY) {
        //
        // Typed in old password incorrect
        //
        PasswordInvalid ();
      } else {
        Status = EFI_SUCCESS;
      }

      ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
      FreePool (StringPtr);
      return Status;
    }
  }

  //
  // Ask for new password
  //
  ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
  Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr);
  if (EFI_ERROR (Status)) {
    //
    // Reset state machine for password
    //
    Question->PasswordCheck (gFormData, Question, NULL);
    ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
    FreePool (StringPtr);
    return Status;
  }

  //
  // Confirm new password
  //
  TempString = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
  ASSERT (TempString);
  Status = ReadString (MenuOption, gConfirmPassword, TempString);
  if (EFI_ERROR (Status)) {
    //
    // Reset state machine for password
    //
    Question->PasswordCheck (gFormData, Question, NULL);
    ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
    ZeroMem (TempString, (Maximum + 1) * sizeof (CHAR16));
    FreePool (StringPtr);
    FreePool (TempString);
    return Status;
  }

  //
  // Compare two typed-in new passwords
  //
  if (StrCmp (StringPtr, TempString) == 0) {
    gUserInput->InputValue.Buffer       = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr);
    gUserInput->InputValue.BufferLen    = Question->CurrentValue.BufferLen;
    gUserInput->InputValue.Type         = Question->CurrentValue.Type;
    gUserInput->InputValue.Value.string = HiiSetString (gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL);

    Status = EFI_SUCCESS;
  } else {
    //
    // Reset state machine for password
    //
    Question->PasswordCheck (gFormData, Question, NULL);

    //
    // Two password mismatch, prompt error message
    //
    do {
      CreateDialog (&Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString, NULL);
    } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);

    Status = EFI_INVALID_PARAMETER;
  }

  ZeroMem (TempString, (Maximum + 1) * sizeof (CHAR16));
  ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
  FreePool (TempString);
  FreePool (StringPtr);

  return Status;
}

/**
  Print some debug message about mismatched menu info.

  @param  MenuOption             The MenuOption for this Question.

**/
VOID
PrintMismatchMenuInfo (
  IN  UI_MENU_OPTION  *MenuOption
  )
{
  CHAR16                         *FormTitleStr;
  CHAR16                         *FormSetTitleStr;
  CHAR16                         *OneOfOptionStr;
  CHAR16                         *QuestionName;
  LIST_ENTRY                     *Link;
  FORM_DISPLAY_ENGINE_STATEMENT  *Question;
  EFI_IFR_ORDERED_LIST           *OrderList;
  UINT8                          Index;
  EFI_HII_VALUE                  HiiValue;
  EFI_HII_VALUE                  *QuestionValue;
  DISPLAY_QUESTION_OPTION        *Option;
  UINT8                          *ValueArray;
  UINT8                          ValueType;
  EFI_IFR_FORM_SET               *FormsetBuffer;
  UINTN                          FormsetBufferSize;

  Question = MenuOption->ThisTag;

  if (!EFI_ERROR (HiiGetFormSetFromHiiHandle (gFormData->HiiHandle, &FormsetBuffer, &FormsetBufferSize))) {
    FormSetTitleStr = GetToken (FormsetBuffer->FormSetTitle, gFormData->HiiHandle);
    FormTitleStr    = GetToken (gFormData->FormTitle, gFormData->HiiHandle);

    DEBUG ((DEBUG_ERROR, "\n[%a]: Mismatch Formset    : Formset Guid = %g,  FormSet title = %s\n", gEfiCallerBaseName, &gFormData->FormSetGuid, FormSetTitleStr));
    DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Form       : FormId = %d,  Form title = %s.\n", gEfiCallerBaseName, gFormData->FormId, FormTitleStr));
  }

  if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
    QuestionName = GetToken (((EFI_IFR_ORDERED_LIST *)MenuOption->ThisTag->OpCode)->Question.Header.Prompt, gFormData->HiiHandle);
    Link         = GetFirstNode (&Question->OptionListHead);
    Option       = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
    ValueType    = Option->OptionOpCode->Type;
    DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Error      : OrderedList value in the array doesn't match with option value.\n", gEfiCallerBaseName));
    DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OrderedList: Name = %s.\n", gEfiCallerBaseName, QuestionName));
    DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OrderedList: OrderedList array value :\n", gEfiCallerBaseName));

    OrderList = (EFI_IFR_ORDERED_LIST *)Question->OpCode;
    for (Index = 0; Index < OrderList->MaxContainers; Index++) {
      ValueArray         = Question->CurrentValue.Buffer;
      HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
      DEBUG ((DEBUG_ERROR, "                                       Value[%d] =%ld.\n", Index, HiiValue.Value.u64));
    }
  } else if (Question->OpCode->OpCode == EFI_IFR_ONE_OF_OP) {
    QuestionName  = GetToken (((EFI_IFR_ONE_OF *)MenuOption->ThisTag->OpCode)->Question.Header.Prompt, gFormData->HiiHandle);
    QuestionValue = &Question->CurrentValue;
    DEBUG ((DEBUG_ERROR, "[%a]: Mismatch Error      : OneOf value doesn't match with option value.\n", gEfiCallerBaseName));
    DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf      : Name = %s.\n", gEfiCallerBaseName, QuestionName));
    switch (QuestionValue->Type) {
      case EFI_IFR_TYPE_NUM_SIZE_64:
        DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf      : OneOf value = %ld.\n", gEfiCallerBaseName, QuestionValue->Value.u64));
        break;

      case EFI_IFR_TYPE_NUM_SIZE_32:
        DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf      : OneOf value = %d.\n", gEfiCallerBaseName, QuestionValue->Value.u32));
        break;

      case EFI_IFR_TYPE_NUM_SIZE_16:
        DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf      : OneOf value = %d.\n", gEfiCallerBaseName, QuestionValue->Value.u16));
        break;

      case EFI_IFR_TYPE_NUM_SIZE_8:
        DEBUG ((DEBUG_ERROR, "[%a]: Mismatch OneOf      : OneOf value = %d.\n", gEfiCallerBaseName, QuestionValue->Value.u8));
        break;

      default:
        ASSERT (FALSE);
        break;
    }
  }

  Index = 0;
  Link  = GetFirstNode (&Question->OptionListHead);
  while (!IsNull (&Question->OptionListHead, Link)) {
    Option         = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
    OneOfOptionStr = GetToken (Option->OptionOpCode->Option, gFormData->HiiHandle);
    switch (Option->OptionOpCode->Type) {
      case EFI_IFR_TYPE_NUM_SIZE_64:
        DEBUG ((DEBUG_ERROR, "[%a]: Option %d            : Option Value = %ld,  Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u64, OneOfOptionStr));
        break;

      case EFI_IFR_TYPE_NUM_SIZE_32:
        DEBUG ((DEBUG_ERROR, "[%a]: Option %d            : Option Value = %d,  Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u32, OneOfOptionStr));
        break;

      case EFI_IFR_TYPE_NUM_SIZE_16:
        DEBUG ((DEBUG_ERROR, "[%a]: Option %d            : Option Value = %d,  Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u16, OneOfOptionStr));
        break;

      case EFI_IFR_TYPE_NUM_SIZE_8:
        DEBUG ((DEBUG_ERROR, "[%a]: Option %d            : Option Value = %d,  Option Name = %s.\n", gEfiCallerBaseName, Index, Option->OptionOpCode->Value.u8, OneOfOptionStr));
        break;

      default:
        ASSERT (FALSE);
        break;
    }

    Link = GetNextNode (&Question->OptionListHead, Link);
    Index++;
  }
}

/**
  Process a Question's Option (whether selected or un-selected).

  @param  MenuOption             The MenuOption for this Question.
  @param  Selected               TRUE: if Question is selected.
  @param  OptionString           Pointer of the Option String to be displayed.
  @param  SkipErrorValue         Whether need to return when value without option for it.

  @retval EFI_SUCCESS            Question Option process success.
  @retval Other                  Question Option process fail.

**/
EFI_STATUS
ProcessOptions (
  IN  UI_MENU_OPTION  *MenuOption,
  IN  BOOLEAN         Selected,
  OUT CHAR16          **OptionString,
  IN  BOOLEAN         SkipErrorValue
  )
{
  EFI_STATUS                     Status;
  CHAR16                         *StringPtr;
  UINTN                          Index;
  FORM_DISPLAY_ENGINE_STATEMENT  *Question;
  CHAR16                         FormattedNumber[21];
  UINT16                         Number;
  CHAR16                         Character[2];
  EFI_INPUT_KEY                  Key;
  UINTN                          BufferSize;
  DISPLAY_QUESTION_OPTION        *OneOfOption;
  LIST_ENTRY                     *Link;
  EFI_HII_VALUE                  HiiValue;
  EFI_HII_VALUE                  *QuestionValue;
  DISPLAY_QUESTION_OPTION        *Option;
  UINTN                          Index2;
  UINT8                          *ValueArray;
  UINT8                          ValueType;
  EFI_IFR_ORDERED_LIST           *OrderList;
  BOOLEAN                        ValueInvalid;
  UINTN                          MaxLen;

  Status = EFI_SUCCESS;

  StringPtr     = NULL;
  Character[1]  = L'\0';
  *OptionString = NULL;
  ValueInvalid  = FALSE;

  ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
  BufferSize = (gOptionBlockWidth + 1) * 2 * gStatementDimensions.BottomRow;

  Question      = MenuOption->ThisTag;
  QuestionValue = &Question->CurrentValue;

  switch (Question->OpCode->OpCode) {
    case EFI_IFR_ORDERED_LIST_OP:

      //
      // Check whether there are Options of this OrderedList
      //
      if (IsListEmpty (&Question->OptionListHead)) {
        break;
      }

      OrderList = (EFI_IFR_ORDERED_LIST *)Question->OpCode;

      Link        = GetFirstNode (&Question->OptionListHead);
      OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);

      ValueType  =  OneOfOption->OptionOpCode->Type;
      ValueArray = Question->CurrentValue.Buffer;

      if (Selected) {
        //
        // Go ask for input
        //
        Status = GetSelectionInputPopUp (MenuOption);
      } else {
        //
        // We now know how many strings we will have, so we can allocate the
        // space required for the array or strings.
        //
        MaxLen        = OrderList->MaxContainers * BufferSize / sizeof (CHAR16);
        *OptionString = AllocateZeroPool (MaxLen * sizeof (CHAR16));
        ASSERT (*OptionString);

        HiiValue.Type      = ValueType;
        HiiValue.Value.u64 = 0;
        for (Index = 0; Index < OrderList->MaxContainers; Index++) {
          HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
          if (HiiValue.Value.u64 == 0) {
            //
            // Values for the options in ordered lists should never be a 0
            //
            break;
          }

          OneOfOption = ValueToOption (Question, &HiiValue);
          if (OneOfOption == NULL) {
            //
            // Print debug msg for the mistach menu.
            //
            PrintMismatchMenuInfo (MenuOption);

            if (SkipErrorValue) {
              //
              // Just try to get the option string, skip the value which not has option.
              //
              continue;
            }

            //
            // Show error message
            //
            do {
              CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
            } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);

            //
            // The initial value of the orderedlist is invalid, force to be valid value
            // Exit current DisplayForm with new value.
            //
            gUserInput->SelectedStatement = Question;
            gMisMatch                     = TRUE;
            ValueArray                    = AllocateZeroPool (Question->CurrentValue.BufferLen);
            ASSERT (ValueArray != NULL);
            gUserInput->InputValue.Buffer    = ValueArray;
            gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
            gUserInput->InputValue.Type      = Question->CurrentValue.Type;

            Link   = GetFirstNode (&Question->OptionListHead);
            Index2 = 0;
            while (!IsNull (&Question->OptionListHead, Link) && Index2 < OrderList->MaxContainers) {
              Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
              Link   = GetNextNode (&Question->OptionListHead, Link);
              SetArrayData (ValueArray, ValueType, Index2, Option->OptionOpCode->Value.u64);
              Index2++;
            }

            SetArrayData (ValueArray, ValueType, Index2, 0);

            FreePool (*OptionString);
            *OptionString = NULL;
            return EFI_NOT_FOUND;
          }

          Character[0] = LEFT_ONEOF_DELIMITER;
          NewStrCat (OptionString[0], MaxLen, Character);
          StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
          ASSERT (StringPtr != NULL);
          NewStrCat (OptionString[0], MaxLen, StringPtr);
          Character[0] = RIGHT_ONEOF_DELIMITER;
          NewStrCat (OptionString[0], MaxLen, Character);
          Character[0] = CHAR_CARRIAGE_RETURN;
          NewStrCat (OptionString[0], MaxLen, Character);
          FreePool (StringPtr);
        }

        //
        // If valid option more than the max container, skip these options.
        //
        if (Index >= OrderList->MaxContainers) {
          break;
        }

        //
        // Search the other options, try to find the one not in the container.
        //
        Link = GetFirstNode (&Question->OptionListHead);
        while (!IsNull (&Question->OptionListHead, Link)) {
          OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
          Link        = GetNextNode (&Question->OptionListHead, Link);

          if (FindArrayData (ValueArray, ValueType, OneOfOption->OptionOpCode->Value.u64, NULL)) {
            continue;
          }

          //
          // Print debug msg for the mistach menu.
          //
          PrintMismatchMenuInfo (MenuOption);

          if (SkipErrorValue) {
            //
            // Not report error, just get the correct option string info.
            //
            Character[0] = LEFT_ONEOF_DELIMITER;
            NewStrCat (OptionString[0], MaxLen, Character);
            StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
            ASSERT (StringPtr != NULL);
            NewStrCat (OptionString[0], MaxLen, StringPtr);
            Character[0] = RIGHT_ONEOF_DELIMITER;
            NewStrCat (OptionString[0], MaxLen, Character);
            Character[0] = CHAR_CARRIAGE_RETURN;
            NewStrCat (OptionString[0], MaxLen, Character);
            FreePool (StringPtr);

            continue;
          }

          if (!ValueInvalid) {
            ValueInvalid = TRUE;
            //
            // Show error message
            //
            do {
              CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
            } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);

            //
            // The initial value of the orderedlist is invalid, force to be valid value
            // Exit current DisplayForm with new value.
            //
            gUserInput->SelectedStatement = Question;
            gMisMatch                     = TRUE;
            ValueArray                    = AllocateCopyPool (Question->CurrentValue.BufferLen, Question->CurrentValue.Buffer);
            ASSERT (ValueArray != NULL);
            gUserInput->InputValue.Buffer    = ValueArray;
            gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
            gUserInput->InputValue.Type      = Question->CurrentValue.Type;
          }

          SetArrayData (ValueArray, ValueType, Index++, OneOfOption->OptionOpCode->Value.u64);
        }

        if (ValueInvalid) {
          FreePool (*OptionString);
          *OptionString = NULL;
          return EFI_NOT_FOUND;
        }
      }

      break;

    case EFI_IFR_ONE_OF_OP:
      //
      // Check whether there are Options of this OneOf
      //
      if (IsListEmpty (&Question->OptionListHead)) {
        break;
      }

      if (Selected) {
        //
        // Go ask for input
        //
        Status = GetSelectionInputPopUp (MenuOption);
      } else {
        MaxLen        = BufferSize / sizeof (CHAR16);
        *OptionString = AllocateZeroPool (BufferSize);
        ASSERT (*OptionString);

        OneOfOption = ValueToOption (Question, QuestionValue);
        if (OneOfOption == NULL) {
          //
          // Print debug msg for the mistach menu.
          //
          PrintMismatchMenuInfo (MenuOption);

          if (SkipErrorValue) {
            //
            // Not report error, just get the correct option string info.
            //
            Link        = GetFirstNode (&Question->OptionListHead);
            OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
          } else {
            //
            // Show error message
            //
            do {
              CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
            } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);

            //
            // Force the Question value to be valid
            // Exit current DisplayForm with new value.
            //
            Link   = GetFirstNode (&Question->OptionListHead);
            Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);

            gUserInput->InputValue.Type = Option->OptionOpCode->Type;
            switch (gUserInput->InputValue.Type) {
              case EFI_IFR_TYPE_NUM_SIZE_8:
                gUserInput->InputValue.Value.u8 = Option->OptionOpCode->Value.u8;
                break;
              case EFI_IFR_TYPE_NUM_SIZE_16:
                CopyMem (&gUserInput->InputValue.Value.u16, &Option->OptionOpCode->Value.u16, sizeof (UINT16));
                break;
              case EFI_IFR_TYPE_NUM_SIZE_32:
                CopyMem (&gUserInput->InputValue.Value.u32, &Option->OptionOpCode->Value.u32, sizeof (UINT32));
                break;
              case EFI_IFR_TYPE_NUM_SIZE_64:
                CopyMem (&gUserInput->InputValue.Value.u64, &Option->OptionOpCode->Value.u64, sizeof (UINT64));
                break;
              default:
                ASSERT (FALSE);
                break;
            }

            gUserInput->SelectedStatement = Question;
            gMisMatch                     = TRUE;
            FreePool (*OptionString);
            *OptionString = NULL;
            return EFI_NOT_FOUND;
          }
        }

        Character[0] = LEFT_ONEOF_DELIMITER;
        NewStrCat (OptionString[0], MaxLen, Character);
        StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
        ASSERT (StringPtr != NULL);
        NewStrCat (OptionString[0], MaxLen, StringPtr);
        Character[0] = RIGHT_ONEOF_DELIMITER;
        NewStrCat (OptionString[0], MaxLen, Character);

        FreePool (StringPtr);
      }

      break;

    case EFI_IFR_CHECKBOX_OP:
      if (Selected) {
        //
        // Since this is a BOOLEAN operation, flip it upon selection
        //
        gUserInput->InputValue.Type    = QuestionValue->Type;
        gUserInput->InputValue.Value.b = (BOOLEAN)(QuestionValue->Value.b ? FALSE : TRUE);

        //
        // Perform inconsistent check
        //
        return EFI_SUCCESS;
      } else {
        *OptionString = AllocateZeroPool (BufferSize);
        ASSERT (*OptionString);

        *OptionString[0] = LEFT_CHECKBOX_DELIMITER;

        if (QuestionValue->Value.b) {
          *(OptionString[0] + 1) = CHECK_ON;
        } else {
          *(OptionString[0] + 1) = CHECK_OFF;
        }

        *(OptionString[0] + 2) = RIGHT_CHECKBOX_DELIMITER;
      }

      break;

    case EFI_IFR_NUMERIC_OP:
      if (Selected) {
        //
        // Go ask for input
        //
        Status = GetNumericInput (MenuOption);
      } else {
        *OptionString = AllocateZeroPool (BufferSize);
        ASSERT (*OptionString);

        *OptionString[0] = LEFT_NUMERIC_DELIMITER;

        //
        // Formatted print
        //
        PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
        Number = (UINT16)GetStringWidth (FormattedNumber);
        CopyMem (OptionString[0] + 1, FormattedNumber, Number);

        *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER;
      }

      break;

    case EFI_IFR_DATE_OP:
      if (Selected) {
        //
        // This is similar to numerics
        //
        Status = GetNumericInput (MenuOption);
      } else {
        *OptionString = AllocateZeroPool (BufferSize);
        ASSERT (*OptionString);

        switch (MenuOption->Sequence) {
          case 0:
            *OptionString[0] = LEFT_NUMERIC_DELIMITER;
            if (QuestionValue->Value.date.Month == 0xff) {
              UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"??");
            } else {
              UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Month);
            }

            *(OptionString[0] + 3) = DATE_SEPARATOR;
            break;

          case 1:
            SetUnicodeMem (OptionString[0], 4, L' ');
            if (QuestionValue->Value.date.Day == 0xff) {
              UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"??");
            } else {
              UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Day);
            }

            *(OptionString[0] + 6) = DATE_SEPARATOR;
            break;

          case 2:
            SetUnicodeMem (OptionString[0], 7, L' ');
            if (QuestionValue->Value.date.Year == 0xff) {
              UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"????");
            } else {
              UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%04d", QuestionValue->Value.date.Year);
            }

            *(OptionString[0] + 11) = RIGHT_NUMERIC_DELIMITER;
            break;
        }
      }

      break;

    case EFI_IFR_TIME_OP:
      if (Selected) {
        //
        // This is similar to numerics
        //
        Status = GetNumericInput (MenuOption);
      } else {
        *OptionString = AllocateZeroPool (BufferSize);
        ASSERT (*OptionString);

        switch (MenuOption->Sequence) {
          case 0:
            *OptionString[0] = LEFT_NUMERIC_DELIMITER;
            if (QuestionValue->Value.time.Hour == 0xff) {
              UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"??");
            } else {
              UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Hour);
            }

            *(OptionString[0] + 3) = TIME_SEPARATOR;
            break;

          case 1:
            SetUnicodeMem (OptionString[0], 4, L' ');
            if (QuestionValue->Value.time.Minute == 0xff) {
              UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"??");
            } else {
              UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Minute);
            }

            *(OptionString[0] + 6) = TIME_SEPARATOR;
            break;

          case 2:
            SetUnicodeMem (OptionString[0], 7, L' ');
            if (QuestionValue->Value.time.Second == 0xff) {
              UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"??");
            } else {
              UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Second);
            }

            *(OptionString[0] + 9) = RIGHT_NUMERIC_DELIMITER;
            break;
        }
      }

      break;

    case EFI_IFR_STRING_OP:
      if (Selected) {
        StringPtr = AllocateZeroPool (Question->CurrentValue.BufferLen + sizeof (CHAR16));
        ASSERT (StringPtr);
        CopyMem (StringPtr, Question->CurrentValue.Buffer, Question->CurrentValue.BufferLen);

        Status = ReadString (MenuOption, gPromptForData, StringPtr);
        if (EFI_ERROR (Status)) {
          FreePool (StringPtr);
          return Status;
        }

        gUserInput->InputValue.Buffer       = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr);
        gUserInput->InputValue.BufferLen    = Question->CurrentValue.BufferLen;
        gUserInput->InputValue.Type         = Question->CurrentValue.Type;
        gUserInput->InputValue.Value.string = HiiSetString (gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL);
        FreePool (StringPtr);
        return EFI_SUCCESS;
      } else {
        *OptionString = AllocateZeroPool (BufferSize);
        ASSERT (*OptionString);

        if (((CHAR16 *)Question->CurrentValue.Buffer)[0] == 0x0000) {
          *(OptionString[0]) = '_';
        } else {
          if (Question->CurrentValue.BufferLen < BufferSize) {
            BufferSize = Question->CurrentValue.BufferLen;
          }

          CopyMem (OptionString[0], (CHAR16 *)Question->CurrentValue.Buffer, BufferSize);
        }
      }

      break;

    case EFI_IFR_PASSWORD_OP:
      if (Selected) {
        Status = PasswordProcess (MenuOption);
      }

      break;

    default:
      break;
  }

  return Status;
}

/**
  Process the help string: Split StringPtr to several lines of strings stored in
  FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.

  @param  StringPtr              The entire help string.
  @param  FormattedString        The oupput formatted string.
  @param  EachLineWidth          The max string length of each line in the formatted string.
  @param  RowCount               TRUE: if Question is selected.

**/
UINTN
ProcessHelpString (
  IN  CHAR16  *StringPtr,
  OUT CHAR16  **FormattedString,
  OUT UINT16  *EachLineWidth,
  IN  UINTN   RowCount
  )
{
  UINTN   Index;
  CHAR16  *OutputString;
  UINTN   TotalRowNum;
  UINTN   CheckedNum;
  UINT16  GlyphWidth;
  UINT16  LineWidth;
  UINT16  MaxStringLen;
  UINT16  StringLen;

  TotalRowNum  = 0;
  CheckedNum   = 0;
  GlyphWidth   = 1;
  Index        = 0;
  MaxStringLen = 0;
  StringLen    = 0;

  //
  // Set default help string width.
  //
  LineWidth = (UINT16)(gHelpBlockWidth - 1);

  //
  // Get row number of the String.
  //
  while ((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
    if (StringLen > MaxStringLen) {
      MaxStringLen = StringLen;
    }

    TotalRowNum++;
    FreePool (OutputString);
  }

  *EachLineWidth = MaxStringLen;

  *FormattedString = AllocateZeroPool (TotalRowNum * MaxStringLen * sizeof (CHAR16));
  ASSERT (*FormattedString != NULL);

  //
  // Generate formatted help string array.
  //
  GlyphWidth = 1;
  Index      = 0;
  while ((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
    CopyMem (*FormattedString + CheckedNum * MaxStringLen, OutputString, StringLen * sizeof (CHAR16));
    CheckedNum++;
    FreePool (OutputString);
  }

  return TotalRowNum;
}
