unit UFormMain;

{$mode delphi}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
  UGame, LCLType, Menus, ActnList, ComCtrls, dateutils, XMLConf, DOM,
  UGeometry;

const
  ZoomFactor = 1.5;
  MouseMinDiff = 0.1;

type

  { TFormMain }

  TFormMain = class(TForm)
    AStatusBarVisible: TAction;
    AToolBarVisible: TAction;
    AToolBarBigIcons: TAction;
    AZoomIn: TAction;
    AZoomAll: TAction;
    AZoomOut: TAction;
    ActionList1: TActionList;
    MainMenu1: TMainMenu;
    MenuItem1: TMenuItem;
    MenuItem10: TMenuItem;
    MenuItem11: TMenuItem;
    MenuItem12: TMenuItem;
    MenuItem13: TMenuItem;
    MenuItem14: TMenuItem;
    MenuItem15: TMenuItem;
    MenuItem16: TMenuItem;
    MenuItem17: TMenuItem;
    MenuItem18: TMenuItem;
    MenuItem19: TMenuItem;
    MenuItem2: TMenuItem;
    MenuItem20: TMenuItem;
    MenuItem21: TMenuItem;
    MenuItem22: TMenuItem;
    MenuItem23: TMenuItem;
    MenuItem24: TMenuItem;
    MenuItem25: TMenuItem;
    MenuItemLoadRecent: TMenuItem;
    MenuItem3: TMenuItem;
    MenuItem4: TMenuItem;
    MenuItem5: TMenuItem;
    MenuItem6: TMenuItem;
    MenuItem7: TMenuItem;
    MenuItem8: TMenuItem;
    MenuItem9: TMenuItem;
    PaintBox1: TPaintBox;
    PopupMenuToolbar: TPopupMenu;
    StatusBar1: TStatusBar;
    Timer1: TTimer;
    ToolBar1: TToolBar;
    ToolButton1: TToolButton;
    ToolButton10: TToolButton;
    ToolButton11: TToolButton;
    ToolButton12: TToolButton;
    ToolButton13: TToolButton;
    ToolButton2: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    ToolButton5: TToolButton;
    ToolButton6: TToolButton;
    ToolButton7: TToolButton;
    ToolButton8: TToolButton;
    ToolButton9: TToolButton;
    procedure AStatusBarVisibleExecute(Sender: TObject);
    procedure AToolBarBigIconsExecute(Sender: TObject);
    procedure AToolBarVisibleExecute(Sender: TObject);
    procedure AZoomAllExecute(Sender: TObject);
    procedure AZoomInExecute(Sender: TObject);
    procedure AZoomOutExecute(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure PaintBox1MouseLeave(Sender: TObject);
    procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure PaintBox1MouseWheelDown(Sender: TObject; Shift: TShiftState;
      MousePos: TPoint; var Handled: Boolean);
    procedure PaintBox1MouseWheelUp(Sender: TObject; Shift: TShiftState;
      MousePos: TPoint; var Handled: Boolean);
    procedure PaintBox1Paint(Sender: TObject);
    procedure EraseBackground(DC: HDC); override;
    procedure PaintBox1Resize(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    TempBitmap: TBitmap;
    StartMousePoint: TPoint;
    StartViewPoint: TPoint;
    MoveActive: Boolean;
    RedrawPending: Boolean;
    Drawing: Boolean;
    DrawDuration: TDateTime;
    LastTimerTime: TDateTime;
    TimerPeriod: TDateTime;
  public
    procedure LoadConfig(Config: TXmlConfig; Path: string);
    procedure SaveConfig(Config: TXmlConfig; Path: string);
    procedure ReloadView;
    procedure Redraw;
  end;

var
  FormMain: TFormMain;

implementation

uses
  UCore;

resourcestring
  STurn = 'turn';

{$R *.lfm}

{ TFormMain }

procedure TFormMain.PaintBox1Paint(Sender: TObject);
var
  DrawStart: TDateTime;
const
  BackgroundColor = $404040;
begin
  DrawStart := Now;
  if Assigned(Core.CurrentClient) then
  with Core.CurrentClient do begin
    View.DestRect := TRect.CreateBounds(TPoint.Create(0, 0), TPoint.Create(PaintBox1.Width, PaintBox1.Height));
    if csOpaque in PaintBox1.ControlStyle then begin
      TempBitmap.SetSize(PaintBox1.Width, PaintBox1.Height);
      TempBitmap.Canvas.Brush.Color := BackGroundColor; //clBackground; //PaintBox1.GetColorResolvingParent;
      TempBitmap.Canvas.FillRect(0, 0, PaintBox1.Width, PaintBox1.Height);
      if Assigned(ControlPlayer) then ControlPlayer.Paint(TempBitmap.Canvas, View)
        else Core.Game.Map.Paint(TempBitmap.Canvas, View);
      PaintBox1.Canvas.Draw(0, 0, TempBitmap);
    end else begin
      {$ifdef WINDOWS}
      PaintBox1.Canvas.Brush.Color := BackgroundColor; //clBackground; //PaintBox1.GetColorResolvingParent;
      PaintBox1.Canvas.FillRect(0, 0, PaintBox1.Width, PaintBox1.Height);
      {$endif}
      if Assigned(ControlPlayer) then ControlPlayer.Paint(PaintBox1.Canvas, View)
        else Core.Game.Map.Paint(PaintBox1.Canvas, View);
    end;
  end;
  DrawDuration := (9 * DrawDuration + (Now - DrawStart)) / 10;
end;

procedure TFormMain.EraseBackground(DC: HDC);
begin
  // Do nothing, all background space covered by controls
end;

procedure TFormMain.PaintBox1Resize(Sender: TObject);
begin
  if Assigned(Core.CurrentClient) then
  with Core.CurrentClient do
    View.DestRect := TRect.CreateBounds(TPoint.Create(0, 0), TPoint.Create(PaintBox1.Width, PaintBox1.Height));
  Redraw;
end;

procedure TFormMain.Timer1Timer(Sender: TObject);
var
  NewCaption: string;
begin
  if RedrawPending and not Drawing then begin
    Drawing := True;
    if not Core.DevelMode then RedrawPending := False;
    TimerPeriod := (9 * TimerPeriod + (Now - LastTimerTime)) / 10;
    LastTimerTime := Now;
    PaintBox1.Repaint;
    StatusBar1.Panels[1].Text := IntToStr(Trunc(DrawDuration / OneMillisecond)) + ' / ' +
      IntToStr(Trunc(TimerPeriod / OneMillisecond)) + ' ms' +
      ' ' + IntToStr(Core.Game.Map.CellLinks.Count);
    NewCaption := 'xTactics';
    if Assigned(Core.Game.CurrentPlayer) then
      NewCaption := Core.Game.CurrentPlayer.Name + ' - ' + STurn + ' ' + IntToStr(Core.Game.TurnCounter) + ' - ' + NewCaption;
    Caption := NewCaption;
    Drawing := False;
  end;
end;

procedure TFormMain.LoadConfig(Config: TXmlConfig; Path: string);
begin
  with Config do begin
    AToolBarBigIcons.Checked := GetValue(DOMString(Path + '/LargeIcons'), False);
    AToolBarVisible.Checked := GetValue(DOMString(Path + '/ToolBarVisible'), True);
    AStatusBarVisible.Checked := GetValue(DOMString(Path + '/StatusBarVisible'), False);
  end;
end;

procedure TFormMain.SaveConfig(Config: TXmlConfig; Path: string);
begin
  with Config do begin
    SetValue(DOMString(Path + '/LargeIcons'), AToolBarBigIcons.Checked);
    SetValue(DOMString(Path + '/ToolBarVisible'), AToolBarVisible.Checked);
    SetValue(DOMString(Path + '/StatusBarVisible'), AStatusBarVisible.Checked);
  end;
end;

procedure TFormMain.ReloadView;
begin
  if AToolBarBigIcons.Checked then begin
    ToolBar1.Images := Core.ImageListLarge;
    ToolBar1.ButtonWidth := Core.ImageListLarge.Width + 7;
    ToolBar1.ButtonHeight := Core.ImageListLarge.Height + 6;
    ToolBar1.Width := Core.ImageListLarge.Width + 10;
    ToolBar1.Height := Core.ImageListLarge.Height + 10;
  end else begin
    ToolBar1.Images := Core.ImageListSmall;
    ToolBar1.ButtonWidth := Core.ImageListSmall.Width + 7;
    ToolBar1.ButtonHeight := Core.ImageListSmall.Height + 6;
    ToolBar1.Width := Core.ImageListSmall.Width + 10;
    ToolBar1.Height := Core.ImageListSmall.Height + 10;
  end;
  ToolBar1.Visible := AToolBarVisible.Checked;
  StatusBar1.Visible := AStatusBarVisible.Checked;
end;

procedure TFormMain.Redraw;
begin
  RedrawPending := True;
end;

procedure TFormMain.FormCreate(Sender: TObject);
begin
  {$IFDEF Linux}
  //PaintBox1.ControlStyle := PaintBox1.ControlStyle + [csOpaque];
  {$ENDIF}
  //DoubleBuffered := True;
  TempBitmap := TBitmap.Create;
  TimerPeriod := 0;
  LastTimerTime := Now;
end;

procedure TFormMain.AZoomAllExecute(Sender: TObject);
var
  Factor: TPointF;
  MapRect: TRect;
  NewZoom: Single;
begin
  with Core, Game, CurrentClient, View do begin
    MapRect := Map.CalculatePixelRect;
    Factor := TPointF.Create(DestRect.Size.X / MapRect.Size.X,
      DestRect.Size.Y / MapRect.Size.Y);
    if Factor.X < Factor.Y then NewZoom := Factor.X
      else NewZoom := Factor.Y;
    if NewZoom = 0 then NewZoom := 1;
    Zoom := NewZoom;
    CenterMap;
  end;
  Redraw;
end;

procedure TFormMain.AToolBarBigIconsExecute(Sender: TObject);
begin
  AToolBarBigIcons.Checked := not AToolBarBigIcons.Checked;
  ReloadView;
end;

procedure TFormMain.AStatusBarVisibleExecute(Sender: TObject);
begin
  AStatusBarVisible.Checked := not AStatusBarVisible.Checked;
  ReloadView;
end;

procedure TFormMain.AToolBarVisibleExecute(Sender: TObject);
begin
  AToolBarVisible.Checked := not AToolBarVisible.Checked;
  ReloadView;
end;

procedure TFormMain.AZoomInExecute(Sender: TObject);
begin
  with Core.CurrentClient do begin
    View.Zoom := View.Zoom * ZoomFactor;
  end;
  Redraw;
end;

procedure TFormMain.AZoomOutExecute(Sender: TObject);
//var
//  D: TPoint;
begin
  with Core.CurrentClient do begin
    //D := Point(Trunc(MousePos.X - View.Left / ViewZoom),
    //  Trunc(MousePos.Y - View.Top / ViewZoom));
    View.Zoom := View.Zoom / ZoomFactor;
    //View := Bounds(Trunc((D.X - MousePos.X) * ViewZoom),
    //  Trunc((D.Y - MousePos.Y) * ViewZoom),
    //  View.Right - View.Left,
    //  View.Bottom - View.Top);
  end;
  Redraw;
end;

procedure TFormMain.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  Core.Game.Running := False;
  Core.PersistentForm.Save(Self);
  SaveConfig(Core.XMLConfig1, 'FormMain');
  Core.XMLConfig1.Flush;
end;

procedure TFormMain.FormDestroy(Sender: TObject);
begin
  TempBitmap.Free;
end;

procedure TFormMain.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState
  );
begin
  if (Key = 27) or (Key = 17) then
  if Assigned(Core.Game.CurrentPlayer) then begin
    Core.CurrentClient.View.SelectedCell := nil;
    Redraw;
  end;
end;

procedure TFormMain.FormShow(Sender: TObject);
begin
  Core.Init;
  LoadConfig(Core.XMLConfig1, 'FormMain');
  Core.PersistentForm.Load(Self, wsMaximized);
  ReloadView;
  Redraw;
end;

procedure TFormMain.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then begin
    if Assigned(Core.CurrentClient) then begin
      StartMousePoint := TPoint.Create(X, Y);
      StartViewPoint := Core.CurrentClient.View.SourceRect.P1;
      MoveActive := True;
    end;
  end;
end;

procedure TFormMain.PaintBox1MouseLeave(Sender: TObject);
begin
  MoveActive := False;
end;

procedure TFormMain.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  Cell: TCell;
  OldCell: TCell;
  CellPos: TPoint;
begin
  if Assigned(Core.CurrentClient) then begin
    if MoveActive then
    if (Abs(StartMousePoint.X - X) > Trunc(Screen.PixelsPerInch * MouseMinDiff)) or
    (Abs(StartMousePoint.Y - Y) > Trunc(Screen.PixelsPerInch * MouseMinDiff)) then
    with Core.Game.CurrentPlayer, Core.CurrentClient do begin
      View.SourceRect := TRect.CreateBounds(TPoint.Create(Trunc(StartViewPoint.X + (StartMousePoint.X - X) / View.Zoom),
        Trunc(StartViewPoint.Y + (StartMousePoint.Y - Y) / View.Zoom)),
        View.SourceRect.Size);
      Redraw;
    end;
    Cell := nil;
    OldCell := Core.CurrentClient.View.FocusedCell;
    with Core.Game do
      Cell := Map.PosToCell(Core.CurrentClient.View.CanvasToCellPos(TPoint.Create(X, Y)), Core.CurrentClient.View );
    if Assigned(Cell) then begin
      Core.CurrentClient.View.FocusedCell := Cell;
      StatusBar1.Panels[0].Text := '[' + IntToStr(Cell.PosPx.X) + ', ' + IntToStr(Cell.PosPx.Y) +
        '] (' + IntToStr(Cell.MovesFrom.Count) + ', ' + IntToStr(Cell.MovesTo.Count) + ')';
    end else begin
      Core.CurrentClient.View.FocusedCell := nil;
      StatusBar1.Panels[0].Text := '';
    end;
    CellPos := Core.CurrentClient.View.CanvasToCellPos(TPoint.Create(X, Y));
    StatusBar1.Panels[2].Text := 'CellPos: ' + IntToStr(CellPos.X) + ', ' + IntToStr(CellPos.Y);
    if Cell <> OldCell then Redraw;
  end else StatusBar1.Panels[0].Text := '';
end;

procedure TFormMain.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if (Abs(StartMousePoint.X - X) < Trunc(Screen.PixelsPerInch * MouseMinDiff)) and
  (Abs(StartMousePoint.Y - Y) < Trunc(Screen.PixelsPerInch * MouseMinDiff)) then begin
    if Core.Game.Running and (Core.Game.CurrentPlayer.Mode = pmHuman) then begin
      Core.CurrentClient.View.SelectCell(TPoint.Create(X, Y), Core.Game.CurrentPlayer, Shift);
      Redraw;
    end;
  end;
  MoveActive := False;
end;

procedure TFormMain.PaintBox1MouseWheelDown(Sender: TObject;
  Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
begin
  AZoomOut.Execute;
end;

procedure TFormMain.PaintBox1MouseWheelUp(Sender: TObject; Shift: TShiftState;
  MousePos: TPoint; var Handled: Boolean);
begin
  AZoomIn.Execute;
end;

end.

