unit MotohrAdmin;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Buttons, StdCtrls, ComCtrls, ExtCtrls, ImgList, Menus, AdvGlowButton,
  Grids;

type
  PCnts = ^TCnts;
  TCnts = array of Boolean;

  PMachRec = ^TMachRec;
  TMachRec = record
    id: Integer;
    name: string;
    descr: string;
    cnts: TCnts;
  end;



  TMotohrAdminForm = class(TForm)
    listCt: TListView;
    Splitter2: TSplitter;
    Panel2: TPanel;
    btCancel: TButton;
    btSaveAndApply: TButton;
    Panel1: TPanel;
    Panel3: TPanel;
    edFilter: TEdit;
    btFilterClear: TAdvGlowButton;
    grid: TStringGrid;
    btCtAdd: TButton;
    btCtDel: TButton;
    btSetCol: TButton;
    btSetRow: TButton;
    progress: TProgressBar;
    procedure FormActivate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure gridDrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure edFilterChange(Sender: TObject);
    procedure btCtAddClick(Sender: TObject);
    procedure btCtDelClick(Sender: TObject);
    procedure listCtDblClick(Sender: TObject);
    procedure gridDblClick(Sender: TObject);
    procedure listCtCompare(Sender: TObject; Item1, Item2: TListItem;
      Data: Integer; var Compare: Integer);
    procedure btSetRowClick(Sender: TObject);
    procedure btSetColClick(Sender: TObject);
    procedure btFilterClearClick(Sender: TObject);
    procedure btSaveAndApplyClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure listCtChange(Sender: TObject; Item: TListItem;
      Change: TItemChange);
  private
    machs: array of PMachRec;

    procedure loadListCt;
    procedure initMachs;
    procedure loadGrid;
    procedure numerateGridColumns;
    procedure numerateListCt;
    procedure clearMachs;
  public
    hasChanges: Boolean;

  end;



implementation
uses
  MotohrView,
  MotohrCounterTypeProp,
  InfoDlg,
  Utils,
  TagStorage,
  Motohr,
  MotohrDb,
  Numbers,
  main;

const
  SUBITEM_NAME    = 0;
  SUBITEM_TIMER   = 1;

{$R *.DFM}


procedure TMotohrAdminForm.FormCreate(Sender: TObject);
begin
  grid.Cells[0,0] := 'Название оборудования';
  grid.ColWidths[0] := 300;
  grid.Canvas.Font.Assign(grid.Font);
  hasChanges := false;
  SetLength(machs, 0);
end;

procedure TMotohrAdminForm.FormDestroy(Sender: TObject);
begin
  clearMachs;
end;


procedure TMotohrAdminForm.clearMachs;
var
  i: Integer;
begin
  for i:=0 to Length(machs)-1 do
    Dispose(machs[i]);
end;


procedure TMotohrAdminForm.FormActivate(Sender: TObject);
begin
  try
    Application.ProcessMessages;
    startTransaction;
    loadListCt;
    initMachs;
    loadGrid;
    commitTransaction;
  except
    on e : Exception do begin
      loadGrid;
      btCtAdd.Enabled := false;
      btCtDel.Enabled := false;
      btSetRow.Enabled := false;
      btSetCol.Enabled := false;
      btSaveAndApply.Enabled := false;
      rollbackTransaction;
      showInfoDlg(e.Message, mtError, [mbOk], 0);
    end;
  end;
end;

procedure TMotohrAdminForm.FormShow(Sender: TObject);
begin
//
end;


procedure loadListCtCallback(data: pointer; id: Integer; descr: string; timer: Boolean);
var
  li: TListItem;
begin
  if id = COUNTER_TOTAL_ID then
    Exit;
  li := TListView(data).Items.Add;
  li.SubItems.Add(descr);
  li.SubItems.Add(getTimerMark(timer));
  li.Data := Pointer(id);
  li.Checked := false;
end;

procedure TMotohrAdminForm.loadListCt;
begin
  listCt.Items.Clear;
  MotohrDb.loadCounterTypes(listCt, loadListCtCallback);
  listCt.AlphaSort;
  numerateListCt;
end;


procedure TMotohrAdminForm.listCtCompare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);
begin
  Compare := AnsiStrIComp( PChar(Item1.SubItems[SUBITEM_TIMER]), PChar(Item2.SubItems[SUBITEM_TIMER]) );
  if Compare = 0 then
    Compare := AnsiStrIComp( PChar(Item1.SubItems[SUBITEM_NAME]), PChar(Item2.SubItems[SUBITEM_NAME]) );
end;


procedure loadMachsCallback(data: pointer; id: Integer; name, descr: string);
var
  idx: Integer;
  p: PMachRec;
begin
  idx := GetTagIndex(getMotohrModulePath + '.' + name + '.descr');
  if (idx >= 0) and IsTagStatusGood(idx) then begin
    new(p);
    p.id := id;
    p.name := name;
    p.descr := descr;
    TStringList(data).AddObject( toPaddingNumbersLeft(descr), TObject(p) );
  end;
end;

procedure loadCountersCallback(data: pointer; cntIdx, machId: Integer);
var
  i: Integer;
  p: PMachRec;
  sl: TStringList;
begin
  sl := TStringList(data);
  if sl.Find(IntToStr(machId), i) then begin
    p := PMachRec(sl.Objects[i]);
    p.cnts[cntIdx] := true;
  end;
end;


procedure TMotohrAdminForm.initMachs;
var
  sl: TStringList;
  i,j: Integer;
  p: PMachRec;
  ctCount: integer;
  ctId: integer;
begin
  clearMachs;

  sl := TStringList.Create;
  MotohrDb.loadMachs(sl, loadMachsCallback);
  sl.Sort;

  ctCount := listCt.Items.Count;
  SetLength(machs, sl.Count);
  for i:=0 to sl.Count-1 do begin
    p := PMachRec(sl.Objects[i]);
    machs[i] := p;
    SetLength(p.cnts, ctCount);
//    FillMemory(@p.cnts[0], SizeOf(Boolean) * ctCount, Byte(false));
    for j:=0 to ctCount-1 do
      p.cnts[j] := False;
    sl[i] := IntToStr(p.id);
  end;
  sl.Sort;

  for i:=0 to ctCount-1 do begin
    ctId := Integer(listCt.Items[i].Data);
    MotohrDb.loadCounters(sl, i, ctId, loadCountersCallback);
  end;

  sl.Free;
end;


procedure TMotohrAdminForm.loadGrid;
var
  filter: string;
  i,j: Integer;
  machCount, ctCount: integer;
  a: array of Integer;
  p: PMachRec;
begin
  numerateGridColumns;

  SetLength(a, Length(machs));
  j := 0;

  filter := prepareFilter(edFilter.Text);
  for i:=0 to Length(machs)-1 do begin
    if (filter = '') or CheckFilter(machs[i].descr, filter) then begin
      a[j] := i;
      inc(j);
    end;
  end;
  machCount := j;

  ctCount := listCt.Items.Count;
  grid.ColCount := ctCount + 1;
  if machCount = 0 then begin
    grid.RowCount := 2;
    for j:=0 to ctCount-1 do
      grid.Cells[j, 1] := '';
    exit;
  end;
  grid.RowCount := machCount + 1;

  for i:=0 to machCount-1 do begin
    p := machs[a[i]];
    grid.Objects[0, i+1] := TObject(p);
    grid.Cells[0, i+1] := p.descr;
    for j:=0 to Length(p.cnts)-1 do begin
      grid.Cells[j+1, i+1] := iif(p.cnts[j], ' ', '');
    end;
  end;
end;


procedure TMotohrAdminForm.numerateGridColumns;
var
  i: Integer;
begin
  for i:=1 to listCt.Items.Count do
    grid.Cells[i, 0] := IntToStr(i);
end;


procedure TMotohrAdminForm.numerateListCt;
var
  i: Integer;
begin
  for i:=0 to listCt.Items.Count-1 do
    listCt.Items[i].Caption := inttostr(i+1);
end;




procedure TMotohrAdminForm.gridDrawCell(Sender: TObject; ACol,
  ARow: Integer; Rect: TRect; State: TGridDrawState);
var
  s: string;
  w, h, x, y: integer;
  cb: TColor;
  sel: Boolean;
begin
  s := grid.Cells[ACol, ARow];
  w := grid.Canvas.TextWidth(s);
  h := grid.Canvas.TextHeight(s);
  y := Rect.Top + (Rect.Bottom - Rect.Top - h) div 2;

  if ACol = 0 then
    x := 8
  else
    x := Rect.Left + (Rect.Right - Rect.Left - w) div 2;


  sel := (ACol > 0) and (grid.Cells[ACol, ARow] <> '');

  if gdFixed in State then begin
    cb := clBtnFace;
  end else
  if (gdSelected in State) and (Length(machs) > 0) then begin
    if sel then
      cb := clLime
    else
      cb := clBtnFace;
  end else begin
    if sel then
      cb := $0000C400
    else
      cb := clWindow;
  end;

  grid.Canvas.Brush.Color := cb;

  grid.Canvas.TextRect(Rect, x, y, s);

  if (gdSelected in State) then
    Grid.Canvas.DrawFocusRect(Rect);
end;

procedure TMotohrAdminForm.edFilterChange(Sender: TObject);
begin
  loadGrid;
end;


procedure TMotohrAdminForm.listCtDblClick(Sender: TObject);
var
  timer: Boolean;
  descr: string;
  li: TListItem;
begin
  li := listCt.Selected;
  if li = nil then
    Exit;

  descr := li.SubItems[SUBITEM_NAME];
  timer := li.SubItems[SUBITEM_TIMER] <> '';

  if not ShowMotohrCounterTypeProp(descr, timer) then
    Exit;

  if (li.SubItems[SUBITEM_NAME] <> descr) or
      (li.SubItems[SUBITEM_TIMER] <> getTimerMark(timer)) then
    li.Checked := true;

  li.SubItems[SUBITEM_NAME] := descr;
  li.SubItems[SUBITEM_TIMER] := getTimerMark(timer);
  listCt.SetFocus;
  hasChanges := true;
end;


procedure TMotohrAdminForm.btCtAddClick(Sender: TObject);
var
  timer: Boolean;
  descr: string;
  i, n: integer;
begin
  descr := 'Новый счетчик';
  timer := false;
  if not ShowMotohrCounterTypeProp(descr, timer) then
    Exit;

  with listCt.Items.Add do begin
    Caption := IntToStr(index+1);
    SubItems.Add(descr);
    SubItems.Add(getTimerMark(timer));
    Data := nil;
    Checked := true;
  end;

  for i:=0 to Length(machs)-1 do begin
    n := Length(machs[i].cnts);
    SetLength(machs[i].cnts, n + 1);
  end;
  loadGrid;

  listCt.Items[listCt.Items.Count-1].Selected := true;
  listCt.SetFocus;
  hasChanges := true;
end;


procedure TMotohrAdminForm.btCtDelClick(Sender: TObject);
var
  k,i,l,n: integer;
  p: PMachRec;
begin
  if (listCt.Selected = nil) then
    Exit;

  if showInfoDlg('Удалить ' + listCt.Selected.SubItems[SUBITEM_NAME] + '?', 'Подтверждение', 'Удалить', 'Отмена') <> 1 then
    exit;

  k := listCt.Selected.index;
  listCt.Selected.Delete;
  numerateListCt;

  for i:=0 to Length(machs)-1 do begin
    p := machs[i];
    l := length(p.cnts) - 1;
    n := l - k;
    if n > 0 then
      CopyMemory(@p.cnts[k], @p.cnts[k + 1], SizeOf(Boolean) * n);
    SetLength(p.cnts, l);
  end;
  loadGrid;

  if k < listCt.Items.Count then
    listCt.Items[k].Selected := true;
  listCt.SetFocus;
  hasChanges := true;
end;


procedure TMotohrAdminForm.gridDblClick(Sender: TObject);
var
  j: integer;
  p: PMachRec;
begin
  j := grid.Col - 1;
  if (Length(machs) = 0) or (grid.Row < 1) or (j < 0) then
    exit;

  p := PMachRec(grid.Objects[0, grid.Row]);

  p.cnts[j] := not p.cnts[j];
  loadGrid;
  hasChanges := true;
end;


procedure TMotohrAdminForm.btSetRowClick(Sender: TObject);
var
  j: integer;
  p: PMachRec;
  value: Boolean;
begin
  grid.SetFocus;
  if (Length(machs) = 0) or (grid.Row < 1) or (listCt.Items.Count < 0) then
    exit;
  p := PMachRec(grid.Objects[0, grid.Row]);

  value := not p.cnts[0];
  for j:=0 to listCt.Items.Count-1 do begin
    p.cnts[j] := value;
  end;
  loadGrid;
  hasChanges := true;
end;


procedure TMotohrAdminForm.btSetColClick(Sender: TObject);
var
  i,j: integer;
  p: PMachRec;
  value: Boolean;
begin
  grid.SetFocus;
  j := grid.Col - 1;
  if (Length(machs) = 0) or (grid.Row < 1) or (j < 0) then
    exit;
  p := PMachRec(grid.Objects[0, 1]);
  value := not p.cnts[j];

  for i:=1 to grid.RowCount-1 do begin
    p := PMachRec(grid.Objects[0, i]);
    p.cnts[j] := value;
  end;
  loadGrid;
  hasChanges := true;
end;


procedure TMotohrAdminForm.btFilterClearClick(Sender: TObject);
begin
  edFilter.Text := '';
end;


procedure TMotohrAdminForm.btSaveAndApplyClick(Sender: TObject);
var
  i,j,k: integer;
  machId: integer;
  ctId: integer;
  sids: string;
  descr: string;
  timer: Boolean;
  li: TListItem;
  ctCount: Integer;
  p: PMachRec;
  sl: TStringList;
begin
  if not hasChanges then begin
    showInfoDlg('Нет изменений');
    exit;
  end;

  if Sender <> nil then
    if showInfoDlg('Сохранить изменения?', mtConfirmation, mbOKCancel, 0) <> mrOk then
      exit;

  progress.Max := listCt.Items.Count * (Length(machs) + 1);
  progress.Position := 0;
  progress.Visible := true;
  Application.ProcessMessages;

  sl := TStringList.Create;

  try
    startTransaction(true);

    loadCounters(sl);

    // counter types
    ctCount := listCt.Items.Count;
    sids := IntToStr(COUNTER_TOTAL_ID);
    for i:=0 to ctCount-1 do begin
      progress.StepIt;

      li := listCt.Items[i];
      ctId := Integer(li.data);
      if li.Checked then begin
        descr := li.SubItems[SUBITEM_NAME];
        timer := li.SubItems[SUBITEM_TIMER] <> '';

        if ctId = 0 then begin
          ctId := insertCounterType(descr, timer);
          li.data := pointer(ctId);
        end else begin
          updateCounterType(ctId, descr, timer);
        end;
      end;
      sids := sids + ',' + IntToStr(ctId);
    end;
    deleteOtherCounterTypes(sids);

    // add counters
    for i:=0 to Length(machs)-1 do begin
      p := machs[i];
      machId := p.id;
      for j:=0 to ctCount-1 do begin
        progress.StepIt;
        ctId := Integer(listCt.Items[j].Data);
        if p.cnts[j] then begin
          if sl.Find(Format('%d.%d', [machId, ctId]), k) then
            sl.Delete(k)
          else
            MotohrDb.insertCounter(machId, ctId)
        end;
//        if p.cnts[j] then
//          MotohrDb.insertCounter(machId, ctId)
//        else
//          MotohrDb.deleteCounter(machId, ctId);
      end;
    end;

    // delete counters
    for i:=0 to sl.Count-1 do begin
      StrToIntPair(sl[i], machId, ctId);
      if ctId <> COUNTER_TOTAL_ID then
        MotohrDb.deleteCounter(machId, ctId);
    end;

    commitTransaction;
    hasChanges := false;

    // send reinit cmd
    if showInfoDlg('Перезапустить сервис учета моточасов?', mtConfirmation, mbOKCancel, 0) = mrOk then begin
      sendSystemUtilCommand(SYSTEM_CMD_REINIT);
    end;
  except
    on e : Exception do begin
      rollbackTransaction;
      showInfoDlg(e.Message, mtError, [mbOk], 0);
    end;
  end;
  sl.Free;

  progress.Visible := false;
end;


procedure TMotohrAdminForm.listCtChange(Sender: TObject; Item: TListItem;
  Change: TItemChange);
begin
  if listCt.selected <> nil then
    grid.col := listCt.selected.index + 1;
end;

end.

