/** @file
  This library class defines a set of interfaces to customize Ui module
Copyright (c) 2016, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "FrontPageCustomizedUiSupport.h"
//
// This is the VFR compiler generated header file which defines the
// string identifiers.
//
#define PRINTABLE_LANGUAGE_NAME_STRING_ID     0x0001
#define UI_HII_DRIVER_LIST_SIZE               0x8
#define FRONT_PAGE_KEY_CONTINUE               0x1000
#define FRONT_PAGE_KEY_DRIVER                 0x2000
typedef struct {
  EFI_STRING_ID   PromptId;
  EFI_STRING_ID   HelpId;
  EFI_STRING_ID   DevicePathId;
  EFI_GUID        FormSetGuid;
  BOOLEAN         EmptyLineAfter;
} UI_HII_DRIVER_INSTANCE;
UI_HII_DRIVER_INSTANCE       *gHiiDriverList;
extern EFI_HII_HANDLE        gStringPackHandle;
/**
  This function processes the results of changes in configuration.
  @param HiiHandle       Points to the hii handle for this formset.
  @param Action          Specifies the type of action taken by the browser.
  @param QuestionId      A unique value which is sent to the original exporting driver
                         so that it can identify the type of data to expect.
  @param Type            The type of value for the question.
  @param Value           A pointer to the data being sent to the original exporting driver.
  @param ActionRequest   On return, points to the action requested by the callback function.
  @param Status          Return the handle status.
  @retval  TRUE          The callback successfully handled the action.
  @retval  FALSE         The callback not supported in this handler.
**/
BOOLEAN
UiSupportLibCallbackHandler (
  IN  EFI_HII_HANDLE                         HiiHandle,
  IN  EFI_BROWSER_ACTION                     Action,
  IN  EFI_QUESTION_ID                        QuestionId,
  IN  UINT8                                  Type,
  IN  EFI_IFR_TYPE_VALUE                     *Value,
  OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest,
  OUT EFI_STATUS                             *Status
  )
{
  if (QuestionId != FRONT_PAGE_KEY_CONTINUE) {
    return FALSE;
  }
  if (Action == EFI_BROWSER_ACTION_RETRIEVE) {
    *Status = EFI_UNSUPPORTED;
    return TRUE;
  }
  if (Action != EFI_BROWSER_ACTION_CHANGED) {
    //
    // Do nothing for other UEFI Action. Only do call back when data is changed.
    //
    *Status = EFI_UNSUPPORTED;
    return TRUE;
  }
  if (Action == EFI_BROWSER_ACTION_CHANGED) {
    if ((Value == NULL) || (ActionRequest == NULL)) {
      *Status = EFI_INVALID_PARAMETER;
      return TRUE;
    }
    *Status = EFI_SUCCESS;
    switch (QuestionId) {
    case FRONT_PAGE_KEY_CONTINUE:
      //
      // This is the continue - clear the screen and return an error to get out of FrontPage loop
      //
      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
      break;
    default:
      break;
    }
  }
  return TRUE;
}
/**
  Create continue menu in the front page.
  @param[in]    HiiHandle           The hii handle for the Uiapp driver.
  @param[in]    StartOpCodeHandle   The opcode handle to save the new opcode.
**/
VOID
UiCreateContinueMenu (
  IN EFI_HII_HANDLE              HiiHandle,
  IN VOID                        *StartOpCodeHandle
  )
{
  HiiCreateActionOpCode (
    StartOpCodeHandle,
    FRONT_PAGE_KEY_CONTINUE,
    STRING_TOKEN (STR_BOOT_DEFAULT_PROMPT),
    STRING_TOKEN (STR_BOOT_DEFAULT_HELP),
    EFI_IFR_FLAG_CALLBACK,
    0
    );
}
/**
  Create empty line menu in the front page.
  @param    HiiHandle           The hii handle for the Uiapp driver.
  @param    StartOpCodeHandle   The opcode handle to save the new opcode.
**/
VOID
UiCreateEmptyLine (
  IN EFI_HII_HANDLE              HiiHandle,
  IN VOID                        *StartOpCodeHandle
  )
{
  HiiCreateSubTitleOpCode (StartOpCodeHandle, STRING_TOKEN (STR_EMPTY_STRING), 0, 0, 0);
}
/**
  Extract device path for given HII handle and class guid.
  @param Handle          The HII handle.
  @retval  NULL          Fail to get the device path string.
  @return  PathString    Get the device path string.
**/
CHAR16 *
ExtractDevicePathFromHiiHandle (
  IN      EFI_HII_HANDLE      Handle
  )
{
  EFI_STATUS                       Status;
  EFI_HANDLE                       DriverHandle;
  ASSERT (Handle != NULL);
  if (Handle == NULL) {
    return NULL;
  }
  Status = gHiiDatabase->GetPackageListHandle (gHiiDatabase, Handle, &DriverHandle);
  if (EFI_ERROR (Status)) {
    return NULL;
  }
  return ConvertDevicePathToText(DevicePathFromHandle (DriverHandle), FALSE, FALSE);
}
/**
  Check whether this driver need to be shown in the front page.
  @param    HiiHandle           The hii handle for the driver.
  @param    Guid                The special guid for the driver which is the target.
  @param    PromptId            Return the prompt string id.
  @param    HelpId              Return the help string id.
  @param    FormsetGuid         Return the formset guid info.
  @retval   EFI_SUCCESS         Search the driver success
**/
BOOLEAN
RequiredDriver (
  IN  EFI_HII_HANDLE              HiiHandle,
  IN  EFI_GUID                    *Guid,
  OUT EFI_STRING_ID               *PromptId,
  OUT EFI_STRING_ID               *HelpId,
  OUT VOID                        *FormsetGuid
  )
{
  EFI_STATUS                  Status;
  UINT8                       ClassGuidNum;
  EFI_GUID                    *ClassGuid;
  EFI_IFR_FORM_SET            *Buffer;
  UINTN                       BufferSize;
  UINT8                       *Ptr;
  UINTN                       TempSize;
  BOOLEAN                     RetVal;
  Status = HiiGetFormSetFromHiiHandle(HiiHandle, &Buffer,&BufferSize);
  if (EFI_ERROR (Status)) {
    return FALSE;
  }
  RetVal = FALSE;
  TempSize = 0;
  Ptr = (UINT8 *) Buffer;
  while(TempSize < BufferSize)  {
    TempSize += ((EFI_IFR_OP_HEADER *) Ptr)->Length;
    if (((EFI_IFR_OP_HEADER *) Ptr)->Length <= OFFSET_OF (EFI_IFR_FORM_SET, Flags)){
      Ptr += ((EFI_IFR_OP_HEADER *) Ptr)->Length;
      continue;
    }
    ClassGuidNum = (UINT8) (((EFI_IFR_FORM_SET *)Ptr)->Flags & 0x3);
    ClassGuid = (EFI_GUID *) (VOID *)(Ptr + sizeof (EFI_IFR_FORM_SET));
    while (ClassGuidNum-- > 0) {
      if (!CompareGuid (Guid, ClassGuid)){
        ClassGuid ++;
        continue;
      }
      *PromptId = ((EFI_IFR_FORM_SET *)Ptr)->FormSetTitle;
      *HelpId = ((EFI_IFR_FORM_SET *)Ptr)->Help;
      CopyMem (FormsetGuid, &((EFI_IFR_FORM_SET *) Ptr)->Guid, sizeof (EFI_GUID));
      RetVal = TRUE;
    }
  }
  FreePool (Buffer);
  return RetVal;
}
/**
  Search the drivers in the system which need to show in the front page
  and insert the menu to the front page.
  @param    HiiHandle           The hii handle for the Uiapp driver.
  @param    ClassGuid           The class guid for the driver which is the target.
  @param    SpecialHandlerFn    The pointer to the special handler function, if any.
  @param    StartOpCodeHandle   The opcode handle to save the new opcode.
  @retval   EFI_SUCCESS         Search the driver success
**/
EFI_STATUS
UiListThirdPartyDrivers (
  IN EFI_HII_HANDLE              HiiHandle,
  IN EFI_GUID                    *ClassGuid,
  IN DRIVER_SPECIAL_HANDLER      SpecialHandlerFn,
  IN VOID                        *StartOpCodeHandle
  )
{
  UINTN                       Index;
  EFI_STRING                  String;
  EFI_STRING_ID               Token;
  EFI_STRING_ID               TokenHelp;
  EFI_HII_HANDLE              *HiiHandles;
  CHAR16                      *DevicePathStr;
  UINTN                       Count;
  UINTN                       CurrentSize;
  UI_HII_DRIVER_INSTANCE      *DriverListPtr;
  EFI_STRING                  NewName;
  BOOLEAN                     EmptyLineAfter;
  if (gHiiDriverList != NULL) {
    FreePool (gHiiDriverList);
  }
  HiiHandles = HiiGetHiiHandles (NULL);
  ASSERT (HiiHandles != NULL);
  gHiiDriverList = AllocateZeroPool (UI_HII_DRIVER_LIST_SIZE * sizeof (UI_HII_DRIVER_INSTANCE));
  ASSERT (gHiiDriverList != NULL);
  DriverListPtr = gHiiDriverList;
  CurrentSize = UI_HII_DRIVER_LIST_SIZE;
  for (Index = 0, Count = 0; HiiHandles[Index] != NULL; Index++) {
    if (!RequiredDriver (HiiHandles[Index], ClassGuid, &Token, &TokenHelp, &gHiiDriverList[Count].FormSetGuid)) {
      continue;
    }
    String = HiiGetString (HiiHandles[Index], Token, NULL);
    if (String == NULL) {
      String = HiiGetString (gStringPackHandle, STRING_TOKEN (STR_MISSING_STRING), NULL);
      ASSERT (String != NULL);
    } else if (SpecialHandlerFn != NULL) {
      //
      // Check whether need to rename the driver name.
      //
      EmptyLineAfter = FALSE;
      if (SpecialHandlerFn (String, &NewName, &EmptyLineAfter)) {
        FreePool (String);
        String = NewName;
        DriverListPtr[Count].EmptyLineAfter = EmptyLineAfter;
      }
    }
    DriverListPtr[Count].PromptId = HiiSetString (HiiHandle, 0, String, NULL);
    FreePool (String);
    String = HiiGetString (HiiHandles[Index], TokenHelp, NULL);
    if (String == NULL) {
      String = HiiGetString (gStringPackHandle, STRING_TOKEN (STR_MISSING_STRING), NULL);
      ASSERT (String != NULL);
    }
    DriverListPtr[Count].HelpId = HiiSetString (HiiHandle, 0, String, NULL);
    FreePool (String);
    DevicePathStr = ExtractDevicePathFromHiiHandle(HiiHandles[Index]);
    if (DevicePathStr != NULL){
      DriverListPtr[Count].DevicePathId = HiiSetString (HiiHandle, 0, DevicePathStr, NULL);
      FreePool (DevicePathStr);
    } else {
      DriverListPtr[Count].DevicePathId = 0;
    }
    Count++;
    if (Count >= CurrentSize) {
      DriverListPtr = ReallocatePool (
                        CurrentSize * sizeof (UI_HII_DRIVER_INSTANCE),
                        (Count + UI_HII_DRIVER_LIST_SIZE)
                          * sizeof (UI_HII_DRIVER_INSTANCE),
                        gHiiDriverList
                        );
      ASSERT (DriverListPtr != NULL);
      gHiiDriverList = DriverListPtr;
      CurrentSize += UI_HII_DRIVER_LIST_SIZE;
    }
  }
  FreePool (HiiHandles);
  Index = 0;
  while (gHiiDriverList[Index].PromptId != 0) {
    HiiCreateGotoExOpCode (
      StartOpCodeHandle,
      0,
      gHiiDriverList[Index].PromptId,
      gHiiDriverList[Index].HelpId,
      0,
      (EFI_QUESTION_ID) (Index + FRONT_PAGE_KEY_DRIVER),
      0,
      &gHiiDriverList[Index].FormSetGuid,
      gHiiDriverList[Index].DevicePathId
    );
    if (gHiiDriverList[Index].EmptyLineAfter) {
      UiCreateEmptyLine (HiiHandle, StartOpCodeHandle);
    }
    Index ++;
  }
  return EFI_SUCCESS;
}