program Weihnachtsbaum;

{$mode objfpc}{$H+}

uses
  Classes, SysUtils, Terminal, TerminalStreams, TerminalKeys, TerminalModifier, TerminalColor;


type
  TTreeData = (tdNone, tdStem, tdTree, tdLight);
  TTree = array of array of TTreeData;

function computeTree(height: Integer; WindowSizeX, WindowSizeY: Integer): TTree;
var
  stemHeight,
  treeHeight,
  treeWidth,
  stemWidth,
  x, y: Integer;
  CenterX,
  TreeSlope: Double;
begin
  stemHeight := height div 8;
  treeHeight := height - stemHeight;
  treeWidth := height div 2;
  stemWidth := treeWidth div 4;
  CenterX := WindowSizeX / 2;
  TreeSlope := treeHeight / treeWidth;

  Result := nil;
  SetLength(Result, WindowSizeX, WindowSizeY);

  for x := 0 to WindowSizeX - 1 do
    for y := 0 to WindowSizeY - 1 do
      if y < treeHeight then
      begin
        // In tree, check if in correct triangle
        if (height - y) < treeHeight - Abs(x - CenterX) * TreeSlope then
        begin
          if Random(1024) < 256 then
            Result[x, y] := tdLight
          else
            Result [x, y] := tdTree;
        end
        else
          Result[x, y] := tdNone;
      end
      else if y < height then
      begin
        if Abs(x - CenterX) < stemWidth then
          Result[x, y] := tdStem;
      end
      else
        Result[x,y] := tdNone;
end;

procedure DrawTree(const Tree: TTree; const cStem, cTree, cLights: TTextColor;
                   output: TTerminalOutputStream; sz: TTerminalSize);
var
  curr: TTreeData;
  y, x: Integer;
begin
  output.Clear(cmResetScreen);
  for y := 0 to sz.Rows - 1 do
    for x := 0 to sz.Columns -1 do
    begin
      curr := Tree[x, y];
      case curr of
      tdNone: output.WriteColored(' ', TTextColor.ResetColor);
      tdLight: output.WriteColored('#', cLights);
      tdTree: output.WriteColored('#', cTree);
      tdStem: output.WriteColored('#', cStem);
      end;
    end;
  output.FlushBuffer;
end;

var
  term: TTerminal;
  tree: TTree;
  sz, newSize: TTerminalSize;
  key: TTerminalKey;
  t0: QWord;
begin
  Randomize;
  term := TTerminal.Create;
  try
    term.Output.BufferSize:=SizeInt.MaxValue;
    sz.Rows:=0;
    sz.Columns:=0;
    term.Input.DirectRead:=True;
    While True do
    begin   
      t0 := GetTickCount64;
      if term.Input.ReadKeyNonBlocking(key) then
        if (key.SpecialKeyCode = skEscape) Or (
           (key.CharValue = 'c') And (kmCtrl in Key.Modifiers)
        ) then
          Break;
      newSize := term.GetWindowSize;
      if (newSize.Columns <> sz.Columns) or (newSize.Rows <> sz.Rows) then
      begin
        sz := newSize;
        tree := computeTree(sz.Rows, sz.Columns, sz.Rows);
      end;
      DrawTree(Tree, $AB8600, $00DE00, Random($FFFFFF), term.Output, sz);
      Sleep(500 - (t0 - GetTickCount64));
    end;
  finally
    term.Free;
  end;
end.

