/** @file
  FormDiplay protocol to show Form

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

**/

#ifndef __FORM_DISPLAY_H__
#define __FORM_DISPLAY_H__

#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/BaseLib.h>
#include <Library/HiiLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PrintLib.h>
#include <Library/CustomizedDisplayLib.h>

#include <Protocol/FormBrowserEx2.h>
#include <Protocol/SimpleTextIn.h>
#include <Protocol/DisplayProtocol.h>
#include <Protocol/HiiPopup.h>

#include <Guid/MdeModuleHii.h>

//
// This is the generated header file which includes whatever needs to be exported (strings + IFR)
//
extern UINT8                     DisplayEngineStrings[];
extern EFI_SCREEN_DESCRIPTOR     gStatementDimensions;
extern USER_INPUT                *gUserInput;
extern FORM_DISPLAY_ENGINE_FORM  *gFormData;
extern EFI_HII_HANDLE            gHiiHandle;
extern UINT16                    gDirection;
extern LIST_ENTRY                gMenuOption;
extern CHAR16                    *gConfirmOptYes;
extern CHAR16                    *gConfirmOptNo;
extern CHAR16                    *gConfirmOptOk;
extern CHAR16                    *gConfirmOptCancel;
extern CHAR16                    *gYesOption;
extern CHAR16                    *gNoOption;
extern CHAR16                    *gOkOption;
extern CHAR16                    *gCancelOption;
extern CHAR16                    *gErrorPopup;
extern CHAR16                    *gWarningPopup;
extern CHAR16                    *gInfoPopup;

//
// Browser Global Strings
//
extern CHAR16  *gSaveFailed;
extern CHAR16  *gPromptForData;
extern CHAR16  *gPromptForPassword;
extern CHAR16  *gPromptForNewPassword;
extern CHAR16  *gConfirmPassword;
extern CHAR16  *gConfirmError;
extern CHAR16  *gPassowordInvalid;
extern CHAR16  *gPressEnter;
extern CHAR16  *gEmptyString;
extern CHAR16  *gMiniString;
extern CHAR16  *gOptionMismatch;
extern CHAR16  *gFormSuppress;
extern CHAR16  *gProtocolNotFound;
extern CHAR16  *gPasswordUnsupported;

extern CHAR16   gPromptBlockWidth;
extern CHAR16   gOptionBlockWidth;
extern CHAR16   gHelpBlockWidth;
extern CHAR16   *mUnknownString;
extern BOOLEAN  gMisMatch;

//
// Screen definitions
//

#define LEFT_SKIPPED_COLUMNS   3
#define SCROLL_ARROW_HEIGHT    1
#define POPUP_PAD_SPACE_COUNT  5
#define POPUP_FRAME_WIDTH      2

#define UPPER_LOWER_CASE_OFFSET  0x20

//
// Display definitions
//
#define LEFT_ONEOF_DELIMITER   L'<'
#define RIGHT_ONEOF_DELIMITER  L'>'

#define LEFT_NUMERIC_DELIMITER   L'['
#define RIGHT_NUMERIC_DELIMITER  L']'

#define LEFT_CHECKBOX_DELIMITER   L'['
#define RIGHT_CHECKBOX_DELIMITER  L']'

#define CHECK_ON   L'X'
#define CHECK_OFF  L' '

#define TIME_SEPARATOR  L':'
#define DATE_SEPARATOR  L'/'

#define SUBTITLE_INDENT  2

//
// This is the Input Error Message
//
#define INPUT_ERROR  1

//
// This is the NV RAM update required Message
//
#define NV_UPDATE_REQUIRED  2
//
// Time definitions
//
#define ONE_SECOND  10000000

//
// It take 23 characters including the NULL to print a 64 bits number with "[" and "]".
// pow(2, 64) = [18446744073709551616]
// with extra '-' flat, set the width to 24.
//
#define MAX_NUMERIC_INPUT_WIDTH  24

#define EFI_HII_EXPRESSION_INCONSISTENT_IF  0
#define EFI_HII_EXPRESSION_NO_SUBMIT_IF     1
#define EFI_HII_EXPRESSION_GRAY_OUT_IF      2
#define EFI_HII_EXPRESSION_SUPPRESS_IF      3
#define EFI_HII_EXPRESSION_DISABLE_IF       4

//
// Character definitions
//
#define CHAR_SPACE  0x0020

#define FORM_DISPLAY_DRIVER_SIGNATURE  SIGNATURE_32 ('F', 'D', 'D', 'V')
typedef struct {
  UINT32                                Signature;

  EFI_HANDLE                            Handle;

  //
  // Produced protocol
  //
  EDKII_FORM_DISPLAY_ENGINE_PROTOCOL    FromDisplayProt;
  EFI_HII_POPUP_PROTOCOL                HiiPopup;
} FORM_DISPLAY_DRIVER_PRIVATE_DATA;

typedef enum {
  UiNoOperation,
  UiSelect,
  UiUp,
  UiDown,
  UiLeft,
  UiRight,
  UiReset,
  UiPrevious,
  UiPageUp,
  UiPageDown,
  UiHotKey,
  UiMaxOperation
} UI_SCREEN_OPERATION;

typedef enum {
  CfInitialization,
  CfCheckSelection,
  CfRepaint,
  CfRefreshHighLight,
  CfUpdateHelpString,
  CfPrepareToReadKey,
  CfReadKey,
  CfScreenOperation,
  CfUiSelect,
  CfUiReset,
  CfUiLeft,
  CfUiRight,
  CfUiUp,
  CfUiPageUp,
  CfUiPageDown,
  CfUiDown,
  CfUiNoOperation,
  CfExit,
  CfUiHotKey,
  CfMaxControlFlag
} UI_CONTROL_FLAG;

typedef enum {
  UIEventNone,
  UIEventKey,
  UIEventTimeOut,
  UIEventDriver
} UI_EVENT_TYPE;

typedef struct {
  UINT16                 ScanCode;
  UI_SCREEN_OPERATION    ScreenOperation;
} SCAN_CODE_TO_SCREEN_OPERATION;

typedef struct {
  UI_SCREEN_OPERATION    ScreenOperation;
  UI_CONTROL_FLAG        ControlFlag;
} SCREEN_OPERATION_T0_CONTROL_FLAG;

typedef struct {
  EFI_HII_HANDLE    HiiHandle;
  UINT16            FormId;

  //
  // Info for the highlight question.
  // HLT means highlight
  //
  // If one statement has questionid, save questionid info to find the question.
  // If one statement not has questionid info, save the opcode info to find the
  // statement. If more than one statement has same opcode in one form(just like
  // empty subtitle info may has more than one info one form), also use Index
  // info to find the statement.
  //
  EFI_QUESTION_ID      HLTQuestionId;
  EFI_IFR_OP_HEADER    *HLTOpCode;
  UINTN                HLTIndex;
  UINTN                HLTSequence;

  //
  // Info for the top of screen question.
  // TOS means Top Of Screen
  //
  EFI_QUESTION_ID      TOSQuestionId;
  EFI_IFR_OP_HEADER    *TOSOpCode;
  UINTN                TOSIndex;

  UINT16               SkipValue;
} DISPLAY_HIGHLIGHT_MENU_INFO;

typedef struct {
  EFI_EVENT    SyncEvent;
  UINT8        *TimeOut;
  CHAR16       *ErrorInfo;
} WARNING_IF_CONTEXT;

#define UI_MENU_OPTION_SIGNATURE  SIGNATURE_32 ('u', 'i', 'm', 'm')

typedef struct {
  UINTN                            Signature;
  LIST_ENTRY                       Link;

  EFI_HII_HANDLE                   Handle;
  FORM_DISPLAY_ENGINE_STATEMENT    *ThisTag;
  UINT16                           EntryNumber;

  UINTN                            Row;
  UINTN                            Col;
  UINTN                            OptCol;
  CHAR16                           *Description;
  UINTN                            Skip;  // Number of lines

  //
  // Display item sequence for date/time
  //  Date:      Month/Day/Year
  //  Sequence:  0     1   2
  //
  //  Time:      Hour : Minute : Second
  //  Sequence:  0      1        2
  //
  //
  UINTN      Sequence;

  BOOLEAN    GrayOut;
  BOOLEAN    ReadOnly;

  //
  // Whether user could change value of this item
  //
  BOOLEAN    IsQuestion;
  BOOLEAN    NestInStatement;
} UI_MENU_OPTION;

#define MENU_OPTION_FROM_LINK(a)  CR (a, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE)

#define USER_SELECTABLE_OPTION_OK_WIDTH          StrLen (gOkOption)
#define USER_SELECTABLE_OPTION_OK_CAL_WIDTH      (StrLen (gOkOption) + StrLen (gCancelOption))
#define USER_SELECTABLE_OPTION_YES_NO_WIDTH      (StrLen (gYesOption) + StrLen (gNoOption))
#define USER_SELECTABLE_OPTION_YES_NO_CAL_WIDTH  (StrLen (gYesOption) + StrLen (gNoOption) + StrLen (gCancelOption))

#define USER_SELECTABLE_OPTION_SKIP_WIDTH  2

//
// +-------------------------------------------+ // POPUP_BORDER                        }
// |            ERROR/WARNING/INFO             | // POPUP_STYLE_STRING_HEIGHT           } POPUP_HEADER_HEIGHT
// |-------------------------------------------| // POPUP_EMPTY_LINE_HEIGHT             }
// |             popup messages                |
// |                                           | // POPUP_EMPTY_LINE_HEIGHT             }
// |         user selectable options           | // POPUP_USER_SELECTABLE_OPTION_HEIGHT } POPUP_FOOTER_HEIGHT
// +-------------------------------------------+ // POPUP_BORDER                        }
//
#define POPUP_BORDER                         1
#define POPUP_EMPTY_LINE_HEIGHT              1
#define POPUP_STYLE_STRING_HEIGHT            1
#define POPUP_USER_SELECTABLE_OPTION_HEIGHT  1

#define POPUP_HEADER_HEIGHT  (POPUP_BORDER + POPUP_STYLE_STRING_HEIGHT + POPUP_EMPTY_LINE_HEIGHT)
#define POPUP_FOOTER_HEIGHT  (POPUP_EMPTY_LINE_HEIGHT + POPUP_USER_SELECTABLE_OPTION_HEIGHT + POPUP_BORDER)

#define USER_SELECTABLE_OPTION_SIGNATURE  SIGNATURE_32 ('u', 's', 's', 'o')

typedef struct {
  UINTN                      Signature;
  LIST_ENTRY                 Link;
  EFI_HII_POPUP_SELECTION    OptionType;
  CHAR16                     *OptionString;
  //
  // Display item sequence for user select options
  //  Ok:        Ok
  //  Sequence:  0
  //
  //  Ok/Cancel:   Ok : Cancel
  //  Sequence:    0      1
  //
  //  Yes/No:      Yes : No
  //  Sequence:     0    1
  //
  //  Yes/No/Cancel: Yes : No: Cancel
  //  Sequence:       0    1    2
  //
  UINTN    Sequence;
  UINTN    OptionRow;
  UINTN    OptionCol;
  UINTN    MaxSequence;
  UINTN    MinSequence;
} USER_SELECTABLE_OPTION;

#define SELECTABLE_OPTION_FROM_LINK(a)  CR (a, USER_SELECTABLE_OPTION, Link, USER_SELECTABLE_OPTION_SIGNATURE)

/**
  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
  );

/**
  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
  );

/**
  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
  );

/**
  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
  );

/**
  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
  );

/**
  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,
  ...
  );

/**
  Will copy LineWidth amount of a string in the OutputString buffer and return the
  number of CHAR16 characters that were copied into the OutputString buffer.
  The output string format is:
    Glyph Info + String info + '\0'.

  In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g.

  @param  InputString            String description for this option.
  @param  LineWidth              Width of the desired string to extract in CHAR16
                                 characters
  @param  GlyphWidth             The glyph width of the begin of the char in the string.
  @param  Index                  Where in InputString to start the copy process
  @param  OutputString           Buffer to copy the string into

  @return Returns the number of CHAR16 characters that were copied into the OutputString
  buffer, include extra glyph info and '\0' info.

**/
UINT16
GetLineByWidth (
  IN      CHAR16  *InputString,
  IN      UINT16  LineWidth,
  IN OUT  UINT16  *GlyphWidth,
  IN OUT  UINTN   *Index,
  OUT     CHAR16  **OutputString
  );

/**
  Get the string based on the StringId and HII Package List Handle.

  @param  Token                  The String's ID.
  @param  HiiHandle              The Hii handle for this string package.

  @return The output string.

**/
CHAR16 *
GetToken (
  IN  EFI_STRING_ID   Token,
  IN  EFI_HII_HANDLE  HiiHandle
  );

/**
  Count the storage space of a Unicode string.

  This function handles the Unicode string with NARROW_CHAR
  and WIDE_CHAR control characters. NARROW_HCAR and WIDE_CHAR
  does not count in the resultant output. If a WIDE_CHAR is
  hit, then 2 Unicode character will consume an output storage
  space with size of CHAR16 till a NARROW_CHAR is hit.

  If String is NULL, then ASSERT ().

  @param String          The input string to be counted.

  @return Storage space for the input string.

**/
UINTN
GetStringWidth (
  IN CHAR16  *String
  );

/**
  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
  );

/**
  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
  );

/**
  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
  );

/**
  Wait for a key to be pressed by user.

  @param Key         The key which is pressed by user.

  @retval EFI_SUCCESS The function always completed successfully.

**/
EFI_STATUS
WaitForKeyStroke (
  OUT  EFI_INPUT_KEY  *Key
  );

/**
  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
  );

/**
  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
  );

/**
  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
  );

/**
  Set Buffer to Value for Size bytes.

  @param  Buffer                 Memory to set.
  @param  Size                   Number of bytes to set
  @param  Value                  Value of the set operation.

**/
VOID
SetUnicodeMem (
  IN VOID    *Buffer,
  IN UINTN   Size,
  IN CHAR16  Value
  );

/**
  Display one form, and return user input.

  @param FormData                Form Data to be shown.
  @param UserInputData           User input data.

  @retval EFI_SUCCESS            Form Data is shown, and user input is got.
**/
EFI_STATUS
EFIAPI
FormDisplay (
  IN  FORM_DISPLAY_ENGINE_FORM  *FormData,
  OUT USER_INPUT                *UserInputData
  );

/**
  Clear Screen to the initial state.
**/
VOID
EFIAPI
DriverClearDisplayPage (
  VOID
  );

/**
  Exit Display and Clear Screen to the original state.

**/
VOID
EFIAPI
ExitDisplay (
  VOID
  );

/**
  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
  );

/**
  Record the highlight menu and top of screen menu info.

  @param  Highlight               The menu opton which is highlight.
  @param  TopOfScreen             The menu opton which is at the top of the form.
  @param  SkipValue               The skip line info for the top of screen menu.

**/
VOID
UpdateHighlightMenuInfo (
  IN  LIST_ENTRY  *Highlight,
  IN  LIST_ENTRY  *TopOfScreen,
  IN  UINTN       SkipValue
  );

/**
  Displays a popup window.

  @param  This           A pointer to the EFI_HII_POPUP_PROTOCOL instance.
  @param  PopupStyle     Popup style to use.
  @param  PopupType      Type of the popup to display.
  @param  HiiHandle      HII handle of the string pack containing Message
  @param  Message        A message to display in the popup box.
  @param  UserSelection  User selection.

  @retval EFI_SUCCESS            The popup box was successfully displayed.
  @retval EFI_INVALID_PARAMETER  HiiHandle and Message do not define a valid HII string.
  @retval EFI_INVALID_PARAMETER  PopupType is not one of the values defined by this specification.
  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to display the popup box.

**/
EFI_STATUS
EFIAPI
CreatePopup (
  IN  EFI_HII_POPUP_PROTOCOL   *This,
  IN  EFI_HII_POPUP_STYLE      PopupStyle,
  IN  EFI_HII_POPUP_TYPE       PopupType,
  IN  EFI_HII_HANDLE           HiiHandle,
  IN  EFI_STRING_ID            Message,
  OUT EFI_HII_POPUP_SELECTION  *UserSelection OPTIONAL
  );

#endif
