{
  Blaise - An Object Pascal Compiler
  Copyright (c) 2026 Graeme Geldenhuys
  SPDX-License-Identifier: Apache-2.0 WITH Swift-exception
  Licensed under the Apache License v2.0 with Runtime Library Exception.
  See LICENSE file in the project root for full license terms.
}

program Phase2Milestone;

{ Phase 2 milestone: singly-linked list using TObject descendants.
  Exercises:
    - Class inheritance (TNode → TMarkedNode)
    - Virtual method dispatch (GetTag override)
    - Properties (Value, Next, Count, Head)
    - Self-referential class field (FNext: TNode)
    - try/finally for guaranteed cleanup
    - 'is' type test
  Must compile, produce correct output, and show zero valgrind leaks. }

type
  { Base list node: integer value + link to next }
  TNode = class
    FValue: Integer;
    FNext:  TNode;
    function GetTag: Integer; virtual;
    property Value: Integer read FValue;
    property Next:  TNode   read FNext;
  end;

  { Marked node: identical to TNode but GetTag returns 1 instead of 0 }
  TMarkedNode = class(TNode)
    function GetTag: Integer; override;
  end;

  { Singly-linked list, push-at-front / pop-from-front }
  TLinkedList = class
    FHead:  TNode;
    FCount: Integer;
    procedure Push(Value: Integer);
    procedure PushMarked(Value: Integer);
    function  Pop: Integer;
    procedure Walk;
    procedure Clear;
    property Count: Integer read FCount;
    property Head:  TNode   read FHead;
  end;

{ ---- TNode ---- }

function TNode.GetTag: Integer;
begin
  Result := 0
end;

{ ---- TMarkedNode ---- }

function TMarkedNode.GetTag: Integer;
begin
  Result := 1
end;

{ ---- TLinkedList ---- }

procedure TLinkedList.Push(Value: Integer);
var
  N: TNode;
begin
  N        := TNode.Create;
  N.FValue := Value;
  N.FNext  := Self.FHead;
  Self.FHead  := N;
  Self.FCount := Self.FCount + 1
end;

procedure TLinkedList.PushMarked(Value: Integer);
var
  N: TMarkedNode;
begin
  N        := TMarkedNode.Create;
  N.FValue := Value;
  N.FNext  := Self.FHead;
  Self.FHead  := N;
  Self.FCount := Self.FCount + 1
end;

function TLinkedList.Pop: Integer;
var
  N: TNode;
begin
  N           := Self.FHead;
  Result      := N.FValue;
  Self.FHead  := N.FNext;
  Self.FCount := Self.FCount - 1;
  N.Free()
end;

procedure TLinkedList.Walk;
var
  N:      TNode;
  Marked: Boolean;
begin
  N := Self.FHead;
  while N <> nil do
  begin
    WriteLn('  value=', N.Value);
    WriteLn('  tag=', N.GetTag());
    Marked := N is TMarkedNode;
    WriteLn('  marked=', Marked);
    N := N.Next
  end
end;

procedure TLinkedList.Clear;
var
  N:    TNode;
  Next: TNode;
begin
  N := Self.FHead;
  while N <> nil do
  begin
    Next := N.FNext;
    N.Free();
    N := Next
  end;
  Self.FHead  := nil;
  Self.FCount := 0
end;

{ ---- Main ---- }

var
  List: TLinkedList;
  V:    Integer;

begin
  List := TLinkedList.Create;
  try
    { Build list: push 10, 20 (plain), 30 (marked), 40 (plain)
      After four pushes the front is 40 → 30(marked) → 20 → 10 }
    List.Push(10);
    List.Push(20);
    List.PushMarked(30);
    List.Push(40);

    WriteLn('count=', List.Count);          { 4 }
    WriteLn('--- walk ---');
    List.Walk();                             { 40/tag0, 30/tag1/marked, 20/tag0, 10/tag0 }

    { Pop two values off the front }
    V := List.Pop();
    WriteLn('pop=', V);                     { 40 }
    V := List.Pop();
    WriteLn('pop=', V);                     { 30 }

    WriteLn('count_after_pops=', List.Count);   { 2 }
  finally
    List.Clear();
    List.Free()
  end
end.
