// Puede haber más de una unidad en un fichero
// Unidad en Freya = espacio de nombres en C#
unit Freya.DataStructures.Stacks;

public  // public sustituye a interface

    // No hace falta la palabra clave type
    Stack = class[X]  // ¡La clase es genérica!
    protected
        Items: array of X;
        Count: Integer := 0;
    public
        // Los constructores en Freya tienen el mismo nombre...
        // ... que la clase, como en C++/C#
        constructor Stack(Capacity: Integer);
        // Pero pueden existir varios constructores con distintos parámetros
        constructor Stack;   // La palabra clave overload es innecesaria en Freya.

        // Un iterador "anónimo"
        iterator Stack: X;

        // Dos propiedades de sólo lectura
        property IsEmpty: Boolean; read;
        // Top tiene una precondición
        property Top: X; read;
            requires not IsEmpty;

        procedure Pop;
            requires not IsEmpty as not_empty; 
        procedure Push(V: X);
            ensures not IsEmpty,
                   Top = V as push_on_top;
    end;

private  // private sustituye a implementation

    constructor Stack[X](Capacity: Integer);
    begin
        // Como los constructores son anónimos, la construcción de...
        // ... instancias se parece a la de C++
        Items := new array[X](Capacity);
    end;

    // Esta versión del constructor no necesita cuerpo
    constructor Stack[X]: Self(128);

    // El iterador devuelve todos los elementos de la pila,
    // empezando por el último insertado
    iterator Stack[X]: X;
    begin
        // La variable del bucle se declara en el propio bucle,
        // como en C++/C#... o como en los bloques except de Delphi
        for I: Integer := Count – 1 downto 0 do
        begin
            Result := Items[I];
            Yield;
        end;
    end;

    // El método de acceso en lectura de una propiedad
    property Stack[X].IsEmpty: X;
    begin
        Result := Count = 0;
    end;

    property Stack[X].Top: X;
    begin
        Result := Items[Count - 1];
    end;

    procedure Stack[X].Pop;
    begin
        // Inc, Dec, Include y Exclude son problemáticos:
        // en Delphi eran procedimientos, pero Freya no admite...
        // ... procedimientos que no sean métodos.
        Count--;
    end;

    procedure Stack[X].Push(Value: X);
    begin
        Items[Count] := Value;
        Count++;
    ensures
        // El cliente de la clase no tiene a acceso a Count,
        // por lo que esta postcondición sería "incomprensible" para él.
        Count = old Count + 1;
    end;

end.