unit Bingo.Forms.Main;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  System.Generics.Collections,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, Skia, Skia.FMX, FMX.Ani,
  FMX.Effects, FMX.Filter.Effects, FMX.Media;

type

  TMainForm = class(TForm)
    BoardRect: TRectangle;
    StyleBook: TStyleBook;
    RightLayout: TLayout;
    LogoImage: TSkAnimatedImage;
    BackgroundImage: TSkAnimatedImage;
    DrawLayout: TLayout;
    DrawNumber: TLabel;
    HistoryLayout: TLayout;
    DrawRotation: TFloatAnimation;
    DrawXAnimation: TFloatAnimation;
    DrawYAnimation: TFloatAnimation;
    DrawPlayer: TMediaPlayer;
    HistoryNumber1: TCheckBox;
    HistoryNumber2: TCheckBox;
    HistoryNumber3: TCheckBox;
    procedure LogoImageClick(Sender: TObject);
    procedure LogoImageDblClick(Sender: TObject);
  private
    FHistory: TList<Byte>;
    FNumbers: TDictionary<Byte, Byte>;
    procedure BuildBoard;
    function GetNumberBox(ANumber: Integer): TCheckBox;
    procedure LitNumberOnBoard(ANumber: Byte);
    procedure ClearBoard;
    procedure ClearData;
    procedure ClearHistory;
    function DrawNewNumber: Byte;
    procedure PlayNumber(ANumber: Byte);
    procedure PushHistory(ANumber: Byte);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

var
  MainForm: TMainForm;

implementation

{$R *.fmx}

uses
  System.Math,
  System.IOUtils;

constructor TMainForm.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FHistory := TList<Byte>.Create;
  FNumbers := TDictionary<Byte, Byte>.Create;
  BuildBoard;
  ClearBoard;
  ClearData;
  ClearHistory;
  Randomize;
end;

destructor TMainForm.Destroy;
begin
  if Assigned(FHistory) then
    FreeAndNil(FHistory);
  if Assigned(FNumbers) then
    FreeAndNil(FNumbers);
  inherited Destroy;
end;

function TMainForm.DrawNewNumber: Byte;
begin
  if FNumbers.Count >= 90 then
  begin
    Result := 0;
    Exit;
  end;
  while True do
  begin
    Result := Byte(RandomRange(1, Succ(90)));
    if FNumbers.ContainsKey(Result) then
      Continue;
    FNumbers.Add(Result, Result);
    Break;
  end;
end;

procedure TMainForm.ClearBoard;
begin
  for var LNumber := 1 to 90 do
    GetNumberBox(LNumber).IsChecked := False;
end;

procedure TMainForm.ClearData;
begin
  FHistory.Clear;
  FNumbers.Clear;
end;

procedure TMainForm.ClearHistory;
begin
  for var LNumber := 1 to 3 do
  begin
    var LName := Format('HistoryNumber%d', [LNumber]);
    var LControl := TCheckBox(FindComponent(LName));
    LControl.IsChecked := False;
    LControl.Text := '';
  end;
end;

procedure TMainForm.LogoImageClick(Sender: TObject);
begin
  var LNumber := DrawNewNumber;
  if LNumber = 0 then
    Exit;
  PushHistory(LNumber);
  PlayNumber(LNumber);
  LitNumberOnBoard(LNumber);
  DrawRotation.Start;
  DrawXAnimation.Start;
  DrawYAnimation.Start;
  DrawNumber.Text := LNumber.ToString;
end;

procedure TMainForm.LogoImageDblClick(Sender: TObject);
begin
  ClearBoard;
  ClearData;
  ClearHistory;
end;

procedure TMainForm.PlayNumber(ANumber: Byte);
begin
  var LDirPath := TPath.Combine(TPath.GetDocumentsPath, 'Bingo\mp3');
  var LMp3Name := Format('%.2d.mp3', [ANumber]);
  var LMp3Path := TPath.Combine(LDirPath, LMp3Name);
  DrawPlayer.Stop;
  DrawPlayer.FileName := LMp3Path;
  DrawPlayer.Play;
end;

procedure TMainForm.PushHistory(ANumber: Byte);
begin
  FHistory.Insert(0, ANumber);
  if FHistory.Count > 3 then
    FHistory.Delete(Pred(FHistory.Count));
  var LIndex := FHistory.Count;
  for var LNumber := 3 downto 1 do
  begin
    Dec(LIndex);
    var LName := Format('HistoryNumber%d', [LNumber]);
    var LControl := TCheckBox(FindComponent(LName));
    if LIndex >= 0 then
    begin
      LControl.Text := IntToStr(FHistory[LIndex]);
      LControl.IsChecked := False;
      LControl.IsChecked := True;
    end;
  end;
end;

procedure TMainForm.LitNumberOnBoard(ANumber: Byte);
begin
  GetNumberBox(ANumber).IsChecked := True;
end;

function TMainForm.GetNumberBox(ANumber: Integer): TCheckBox;
begin
  var LName := Format('Number%d', [ANumber]);
  Result := TCheckBox(FindComponent(LName));
end;

procedure TMainForm.BuildBoard;
const
  LControlSize = 110.0;
begin
  for var LRow := 0 to Pred(9) do
    for var LCol := 0 to Pred(10) do
    begin
      var LNumber := LRow * 10 + LCol + 1;
      var LControl := TCheckBox.Create(Self);
      LControl.Name := Format('Number%d', [LNumber]);
      LControl.Parent := BoardRect;
      LControl.SetBounds(LCol * LControlSize, LRow * LControlSize,
        LControlSize, LControlSize);
      LControl.HitTest := False;
      LControl.StyleLookup := 'numberBoardStyle';
      LControl.TabOrder := LNumber - 1;
      LControl.Text := IntToStr(LNumber);
      LControl.TextSettings.HorzAlign := TTextAlign.Center;
    end;
end;

end.
