/** @file
Implementation for handling user input from the User Interfaces.

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

**/

#include "FormDisplay.h"

/**
  Get maximum and minimum info from this opcode.

  @param  OpCode            Pointer to the current input opcode.
  @param  Minimum           The minimum size info for this opcode.
  @param  Maximum           The maximum size info for this opcode.

**/
VOID
GetFieldFromOp (
  IN   EFI_IFR_OP_HEADER       *OpCode,
  OUT  UINTN                   *Minimum,
  OUT  UINTN                   *Maximum
  )
{
  EFI_IFR_STRING    *StringOp;
  EFI_IFR_PASSWORD  *PasswordOp;
  if (OpCode->OpCode == EFI_IFR_STRING_OP) {
    StringOp = (EFI_IFR_STRING *) OpCode;
    *Minimum = StringOp->MinSize;
    *Maximum = StringOp->MaxSize;
  } else if (OpCode->OpCode == EFI_IFR_PASSWORD_OP) {
    PasswordOp = (EFI_IFR_PASSWORD *) OpCode;
    *Minimum = PasswordOp->MinSize;
    *Maximum = PasswordOp->MaxSize;
  } else {
    *Minimum = 0;
    *Maximum = 0;
  }
}

/**
  Get string or password input from user.

  @param  MenuOption        Pointer to the current input menu.
  @param  Prompt            The prompt string shown on popup window.
  @param  StringPtr         Old user input and destination for use input string.

  @retval EFI_SUCCESS       If string input is read successfully
  @retval EFI_DEVICE_ERROR  If operation fails

**/
EFI_STATUS
ReadString (
  IN     UI_MENU_OPTION              *MenuOption,
  IN     CHAR16                      *Prompt,
  IN OUT CHAR16                      *StringPtr
  )
{
  EFI_STATUS              Status;
  EFI_INPUT_KEY           Key;
  CHAR16                  NullCharacter;
  UINTN                   ScreenSize;
  CHAR16                  Space[2];
  CHAR16                  KeyPad[2];
  CHAR16                  *TempString;
  CHAR16                  *BufferedString;
  UINTN                   Index;
  UINTN                   Index2;
  UINTN                   Count;
  UINTN                   Start;
  UINTN                   Top;
  UINTN                   DimensionsWidth;
  UINTN                   DimensionsHeight;
  UINTN                   CurrentCursor;
  BOOLEAN                 CursorVisible;
  UINTN                   Minimum;
  UINTN                   Maximum;
  FORM_DISPLAY_ENGINE_STATEMENT  *Question;
  BOOLEAN                 IsPassword;
  UINTN                   MaxLen;

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

  NullCharacter    = CHAR_NULL;
  ScreenSize       = GetStringWidth (Prompt) / sizeof (CHAR16);
  Space[0]         = L' ';
  Space[1]         = CHAR_NULL;

  Question         = MenuOption->ThisTag;
  GetFieldFromOp(Question->OpCode, &Minimum, &Maximum);

  if (Question->OpCode->OpCode == EFI_IFR_PASSWORD_OP) {
    IsPassword = TRUE;
  } else {
    IsPassword = FALSE;
  }

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

  if (ScreenSize < (Maximum + 1)) {
    ScreenSize = Maximum + 1;
  }

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

  BufferedString = AllocateZeroPool (ScreenSize * 2);
  ASSERT (BufferedString);

  Start = (DimensionsWidth - ScreenSize - 2) / 2 + gStatementDimensions.LeftColumn + 1;
  Top   = ((DimensionsHeight - 6) / 2) + gStatementDimensions.TopRow - 1;

  //
  // Display prompt for string
  //
  // CreateDialog (NULL, "", Prompt, Space, "", NULL);
  CreateMultiStringPopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter);
  gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));

  CursorVisible = gST->ConOut->Mode->CursorVisible;
  gST->ConOut->EnableCursor (gST->ConOut, TRUE);

  CurrentCursor = GetStringWidth (StringPtr) / 2 - 1;
  if (CurrentCursor != 0) {
    //
    // Show the string which has beed saved before.
    //
    SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
    PrintStringAt (Start + 1, Top + 3, BufferedString);

    if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
      Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
    } else {
      Index = 0;
    }

    if (IsPassword) {
      gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
    }

    for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
      BufferedString[Count] = StringPtr[Index];

      if (IsPassword) {
        PrintCharAt ((UINTN)-1, (UINTN)-1, L'*');
      }
    }

    if (!IsPassword) {
      PrintStringAt (Start + 1, Top + 3, BufferedString);
    }

    gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
    gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3);
  }

  do {
    Status = WaitForKeyStroke (&Key);
    ASSERT_EFI_ERROR (Status);

    gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
    switch (Key.UnicodeChar) {
    case CHAR_NULL:
      switch (Key.ScanCode) {
      case SCAN_LEFT:
        if (CurrentCursor > 0) {
          CurrentCursor--;
        }
        break;

      case SCAN_RIGHT:
        if (CurrentCursor < (GetStringWidth (StringPtr) / 2 - 1)) {
          CurrentCursor++;
        }
        break;

      case SCAN_ESC:
        FreePool (TempString);
        FreePool (BufferedString);
        gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
        gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
        return EFI_DEVICE_ERROR;

       case SCAN_DELETE:
        for (Index = CurrentCursor; StringPtr[Index] != CHAR_NULL; Index++) {
          StringPtr[Index] = StringPtr[Index + 1];
          PrintCharAt (Start + Index + 1, Top + 3, IsPassword && StringPtr[Index] != CHAR_NULL? L'*' : StringPtr[Index]);
        }
        break;

      default:
        break;
      }

      break;

    case CHAR_CARRIAGE_RETURN:
      if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) {

        FreePool (TempString);
        FreePool (BufferedString);
        gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
        gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
        return EFI_SUCCESS;
      } else {
        //
        // Simply create a popup to tell the user that they had typed in too few characters.
        // To save code space, we can then treat this as an error and return back to the menu.
        //
        do {
          CreateDialog (&Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter, NULL);
        } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);

        FreePool (TempString);
        FreePool (BufferedString);
        gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
        gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
        return EFI_DEVICE_ERROR;
      }


    case CHAR_BACKSPACE:
      if (StringPtr[0] != CHAR_NULL && CurrentCursor != 0) {
        for (Index = 0; Index < CurrentCursor - 1; Index++) {
          TempString[Index] = StringPtr[Index];
        }
        Count = GetStringWidth (StringPtr) / 2 - 1;
        if (Count >= CurrentCursor) {
          for (Index = CurrentCursor - 1, Index2 = CurrentCursor; Index2 < Count; Index++, Index2++) {
            TempString[Index] = StringPtr[Index2];
          }
          TempString[Index] = CHAR_NULL;
        }
        //
        // Effectively truncate string by 1 character
        //
        StrCpyS (StringPtr, MaxLen, TempString);
        CurrentCursor --;
      }

    default:
      //
      // If it is the beginning of the string, don't worry about checking maximum limits
      //
      if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
        StrnCpyS (StringPtr, MaxLen, &Key.UnicodeChar, 1);
        CurrentCursor++;
      } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
        KeyPad[0] = Key.UnicodeChar;
        KeyPad[1] = CHAR_NULL;
        Count = GetStringWidth (StringPtr) / 2 - 1;
        if (CurrentCursor < Count) {
          for (Index = 0; Index < CurrentCursor; Index++) {
            TempString[Index] = StringPtr[Index];
          }
      TempString[Index] = CHAR_NULL;
          StrCatS (TempString, MaxLen, KeyPad);
          StrCatS (TempString, MaxLen, StringPtr + CurrentCursor);
          StrCpyS (StringPtr, MaxLen, TempString);
        } else {
          StrCatS (StringPtr, MaxLen, KeyPad);
        }
        CurrentCursor++;
      }

      //
      // If the width of the input string is now larger than the screen, we nee to
      // adjust the index to start printing portions of the string
      //
      SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
      PrintStringAt (Start + 1, Top + 3, BufferedString);

      if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
        Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
      } else {
        Index = 0;
      }

      if (IsPassword) {
        gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
      }

      for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
        BufferedString[Count] = StringPtr[Index];

        if (IsPassword) {
          PrintCharAt ((UINTN)-1, (UINTN)-1, L'*');
        }
      }

      if (!IsPassword) {
        PrintStringAt (Start + 1, Top + 3, BufferedString);
      }
      break;
    }

    gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
    gST->ConOut->SetCursorPosition (gST->ConOut, Start + CurrentCursor + 1, Top + 3);
  } while (TRUE);

}

/**
  Adjust the value to the correct one. Rules follow the sample:
  like:  Year change:  2012.02.29 -> 2013.02.29 -> 2013.02.01
         Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28

  @param  QuestionValue     Pointer to current question.
  @param  Sequence          The sequence of the field in the question.
**/
VOID
AdjustQuestionValue (
  IN  EFI_HII_VALUE           *QuestionValue,
  IN  UINT8                   Sequence
  )
{
  UINT8     Month;
  UINT16    Year;
  UINT8     Maximum;
  UINT8     Minimum;

  Month   = QuestionValue->Value.date.Month;
  Year    = QuestionValue->Value.date.Year;
  Minimum = 1;

  switch (Month) {
  case 2:
    if ((Year % 4) == 0 && ((Year % 100) != 0 || (Year % 400) == 0)) {
      Maximum = 29;
    } else {
      Maximum = 28;
    }
    break;
  case 4:
  case 6:
  case 9:
  case 11:
    Maximum = 30;
    break;
  default:
    Maximum = 31;
    break;
  }

  //
  // Change the month area.
  //
  if (Sequence == 0) {
    if (QuestionValue->Value.date.Day > Maximum) {
      QuestionValue->Value.date.Day = Maximum;
    }
  }

  //
  // Change the Year area.
  //
  if (Sequence == 2) {
    if (QuestionValue->Value.date.Day > Maximum) {
      QuestionValue->Value.date.Day = Minimum;
    }
  }
}

/**
  Get field info from numeric opcode.

  @param  OpCode            Pointer to the current input opcode.
  @param  IntInput          Whether question shows with EFI_IFR_DISPLAY_INT_DEC type.
  @param  QuestionValue     Input question value, with EFI_HII_VALUE type.
  @param  Value             Return question value, always return UINT64 type.
  @param  Minimum           The minimum size info for this opcode.
  @param  Maximum           The maximum size info for this opcode.
  @param  Step              The step size info for this opcode.
  @param  StorageWidth      The storage width info for this opcode.

**/
VOID
GetValueFromNum (
  IN  EFI_IFR_OP_HEADER     *OpCode,
  IN  BOOLEAN               IntInput,
  IN  EFI_HII_VALUE         *QuestionValue,
  OUT UINT64                *Value,
  OUT UINT64                *Minimum,
  OUT UINT64                *Maximum,
  OUT UINT64                *Step,
  OUT UINT16                *StorageWidth
)
{
  EFI_IFR_NUMERIC       *NumericOp;

  NumericOp = (EFI_IFR_NUMERIC *) OpCode;

  switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) {
  case EFI_IFR_NUMERIC_SIZE_1:
    if (IntInput) {
      *Minimum = (INT64) (INT8) NumericOp->data.u8.MinValue;
      *Maximum = (INT64) (INT8) NumericOp->data.u8.MaxValue;
      *Value   = (INT64) (INT8) QuestionValue->Value.u8;
    } else {
      *Minimum = NumericOp->data.u8.MinValue;
      *Maximum = NumericOp->data.u8.MaxValue;
      *Value   = QuestionValue->Value.u8;
    }
    *Step    = NumericOp->data.u8.Step;
    *StorageWidth = (UINT16) sizeof (UINT8);
    break;

  case EFI_IFR_NUMERIC_SIZE_2:
    if (IntInput) {
      *Minimum = (INT64) (INT16) NumericOp->data.u16.MinValue;
      *Maximum = (INT64) (INT16) NumericOp->data.u16.MaxValue;
      *Value   = (INT64) (INT16) QuestionValue->Value.u16;
    } else {
      *Minimum = NumericOp->data.u16.MinValue;
      *Maximum = NumericOp->data.u16.MaxValue;
      *Value   = QuestionValue->Value.u16;
    }
    *Step    = NumericOp->data.u16.Step;
    *StorageWidth = (UINT16) sizeof (UINT16);
    break;

  case EFI_IFR_NUMERIC_SIZE_4:
    if (IntInput) {
      *Minimum = (INT64) (INT32) NumericOp->data.u32.MinValue;
      *Maximum = (INT64) (INT32) NumericOp->data.u32.MaxValue;
      *Value   = (INT64) (INT32) QuestionValue->Value.u32;
    } else {
      *Minimum = NumericOp->data.u32.MinValue;
      *Maximum = NumericOp->data.u32.MaxValue;
      *Value   = QuestionValue->Value.u32;
    }
    *Step    = NumericOp->data.u32.Step;
    *StorageWidth = (UINT16) sizeof (UINT32);
    break;

  case EFI_IFR_NUMERIC_SIZE_8:
    if (IntInput) {
      *Minimum = (INT64) NumericOp->data.u64.MinValue;
      *Maximum = (INT64) NumericOp->data.u64.MaxValue;
      *Value   = (INT64) QuestionValue->Value.u64;
    } else {
      *Minimum = NumericOp->data.u64.MinValue;
      *Maximum = NumericOp->data.u64.MaxValue;
      *Value   = QuestionValue->Value.u64;
    }
    *Step    = NumericOp->data.u64.Step;
    *StorageWidth = (UINT16) sizeof (UINT64);
    break;

  default:
    break;
  }

  if (*Maximum == 0) {
    *Maximum = (UINT64) -1;
  }
}

/**
  This routine reads a numeric value from the user input.

  @param  MenuOption        Pointer to the current input menu.

  @retval EFI_SUCCESS       If numerical input is read successfully
  @retval EFI_DEVICE_ERROR  If operation fails

**/
EFI_STATUS
GetNumericInput (
  IN  UI_MENU_OPTION              *MenuOption
  )
{
  UINTN                   Column;
  UINTN                   Row;
  CHAR16                  InputText[MAX_NUMERIC_INPUT_WIDTH];
  CHAR16                  FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];
  UINT64                  PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];
  UINTN                   Count;
  UINTN                   Loop;
  BOOLEAN                 ManualInput;
  BOOLEAN                 HexInput;
  BOOLEAN                 IntInput;
  BOOLEAN                 Negative;
  BOOLEAN                 ValidateFail;
  BOOLEAN                 DateOrTime;
  UINTN                   InputWidth;
  UINT64                  EditValue;
  UINT64                  Step;
  UINT64                  Minimum;
  UINT64                  Maximum;
  UINTN                   EraseLen;
  UINT8                   Digital;
  EFI_INPUT_KEY           Key;
  EFI_HII_VALUE           *QuestionValue;
  FORM_DISPLAY_ENGINE_STATEMENT  *Question;
  EFI_IFR_NUMERIC                *NumericOp;
  UINT16                         StorageWidth;

  Column            = MenuOption->OptCol;
  Row               = MenuOption->Row;
  PreviousNumber[0] = 0;
  Count             = 0;
  InputWidth        = 0;
  Digital           = 0;
  StorageWidth      = 0;
  Minimum           = 0;
  Maximum           = 0;
  NumericOp         = NULL;
  IntInput          = FALSE;
  HexInput          = FALSE;
  Negative          = FALSE;
  ValidateFail      = FALSE;

  Question      = MenuOption->ThisTag;
  QuestionValue = &Question->CurrentValue;
  ZeroMem (InputText, MAX_NUMERIC_INPUT_WIDTH * sizeof (CHAR16));

  //
  // Only two case, user can enter to this function: Enter and +/- case.
  // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
  //
  ManualInput        = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);

  if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) || (Question->OpCode->OpCode == EFI_IFR_TIME_OP)) {
    DateOrTime = TRUE;
  } else {
    DateOrTime = FALSE;
  }

  //
  // Prepare Value to be edit
  //
  EraseLen = 0;
  EditValue = 0;
  if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
    Step = 1;
    Minimum = 1;

    switch (MenuOption->Sequence) {
    case 0:
      Maximum = 12;
      EraseLen = 4;
      EditValue = QuestionValue->Value.date.Month;
      break;

    case 1:
      switch (QuestionValue->Value.date.Month) {
      case 2:
        if ((QuestionValue->Value.date.Year % 4) == 0  &&
            ((QuestionValue->Value.date.Year % 100) != 0 ||
            (QuestionValue->Value.date.Year % 400) == 0)) {
          Maximum = 29;
        } else {
          Maximum = 28;
        }
        break;
      case 4:
      case 6:
      case 9:
      case 11:
        Maximum = 30;
        break;
      default:
        Maximum = 31;
        break;
      }

      EraseLen = 3;
      EditValue = QuestionValue->Value.date.Day;
      break;

    case 2:
      Maximum = 0xffff;
      EraseLen = 5;
      EditValue = QuestionValue->Value.date.Year;
      break;

    default:
      break;
    }
  } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
    Step = 1;
    Minimum = 0;

    switch (MenuOption->Sequence) {
    case 0:
      Maximum = 23;
      EraseLen = 4;
      EditValue = QuestionValue->Value.time.Hour;
      break;

    case 1:
      Maximum = 59;
      EraseLen = 3;
      EditValue = QuestionValue->Value.time.Minute;
      break;

    case 2:
      Maximum = 59;
      EraseLen = 3;
      EditValue = QuestionValue->Value.time.Second;
      break;

    default:
      break;
    }
  } else {
    ASSERT (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP);
    NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode;
    GetValueFromNum(Question->OpCode, (NumericOp->Flags & EFI_IFR_DISPLAY) == 0, QuestionValue, &EditValue, &Minimum, &Maximum, &Step, &StorageWidth);
    EraseLen  = gOptionBlockWidth;
  }

  if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL)) {
    if ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX){
      HexInput = TRUE;
    } else if ((NumericOp->Flags & EFI_IFR_DISPLAY) == 0){
      //
      // Display with EFI_IFR_DISPLAY_INT_DEC type. Support negative number.
      //
      IntInput = TRUE;
    }
  }

  //
  // Enter from "Enter" input, clear the old word showing.
  //
  if (ManualInput) {
    if (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) {
      if (HexInput) {
        InputWidth = StorageWidth * 2;
      } else {
        switch (StorageWidth) {
        case 1:
          InputWidth = 3;
          break;

        case 2:
          InputWidth = 5;
          break;

        case 4:
          InputWidth = 10;
          break;

        case 8:
          InputWidth = 20;
          break;

        default:
          InputWidth = 0;
          break;
        }

        if (IntInput) {
          //
          // Support an extra '-' for negative number.
          //
          InputWidth += 1;
        }
      }

      InputText[0] = LEFT_NUMERIC_DELIMITER;
      SetUnicodeMem (InputText + 1, InputWidth, L' ');
      ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);
      InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
      InputText[InputWidth + 2] = L'\0';

      PrintStringAt (Column, Row, InputText);
      Column++;
    }

    if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
      if (MenuOption->Sequence == 2) {
        InputWidth = 4;
      } else {
        InputWidth = 2;
      }

      if (MenuOption->Sequence == 0) {
        InputText[0] = LEFT_NUMERIC_DELIMITER;
        SetUnicodeMem (InputText + 1, InputWidth, L' ');
        InputText[InputWidth + 1] = DATE_SEPARATOR;
        InputText[InputWidth + 2] = L'\0';
      } else  if (MenuOption->Sequence == 1){
        SetUnicodeMem (InputText, InputWidth, L' ');
        InputText[InputWidth] = DATE_SEPARATOR;
        InputText[InputWidth + 1] = L'\0';
      } else {
        SetUnicodeMem (InputText, InputWidth, L' ');
        InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER;
        InputText[InputWidth + 1] = L'\0';
      }

      PrintStringAt (Column, Row, InputText);
      if (MenuOption->Sequence == 0) {
        Column++;
      }
    }

    if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
      InputWidth = 2;

      if (MenuOption->Sequence == 0) {
        InputText[0] = LEFT_NUMERIC_DELIMITER;
        SetUnicodeMem (InputText + 1, InputWidth, L' ');
        InputText[InputWidth + 1] = TIME_SEPARATOR;
        InputText[InputWidth + 2] = L'\0';
      } else if (MenuOption->Sequence == 1){
        SetUnicodeMem (InputText, InputWidth, L' ');
        InputText[InputWidth] = TIME_SEPARATOR;
        InputText[InputWidth + 1] = L'\0';
      } else {
        SetUnicodeMem (InputText, InputWidth, L' ');
        InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER;
        InputText[InputWidth + 1] = L'\0';
      }

      PrintStringAt (Column, Row, InputText);
      if (MenuOption->Sequence == 0) {
        Column++;
      }
    }
  }

  //
  // First time we enter this handler, we need to check to see if
  // we were passed an increment or decrement directive
  //
  do {
    Key.UnicodeChar = CHAR_NULL;
    if (gDirection != 0) {
      Key.ScanCode  = gDirection;
      gDirection    = 0;
      goto TheKey2;
    }

    WaitForKeyStroke (&Key);

TheKey2:
    switch (Key.UnicodeChar) {

    case '+':
    case '-':
      if (ManualInput && IntInput) {
        //
        // In Manual input mode, check whether input the negative flag.
        //
        if (Key.UnicodeChar == '-') {
          if (Negative) {
            break;
          }
          Negative = TRUE;
          PrintCharAt (Column++, Row, Key.UnicodeChar);
        }
      } else {
        if (Key.UnicodeChar == '+') {
          Key.ScanCode = SCAN_RIGHT;
        } else {
          Key.ScanCode = SCAN_LEFT;
        }
        Key.UnicodeChar = CHAR_NULL;
        goto TheKey2;
      }
      break;

    case CHAR_NULL:
      switch (Key.ScanCode) {
      case SCAN_LEFT:
      case SCAN_RIGHT:
        if (DateOrTime && !ManualInput) {
          //
          // By setting this value, we will return back to the caller.
          // We need to do this since an auto-refresh will destroy the adjustment
          // based on what the real-time-clock is showing.  So we always commit
          // upon changing the value.
          //
          gDirection = SCAN_DOWN;
        }

        if ((Step != 0) && !ManualInput) {
          if (Key.ScanCode == SCAN_LEFT) {
            if (IntInput) {
              if ((INT64) EditValue >= (INT64) Minimum + (INT64) Step) {
                EditValue = EditValue - Step;
              } else if ((INT64) EditValue > (INT64) Minimum){
                EditValue = Minimum;
              } else {
                EditValue = Maximum;
              }
            } else {
              if (EditValue >= Minimum + Step) {
                EditValue = EditValue - Step;
              } else if (EditValue > Minimum){
                EditValue = Minimum;
              } else {
                EditValue = Maximum;
              }
            }
          } else if (Key.ScanCode == SCAN_RIGHT) {
            if (IntInput) {
              if ((INT64) EditValue + (INT64) Step <= (INT64) Maximum) {
                EditValue = EditValue + Step;
              } else if ((INT64) EditValue < (INT64) Maximum) {
                EditValue = Maximum;
              } else {
                EditValue = Minimum;
              }
            } else {
              if (EditValue + Step <= Maximum) {
                EditValue = EditValue + Step;
              } else if (EditValue < Maximum) {
                EditValue = Maximum;
              } else {
                EditValue = Minimum;
              }
            }
          }

          ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
          if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
            if (MenuOption->Sequence == 2) {
              //
              // Year
              //
              UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);
            } else {
              //
              // Month/Day
              //
              UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
            }

            if (MenuOption->Sequence == 0) {
              ASSERT (EraseLen >= 2);
              FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
            } else if (MenuOption->Sequence == 1) {
              ASSERT (EraseLen >= 1);
              FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
            }
          } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
            UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);

            if (MenuOption->Sequence == 0) {
              ASSERT (EraseLen >= 2);
              FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
            } else if (MenuOption->Sequence == 1) {
              ASSERT (EraseLen >= 1);
              FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
            }
          } else {
            QuestionValue->Value.u64 = EditValue;
            PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
          }

          gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
          for (Loop = 0; Loop < EraseLen; Loop++) {
            PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
          }
          gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());

          if (MenuOption->Sequence == 0) {
            PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
            Column = MenuOption->OptCol + 1;
          }

          PrintStringAt (Column, Row, FormattedNumber);

          if (!DateOrTime || MenuOption->Sequence == 2) {
            PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER);
          }
        }

        goto EnterCarriageReturn;

      case SCAN_UP:
      case SCAN_DOWN:
        goto EnterCarriageReturn;

      case SCAN_ESC:
        return EFI_DEVICE_ERROR;

      default:
        break;
      }

      break;

EnterCarriageReturn:

    case CHAR_CARRIAGE_RETURN:
      //
      // Validate input value with Minimum value.
      //
      ValidateFail = FALSE;
      if (IntInput) {
        //
        // After user input Enter, need to check whether the input value.
        // If input a negative value, should compare with maximum value.
        // else compare with the minimum value.
        //
        if (Negative) {
          ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE;
        } else {
          ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE;
        }

        if (ValidateFail) {
          UpdateStatusBar (INPUT_ERROR, TRUE);
          break;
        }
      } else if (EditValue < Minimum) {
        UpdateStatusBar (INPUT_ERROR, TRUE);
        break;
      }

      UpdateStatusBar (INPUT_ERROR, FALSE);
      CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE));
      QuestionValue = &gUserInput->InputValue;
      //
      // Store Edit value back to Question
      //
      if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
        switch (MenuOption->Sequence) {
        case 0:
          QuestionValue->Value.date.Month = (UINT8) EditValue;
          break;

        case 1:
          QuestionValue->Value.date.Day = (UINT8) EditValue;
          break;

        case 2:
          QuestionValue->Value.date.Year = (UINT16) EditValue;
          break;

        default:
          break;
        }
      } else if (Question->OpCode->OpCode  == EFI_IFR_TIME_OP) {
        switch (MenuOption->Sequence) {
        case 0:
          QuestionValue->Value.time.Hour = (UINT8) EditValue;
          break;

        case 1:
          QuestionValue->Value.time.Minute = (UINT8) EditValue;
          break;

        case 2:
          QuestionValue->Value.time.Second = (UINT8) EditValue;
          break;

        default:
          break;
        }
      } else {
        //
        // Numeric
        //
        QuestionValue->Value.u64 = EditValue;
      }

      //
      // Adjust the value to the correct one.
      // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
      //              2013.03.29 -> 2013.02.29 -> 2013.02.28
      //
      if (Question->OpCode->OpCode  == EFI_IFR_DATE_OP &&
        (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {
        AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence);
      }

      return EFI_SUCCESS;

    case CHAR_BACKSPACE:
      if (ManualInput) {
        if (Count == 0) {
          if (Negative) {
            Negative = FALSE;
            Column--;
            PrintStringAt (Column, Row, L" ");
          }
          break;
        }
        //
        // Remove a character
        //
        EditValue = PreviousNumber[Count - 1];
        UpdateStatusBar (INPUT_ERROR,  FALSE);
        Count--;
        Column--;
        PrintStringAt (Column, Row, L" ");
      }
      break;

    default:
      if (ManualInput) {
        if (HexInput) {
          if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
            Digital = (UINT8) (Key.UnicodeChar - L'0');
          } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
            Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);
          } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
            Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);
          } else {
            UpdateStatusBar (INPUT_ERROR, TRUE);
            break;
          }
        } else {
          if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
            UpdateStatusBar (INPUT_ERROR, TRUE);
            break;
          }
        }

        //
        // If Count exceed input width, there is no way more is valid
        //
        if (Count >= InputWidth) {
          break;
        }
        //
        // Someone typed something valid!
        //
        if (Count != 0) {
          if (HexInput) {
            EditValue = LShiftU64 (EditValue, 4) + Digital;
          } else if (IntInput && Negative) {
            //
            // Save the negative number.
            //
            EditValue = ~(MultU64x32 (~(EditValue - 1), 10) + (Key.UnicodeChar - L'0')) + 1;
          } else {
            EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
          }
        } else {
          if (HexInput) {
            EditValue = Digital;
          } else if (IntInput && Negative) {
            //
            // Save the negative number.
            //
            EditValue = ~(Key.UnicodeChar - L'0') + 1;
          } else {
            EditValue = Key.UnicodeChar - L'0';
          }
        }

        if (IntInput) {
          ValidateFail = FALSE;
          //
          // When user input a new value, should check the current value.
          // If user input a negative value, should compare it with minimum
          // value, else compare it with maximum value.
          //
          if (Negative) {
            ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE;
          } else {
            ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE;
          }

          if (ValidateFail) {
            UpdateStatusBar (INPUT_ERROR, TRUE);
            ASSERT (Count < ARRAY_SIZE (PreviousNumber));
            EditValue = PreviousNumber[Count];
            break;
          }
        } else {
          if (EditValue > Maximum) {
            UpdateStatusBar (INPUT_ERROR, TRUE);
            ASSERT (Count < ARRAY_SIZE (PreviousNumber));
            EditValue = PreviousNumber[Count];
            break;
          }
        }

        UpdateStatusBar (INPUT_ERROR, FALSE);

        Count++;
        ASSERT (Count < (ARRAY_SIZE (PreviousNumber)));
        PreviousNumber[Count] = EditValue;

        gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
        PrintCharAt (Column, Row, Key.UnicodeChar);
        Column++;
      }
      break;
    }
  } while (TRUE);
}

/**
  Adjust option order base on the question value.

  @param  Question           Pointer to current question.
  @param  PopUpMenuLines     The line number of the pop up menu.

  @retval EFI_SUCCESS       If Option input is processed successfully
  @retval EFI_DEVICE_ERROR  If operation fails

**/
EFI_STATUS
AdjustOptionOrder (
  IN  FORM_DISPLAY_ENGINE_STATEMENT  *Question,
  OUT UINTN                          *PopUpMenuLines
  )
{
  UINTN                   Index;
  EFI_IFR_ORDERED_LIST    *OrderList;
  UINT8                   *ValueArray;
  UINT8                   ValueType;
  LIST_ENTRY              *Link;
  DISPLAY_QUESTION_OPTION *OneOfOption;
  EFI_HII_VALUE           *HiiValueArray;

  Link        = GetFirstNode (&Question->OptionListHead);
  OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
  ValueArray  = Question->CurrentValue.Buffer;
  ValueType   =  OneOfOption->OptionOpCode->Type;
  OrderList   = (EFI_IFR_ORDERED_LIST *) Question->OpCode;

  for (Index = 0; Index < OrderList->MaxContainers; Index++) {
    if (GetArrayData (ValueArray, ValueType, Index) == 0) {
      break;
    }
  }

  *PopUpMenuLines = Index;

  //
  // Prepare HiiValue array
  //
  HiiValueArray = AllocateZeroPool (*PopUpMenuLines * sizeof (EFI_HII_VALUE));
  ASSERT (HiiValueArray != NULL);

  for (Index = 0; Index < *PopUpMenuLines; Index++) {
    HiiValueArray[Index].Type = ValueType;
    HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);
  }

  for (Index = 0; Index < *PopUpMenuLines; Index++) {
    OneOfOption = ValueToOption (Question, &HiiValueArray[*PopUpMenuLines - Index - 1]);
    if (OneOfOption == NULL) {
      return EFI_NOT_FOUND;
    }

    RemoveEntryList (&OneOfOption->Link);

    //
    // Insert to head.
    //
    InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
  }

  FreePool (HiiValueArray);

  return EFI_SUCCESS;
}

/**
  Base on the type to compare the value.

  @param  Value1                The first value need to compare.
  @param  Value2                The second value need to compare.
  @param  Type                  The value type for above two values.

  @retval TRUE                  The two value are same.
  @retval FALSE                 The two value are different.

**/
BOOLEAN
IsValuesEqual (
  IN EFI_IFR_TYPE_VALUE *Value1,
  IN EFI_IFR_TYPE_VALUE *Value2,
  IN UINT8              Type
  )
{
  switch (Type) {
  case EFI_IFR_TYPE_BOOLEAN:
  case EFI_IFR_TYPE_NUM_SIZE_8:
    return (BOOLEAN) (Value1->u8 == Value2->u8);

  case EFI_IFR_TYPE_NUM_SIZE_16:
    return (BOOLEAN) (Value1->u16 == Value2->u16);

  case EFI_IFR_TYPE_NUM_SIZE_32:
    return (BOOLEAN) (Value1->u32 == Value2->u32);

  case EFI_IFR_TYPE_NUM_SIZE_64:
    return (BOOLEAN) (Value1->u64 == Value2->u64);

  default:
    ASSERT (FALSE);
    return FALSE;
  }
}

/**
  Base on the type to set the value.

  @param  Dest                  The dest value.
  @param  Source                The source value.
  @param  Type                  The value type for above two values.

**/
VOID
SetValuesByType (
  OUT EFI_IFR_TYPE_VALUE *Dest,
  IN  EFI_IFR_TYPE_VALUE *Source,
  IN  UINT8              Type
  )
{
  switch (Type) {
  case EFI_IFR_TYPE_BOOLEAN:
    Dest->b = Source->b;
    break;

  case EFI_IFR_TYPE_NUM_SIZE_8:
    Dest->u8 = Source->u8;
    break;

  case EFI_IFR_TYPE_NUM_SIZE_16:
    Dest->u16 = Source->u16;
    break;

  case EFI_IFR_TYPE_NUM_SIZE_32:
    Dest->u32 = Source->u32;
    break;

  case EFI_IFR_TYPE_NUM_SIZE_64:
    Dest->u64 = Source->u64;
    break;

  default:
    ASSERT (FALSE);
    break;
  }
}

/**
  Get selection for OneOf and OrderedList (Left/Right will be ignored).

  @param  MenuOption        Pointer to the current input menu.

  @retval EFI_SUCCESS       If Option input is processed successfully
  @retval EFI_DEVICE_ERROR  If operation fails

**/
EFI_STATUS
GetSelectionInputPopUp (
  IN  UI_MENU_OPTION              *MenuOption
  )
{
  EFI_INPUT_KEY           Key;
  UINTN                   Index;
  CHAR16                  *StringPtr;
  CHAR16                  *TempStringPtr;
  UINTN                   Index2;
  UINTN                   TopOptionIndex;
  UINTN                   HighlightOptionIndex;
  UINTN                   Start;
  UINTN                   End;
  UINTN                   Top;
  UINTN                   Bottom;
  UINTN                   PopUpMenuLines;
  UINTN                   MenuLinesInView;
  UINTN                   PopUpWidth;
  CHAR16                  Character;
  INT32                   SavedAttribute;
  BOOLEAN                 ShowDownArrow;
  BOOLEAN                 ShowUpArrow;
  UINTN                   DimensionsWidth;
  LIST_ENTRY              *Link;
  BOOLEAN                 OrderedList;
  UINT8                   *ValueArray;
  UINT8                   *ReturnValue;
  UINT8                   ValueType;
  EFI_HII_VALUE           HiiValue;
  DISPLAY_QUESTION_OPTION         *OneOfOption;
  DISPLAY_QUESTION_OPTION         *CurrentOption;
  FORM_DISPLAY_ENGINE_STATEMENT  *Question;
  INTN                    Result;
  EFI_IFR_ORDERED_LIST    *OrderList;

  DimensionsWidth   = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;

  ValueArray        = NULL;
  ValueType         = 0;
  CurrentOption     = NULL;
  ShowDownArrow     = FALSE;
  ShowUpArrow       = FALSE;

  ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE));

  Question = MenuOption->ThisTag;
  if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
    Link = GetFirstNode (&Question->OptionListHead);
    OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
    ValueArray = Question->CurrentValue.Buffer;
    ValueType =  OneOfOption->OptionOpCode->Type;
    OrderedList = TRUE;
    OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
  } else {
    OrderedList = FALSE;
    OrderList = NULL;
  }

  //
  // Calculate Option count
  //
  PopUpMenuLines = 0;
  if (OrderedList) {
    AdjustOptionOrder(Question, &PopUpMenuLines);
  } else {
    Link = GetFirstNode (&Question->OptionListHead);
    while (!IsNull (&Question->OptionListHead, Link)) {
      OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
      PopUpMenuLines++;
      Link = GetNextNode (&Question->OptionListHead, Link);
    }
  }

  //
  // Get the number of one of options present and its size
  //
  PopUpWidth = 0;
  HighlightOptionIndex = 0;
  Link = GetFirstNode (&Question->OptionListHead);
  for (Index = 0; Index < PopUpMenuLines; Index++) {
    OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);

    StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
    if (StrLen (StringPtr) > PopUpWidth) {
      PopUpWidth = StrLen (StringPtr);
    }
    FreePool (StringPtr);
    HiiValue.Type = OneOfOption->OptionOpCode->Type;
    SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type);
    if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
      //
      // Find current selected Option for OneOf
      //
      HighlightOptionIndex = Index;
    }

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

  //
  // Perform popup menu initialization.
  //
  PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;

  SavedAttribute = gST->ConOut->Mode->Attribute;
  gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());

  if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
    PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
  }

  Start  = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn;
  End    = Start + PopUpWidth + POPUP_FRAME_WIDTH;
  Top    = gStatementDimensions.TopRow;
  Bottom = gStatementDimensions.BottomRow - 1;

  MenuLinesInView = Bottom - Top - 1;
  if (MenuLinesInView >= PopUpMenuLines) {
    Top     = Top + (MenuLinesInView - PopUpMenuLines) / 2;
    Bottom  = Top + PopUpMenuLines + 1;
  } else {
    ShowDownArrow = TRUE;
  }

  if (HighlightOptionIndex > (MenuLinesInView - 1)) {
    TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
  } else {
    TopOptionIndex = 0;
  }

  do {
    //
    // Clear that portion of the screen
    //
    ClearLines (Start, End, Top, Bottom, GetPopupColor ());

    //
    // Draw "One of" pop-up menu
    //
    Character = BOXDRAW_DOWN_RIGHT;
    PrintCharAt (Start, Top, Character);
    for (Index = Start; Index + 2 < End; Index++) {
      if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
        Character = GEOMETRICSHAPE_UP_TRIANGLE;
      } else {
        Character = BOXDRAW_HORIZONTAL;
      }

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

    Character = BOXDRAW_DOWN_LEFT;
    PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
    Character = BOXDRAW_VERTICAL;
    for (Index = Top + 1; Index < Bottom; Index++) {
      PrintCharAt (Start, Index, Character);
      PrintCharAt (End - 1, Index, Character);
    }

    //
    // Move to top Option
    //
    Link = GetFirstNode (&Question->OptionListHead);
    for (Index = 0; Index < TopOptionIndex; Index++) {
      Link = GetNextNode (&Question->OptionListHead, Link);
    }

    //
    // Display the One of options
    //
    Index2 = Top + 1;
    for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
      OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
      Link = GetNextNode (&Question->OptionListHead, Link);

      StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
      ASSERT (StringPtr != NULL);
      //
      // If the string occupies multiple lines, truncate it to fit in one line,
      // and append a "..." for indication.
      //
      if (StrLen (StringPtr) > (PopUpWidth - 1)) {
        TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
        ASSERT ( TempStringPtr != NULL );
        CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
        FreePool (StringPtr);
        StringPtr = TempStringPtr;
        StrCatS (StringPtr, PopUpWidth - 1, L"...");
      }

      if (Index == HighlightOptionIndex) {
          //
          // Highlight the selected one
          //
          CurrentOption = OneOfOption;

          gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ());
          PrintStringAt (Start + 2, Index2, StringPtr);
          gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
        } else {
          gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
          PrintStringAt (Start + 2, Index2, StringPtr);
        }

      Index2++;
      FreePool (StringPtr);
    }

    Character = BOXDRAW_UP_RIGHT;
    PrintCharAt (Start, Bottom, Character);
    for (Index = Start; Index + 2 < End; Index++) {
      if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
        Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
      } else {
        Character = BOXDRAW_HORIZONTAL;
      }

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

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

    //
    // Get User selection
    //
    Key.UnicodeChar = CHAR_NULL;
    if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
      Key.ScanCode  = gDirection;
      gDirection    = 0;
      goto TheKey;
    }

    WaitForKeyStroke (&Key);

TheKey:
    switch (Key.UnicodeChar) {
    case '+':
      if (OrderedList) {
        if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
          //
          // Highlight reaches the top of the popup window, scroll one menu item.
          //
          TopOptionIndex--;
          ShowDownArrow = TRUE;
        }

        if (TopOptionIndex == 0) {
          ShowUpArrow = FALSE;
        }

        if (HighlightOptionIndex > 0) {
          HighlightOptionIndex--;

          ASSERT (CurrentOption != NULL);
          SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
        }
      }
      break;

    case '-':
      //
      // If an ordered list op-code, we will allow for a popup of +/- keys
      // to create an ordered list of items
      //
      if (OrderedList) {
        if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
            (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
          //
          // Highlight reaches the bottom of the popup window, scroll one menu item.
          //
          TopOptionIndex++;
          ShowUpArrow = TRUE;
        }

        if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
          ShowDownArrow = FALSE;
        }

        if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
          HighlightOptionIndex++;

          ASSERT (CurrentOption != NULL);
          SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
        }
      }
      break;

    case CHAR_NULL:
      switch (Key.ScanCode) {
      case SCAN_UP:
      case SCAN_DOWN:
        if (Key.ScanCode == SCAN_UP) {
          if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
            //
            // Highlight reaches the top of the popup window, scroll one menu item.
            //
            TopOptionIndex--;
            ShowDownArrow = TRUE;
          }

          if (TopOptionIndex == 0) {
            ShowUpArrow = FALSE;
          }

          if (HighlightOptionIndex > 0) {
            HighlightOptionIndex--;
          }
        } else {
          if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
              (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
            //
            // Highlight reaches the bottom of the popup window, scroll one menu item.
            //
            TopOptionIndex++;
            ShowUpArrow = TRUE;
          }

          if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
            ShowDownArrow = FALSE;
          }

          if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
            HighlightOptionIndex++;
          }
        }
        break;

      case SCAN_ESC:
        gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);

        //
        // Restore link list order for orderedlist
        //
        if (OrderedList) {
          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) {
              break;
            }

            OneOfOption = ValueToOption (Question, &HiiValue);
            if (OneOfOption == NULL) {
              return EFI_NOT_FOUND;
            }

            RemoveEntryList (&OneOfOption->Link);
            InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
          }
        }

        return EFI_DEVICE_ERROR;

      default:
        break;
      }

      break;

    case CHAR_CARRIAGE_RETURN:
      //
      // return the current selection
      //
      if (OrderedList) {
        ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen);
        ASSERT (ReturnValue != NULL);
        Index = 0;
        Link = GetFirstNode (&Question->OptionListHead);
        while (!IsNull (&Question->OptionListHead, Link)) {
          OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
          Link = GetNextNode (&Question->OptionListHead, Link);

          SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64);

          Index++;
          if (Index > OrderList->MaxContainers) {
            break;
          }
        }
        if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) {
          FreePool (ReturnValue);
          return EFI_DEVICE_ERROR;
        } else {
          gUserInput->InputValue.Buffer = ReturnValue;
          gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
        }
      } else {
        ASSERT (CurrentOption != NULL);
        gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type;
        if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) {
          return EFI_DEVICE_ERROR;
        } else {
          SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type);
        }
      }

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

      return EFI_SUCCESS;

    default:
      break;
    }
  } while (TRUE);

}

