// Displays items in the grid. Allows the user
// to sort the entries, filter their search, and search for a specific person.
// Authors:
// Cameron Hayes
// Mac Stephens

unit View.Items;

interface

uses
  System.SysUtils, System.Classes, Web, WEBLib.Graphics, WEBLib.Forms, WEBLib.Dialogs,
  Vcl.Controls, Vcl.StdCtrls, WEBLib.StdCtrls, WEBLib.Controls, WEBLib.Grids, WebLib.Lists,
  XData.Web.Client, WEBLib.ExtCtrls, DB, XData.Web.JsonDataset,
  XData.Web.Dataset, XData.Web.Connection, Vcl.Forms, DateUtils, WebAudio;

type
  TFViewItems = class(TWebForm)
    XDataWebClient1: TXDataWebClient;
    XDataWebDataSet1: TXDataWebDataSet;
    lblEntries: TWebLabel;
    wcbPageSize: TWebComboBox;
    btnAdd: TWebButton;
    edtName: TWebEdit;
    edtDescription: TWebEdit;
    cbStatus: TWebCheckBox;
    pnlMessage: TWebPanel;
    lblMessage: TWebLabel;
    btnCloseNotification: TWebButton;
    btnSave: TWebButton;
    btnCancel: TWebButton;
    btnDelete: TWebButton;
    btnEdit: TWebButton;
    procedure WebFormCreate(Sender: TObject);
    procedure btnAddClick(Sender: TObject);
    procedure wcbPageSizeChange(Sender: TObject);
    procedure btnCloseNotificationClick(Sender: TObject);
    procedure btnEditClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure btnDeleteClick(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);
  private
    FChildForm: TWebForm;
    procedure AddRowToTable(ID, Name, Description, Status: string);
    procedure ClearTable();
    procedure GeneratePagination(TotalPages: Integer);
    function GenerateSearchOptions(): string;
    procedure HideNotification();
    procedure ShowNotification(Notification: string);
    procedure ViewMode();
    procedure EditMode();
    [async] procedure GetItems(searchOptions: string);
    [async] procedure AddItem(itemOptions: string);
    var
      PageNumber: integer;
      PageSize: integer;
      TotalPages: integer;
      StartDate: string;
      EndDate: string;
      OrderBy: string;
      Caller: string;
  public

  end;

var
  FViewItems: TFViewItems;

implementation

uses
  JS, XData.Model.Classes,
  ConnectionModule, Auth.Service, Utils, View.AddItem, View.Main;

{$R *.dfm}

procedure TFViewItems.WebFormCreate(Sender: TObject);
// Initializes important values:
// PageNumber: What page number the user is on IE 1: 1-10, 2: 11-20 etc
// TotalPages: Total number of pages returned from the search.
// PageSize: Number of entries per page.
begin
  DMConnection.ApiConnection.Connected := True;
  PageNumber := 1;
  TotalPages := 1; // Initial total pages
  wcbPageSize.Text := '10';
  getItems(GenerateSearchOptions());
end;

procedure TFViewItems.ViewMode();
begin
  btnAdd.Enabled := true;
  btnDelete.Enabled := true;
  btnSave.Enabled := false;
  btnCancel.Enabled := false;
  btnEdit.Enabled := true;
  FViewMain.change := false;

  edtName.Enabled := false;
  edtDescription.enabled := false;
  cbStatus.Checked := false;
end;

procedure TFViewItems.EditMode();
begin
  FViewMain.change := true;
  btnAdd.Enabled := false;
  btnDelete.Enabled := false;
  btnSave.Enabled := true;
  btnCancel.Enabled := True;
  btnEdit.Enabled := false;

  edtName.Enabled := true;
  edtDescription.Enabled := true;
  cbStatus.enabled := true;
end;


procedure TFViewItems.AddRowToTable(ID, Name, Description, Status: string);
// Adds one row to #tblPhoneGrid and lets Bootstrap 5.3 highlight the row
// with its built-in `table-active` class when the user clicks it.
var
  NewRow, Cell: TJSHTMLElement;
begin
  NewRow := TJSHTMLElement(document.createElement('tr'));

  // Row-select click handler
  NewRow.addEventListener('click',
    procedure(Event: TJSMouseEvent)
    var
      TBody    : TJSHTMLElement;
      Rows     : TJSHTMLCollection;
      I        : Integer;
      RowElem  : TJSHTMLElement;
    begin
      // Grab the <tbody> once and cast it
      TBody := TJSHTMLElement(
                 (document.getElementById('tblPhoneGrid') as TJSHTMLElement)
                   .getElementsByTagName('tbody')[0]
               );

      // Remove 'table-active' from every existing row
      Rows := TBody.children;
      for I := 0 to Rows.length - 1 do
      begin
        RowElem := TJSHTMLElement(Rows.item(I));   // cast Node HTMLElement
        RowElem.classList.remove('table-primary');
      end;

      // Add highlight to the clicked row
      TJSHTMLElement(Event.currentTarget).classList.add('table-primary');
    end
  );

  Cell := TJSHTMLElement(document.createElement('td'));
  Cell.setAttribute('data-label', 'Item ID');
  Cell.innerText := ID;
  NewRow.appendChild(Cell);

  Cell := TJSHTMLElement(document.createElement('td'));
  Cell.setAttribute('data-label', 'Name');
  Cell.innerText := Name;
  NewRow.appendChild(Cell);

  Cell := TJSHTMLElement(document.createElement('td'));
  Cell.setAttribute('data-label', 'Description');
  Cell.innerText := Description;
  NewRow.appendChild(Cell);

  Cell := TJSHTMLElement(document.createElement('td'));
  Cell.setAttribute('data-label', 'Status');
  Cell.innerText := Status;
  NewRow.appendChild(Cell);

  TJSHTMLElement(
    (document.getElementById('tblPhoneGrid') as TJSHTMLElement)
      .getElementsByTagName('tbody')[0]
  ).appendChild(NewRow);

  Utils.HideSpinner('spinner');
end;




procedure TFViewItems.GeneratePagination(TotalPages: Integer);
// Generates pagination for the table.
// TotalPages: Total amount of pages generated by the search
var
  PaginationElement, PageItem, PageLink: TJSHTMLElement;
  I, Start, Finish: Integer;
begin
  PaginationElement := TJSHTMLElement(document.getElementById('pagination'));
  PaginationElement.innerHTML := ''; // Clear existing pagination

  // Previous Button
  PageItem := TJSHTMLElement(document.createElement('li'));
  PageItem.className := 'page-item';
  if PageNumber = 1 then
    PageItem.classList.add('disabled');
  PageLink := TJSHTMLElement(document.createElement('a'));
  PageLink.className := 'page-link';
  PageLink.innerText := 'Previous';
  PageLink.setAttribute('href', 'javascript:void(0)');
  PageLink.addEventListener('click', procedure(Event: TJSMouseEvent)
  begin
    if PageNumber > 1 then
    begin
      Dec(PageNumber);
      GetItems(GenerateSearchOptions());
    end;
  end);
  PageItem.appendChild(PageLink);
  PaginationElement.appendChild(PageItem);

  // Page Numbers

  if PageNumber <= 4 then
  // If page number is low enough no early elipsis needed
  Begin
    Start := 2;
    Finish := 5;
  End
  else if (PageNumber >= (TotalPages - 3)) then
  // If page number is high enough no late elipsis needed
  begin
    Start := TotalPages - 3;
    Finish := TotalPages - 1;
  end
  else
  begin
    Start := PageNumber - 1;
    Finish := PageNumber + 1;
  end;

  PageItem := TJSHTMLElement(document.createElement('li'));
  PageItem.className := 'page-item';
  if 1 = PageNumber then
    PageItem.classList.add('selected-number'); // Add the selected-number class
  PageLink := TJSHTMLElement(document.createElement('a'));
  PageLink.className := 'page-link';
  PageLink.innerText := '1';
  PageLink.setAttribute('href', 'javascript:void(0)');
  PageLink.addEventListener('click', procedure(Event: TJSMouseEvent)
  var
    PageNum: Integer;
  begin
    PageNum := StrToInt((Event.currentTarget as TJSHTMLElement).innerText);
    PageNumber := PageNum;
    GetItems(GenerateSearchOptions());
  end);
  PageItem.appendChild(PageLink);
  PaginationElement.appendChild(PageItem);

  // Adds Elipse to pagination if page number is too big
  if PageNumber > 4 then
  begin
    PageItem := TJSHTMLElement(document.createElement('li'));
    PageItem.className := 'page-item';
    PageItem.classList.add('disabled');
    PageLink := TJSHTMLElement(document.createElement('a'));
    PageLink.className := 'page-link';
    PageLink.innerText := '...';
    PageLink.setAttribute('href', 'javascript:void(0)');
    PageItem.appendChild(PageLink);
    PaginationElement.appendChild(PageItem);
  end;


  // Adds Page, page - 1, and page + 1 to pagination
  for I := Start  to Finish do
  begin
    if ( I > 1) and (I < TotalPages) then
    begin
      PageItem := TJSHTMLElement(document.createElement('li'));
      PageItem.className := 'page-item';
      if I = PageNumber then
        PageItem.classList.add('selected-number'); // Add the selected-number class
      PageLink := TJSHTMLElement(document.createElement('a'));
      PageLink.className := 'page-link';
      PageLink.innerText := IntToStr(I);
      PageLink.setAttribute('href', 'javascript:void(0)');
      PageLink.addEventListener('click', procedure(Event: TJSMouseEvent)
      var
        PageNum: Integer;
      begin
        PageNum := StrToInt((Event.currentTarget as TJSHTMLElement).innerText);
        PageNumber := PageNum;
        GetItems(GenerateSearchOptions());
      end);
      PageItem.appendChild(PageLink);
      PaginationElement.appendChild(PageItem);
    end;
  end;

  // adds ellipse if number is too small
  if PageNumber < TotalPages - 4 then
  begin
    PageItem := TJSHTMLElement(document.createElement('li'));
    PageItem.className := 'page-item';
    PageItem.classList.add('disabled');
    PageLink := TJSHTMLElement(document.createElement('a'));
    PageLink.className := 'page-link';
    PageLink.innerText := '...';
    PageLink.setAttribute('href', 'javascript:void(0)');
    PageItem.appendChild(PageLink);
    PaginationElement.appendChild(PageItem);
  end;

  if TotalPages <> 1 then
  begin
    PageItem := TJSHTMLElement(document.createElement('li'));
    PageItem.className := 'page-item';
    if TotalPages = PageNumber then
          PageItem.classList.add('selected-number');
    PageLink := TJSHTMLElement(document.createElement('a'));
    PageLink.className := 'page-link';
    PageLink.innerText := IntToStr(TotalPages);
    PageLink.setAttribute('href', 'javascript:void(0)');
    PageLink.addEventListener('click', procedure(Event: TJSMouseEvent)
    var
      PageNum: Integer;
    begin
      PageNum := StrToInt((Event.currentTarget as TJSHTMLElement).innerText);
      PageNumber := PageNum;
      GetItems(generateSearchOptions());
    end);
  end;
  PageItem.appendChild(PageLink);
  PaginationElement.appendChild(PageItem);

  // Next Button
  PageItem := TJSHTMLElement(document.createElement('li'));
  PageItem.className := 'page-item';
  if PageNumber = TotalPages then
    PageItem.classList.add('disabled');
  PageLink := TJSHTMLElement(document.createElement('a'));
  PageLink.className := 'page-link';
  PageLink.innerText := 'Next';
  PageLink.setAttribute('href', 'javascript:void(0)');
  PageLink.addEventListener('click', procedure(Event: TJSMouseEvent)
  begin
    if PageNumber < TotalPages then
    begin
      Inc(PageNumber);
      GetItems(GenerateSearchOptions());
    end;
  end);
  PageItem.appendChild(PageLink);
  PaginationElement.appendChild(PageItem);

end;

procedure TFViewItems.GetItems(searchOptions: string);
// retrieves items from the server.
// searchOptions: info to limit how many items we retrieve from the server.
// probably not needed due to database size.
var
  xdcResponse: TXDataClientResponse;
  itemList : TJSObject;
  i: integer;
  data: TJSArray;
  item: TJSObject;
  itemListLength: integer;
begin
  console.log('correct');
  if PageNumber > 0 then
  begin
    Utils.ShowSpinner('spinner');
    try
      xdcResponse := await(XDataWebClient1.RawInvokeAsync('ILookupService.GetItems',
          [searchOptions]));
      itemList :=  TJSObject(xdcResponse.Result);
      data := TJSArray(itemList['data']);
      itemListLength := integer(itemList['count']);
      ClearTable();
      for i := 0 to data.Length - 1 do
      begin
        item := TJSObject(data[i]);
        AddRowToTable(string(item['ID']), string(item['name']), string(item['description']),
        string(item['status']));
      end;
      TotalPages := (itemListLength + PageSize - 1) div PageSize;
      if (PageNumber * PageSize) < itemListLength then
      begin
        lblEntries.Caption := 'Showing entries ' + IntToStr((PageNumber - 1) * PageSize + 1) +
                              ' - ' + IntToStr(PageNumber * PageSize) +
                               ' of ' + IntToStr(itemListLength);
      end
      else
      begin
         lblEntries.Caption := 'Showing entries ' + IntToStr((PageNumber - 1) * PageSize + 1) +
                              ' - ' + IntToStr(itemListLength) +
                               ' of ' + IntToStr(itemListLength);
      end;
      GeneratePagination(TotalPages);
    except
      on E: EXDataClientRequestException do
        Utils.ShowErrorModal('Could not retrieve items: ' + E.ErrorResult.ErrorMessage);
    end;
    Utils.HideSpinner('spinner');
  end;
end;

procedure TFViewItems.btnAddClick(Sender: TObject);
// Button that effectively functions as a GetItems() button
var
  itemOptions: string;
  newform: TFViewAddItem;
begin
  newform := TFViewAddItem.CreateNew;

  newform.Caption := 'Select Customer and Order Type';
  newForm.Popup := True;
  newForm.Border := fbDialog;

  // used to manage Back button handling to close subform
  window.location.hash := 'subform';

  newform.ShowModal(
    procedure(AValue: TModalResult)
    begin
      getItems(generateSearchOptions());
    end
  );
end;

procedure TFViewItems.btnCancelClick(Sender: TObject);
begin
  ShowMessage('Editting Items is not yet implemented');
end;

procedure TFViewItems.btnCloseNotificationClick(Sender: TObject);
begin
  HideNotification();
end;

procedure TFViewItems.btnDeleteClick(Sender: TObject);
begin
  ShowMessage('Deleting Items is no yet implemented');
end;

procedure TFViewItems.btnEditClick(Sender: TObject);
begin
  ShowMessage('Editting Items is not yet implemented');
  //EditMode();
end;

procedure TFViewItems.btnSaveClick(Sender: TObject);
//TODO implement editting items
begin
  ShowMessage('Editting Items is not yet implemented');
  //ViewMode();
end;

procedure TFViewItems.ShowNotification(Notification: string);
var
  splitNotification: TArray<string>;
begin
  if Notification <> '' then
  begin
    splitNotification := Notification.Split([':']);
    if(splitNotification[0] = 'Success') then
    begin
      asm
        var messageDiv = document.getElementById('view.login.message');
        messageDiv.classList.remove('alert-danger');
        messageDiv.classList.add('alert-success');
      end;
    end
    else
    begin
      asm
        var messageDiv = document.getElementById('view.login.message');
        messageDiv.classList.remove('alert-success');
        messageDiv.classList.add('alert-danger');
      end;
    end;
    lblMessage.Caption := splitNotification[1];
    pnlMessage.ElementHandle.hidden := False;
  end;
end;

procedure TFViewItems.HideNotification;
begin
  pnlMessage.ElementHandle.hidden := True;
end;

procedure TFViewItems.AddItem(itemOptions: string);
// adds an item to the database.
var
  xdcResponse: TXDataClientResponse;
begin
  try
    xdcResponse := await(XDataWebClient1.RawInvokeAsync('ILookupService.AddItem', [itemOptions]));
    getItems(GenerateSearchOptions());
  except
    on E: EXDataClientRequestException do
      Utils.ShowErrorModal('Could not add item: ' + E.ErrorResult.ErrorMessage);
  end;
end;


procedure TFViewItems.wcbPageSizeChange(Sender: TObject);
// gets a new amount of items based when the page size is changed
begin
  PageNumber := 1;
  getItems(GenerateSearchOptions());
end;

procedure TFViewItems.ClearTable();
// clears the table
var
  tbody: TJSHTMLElement;
begin
  tbody := TJSHTMLElement(document.getElementById('tblPhoneGrid').getElementsByTagName('tbody')[0]);
  tbody.innerHTML := '';
end;

function TFViewItems.GenerateSearchOptions(): string;
// Generates searchOptions for GetItems.
var
searchOptions: string;
begin
  PageSize := StrToInt(wcbPageSize.Text);
  searchOptions :=  '&pagenumber=' + IntToStr(PageNumber) +
                    '&pagesize=' + IntToStr(PageSize) +
                    '&orderby=' + OrderBy;
  Result := searchOptions;
end;

end.

