{ 
	Include->Lists - Linked List Data Structures & Helpers.
	
	@author(Kieron Morris <kjm@kieronmorris.me>)
}
unit lists;

interface

uses
    console,
    lmemorymanager,
    util,
    strings,
    tracer;

type
    { Managed Linked List }

    PLinkedList = ^TLinkedList;
    TLinkedList = record
        Previous : PLinkedList;
        Data     : void;
        Next     : PLinkedList;
    end;

    PLinkedListBase = ^TLinkedListBase;
    TLinkedListBase = record
        Count       : uint32;
        Head        : PLinkedList;
        ElementSize : uint32;
    end;

{ String Linked List }

procedure  STRLL_Add(LinkedList : PLinkedListBase; str : pchar);
function   STRLL_Get(LinkedList : PLinkedListBase; idx : uint32) : pchar;
function   STRLL_New : PLinkedListBase;
function   STRLL_Size(LinkedList : PLinkedListBase) : uint32;
procedure  STRLL_Delete(LinkedList : PLinkedListBase; idx : uint32);
procedure  STRLL_Free(LinkedList : PLinkedListBase);
procedure  STRLL_Clear(LinkedList : PLinkedListBase);
function   STRLL_FromString(str : pchar; delimter : char) : PLinkedListBase;

{ Managed Linked List }

function  LL_New(ElementSize : uint32) : PLinkedListBase;
function  LL_Add(LinkedList : PLinkedListBase) : Void;
function  LL_Delete(LinkedList : PLinkedListBase; idx : uint32) : boolean;
function  LL_Size(LinkedList : PLinkedListBase) : uint32;
function  LL_Insert(LinkedList : PLinkedListBase; idx : uint32) : Void;
function  LL_Get(LinkedList : PLinkedListBase; idx : uint32) : Void;
procedure LL_Free(LinkedList : PLinkedListBase);
function  LL_FromString(str : pchar; delimter : char) : PLinkedListBase;

implementation

{ Managed Linked List }

function LL_New(ElementSize : uint32) : PLinkedListBase;
begin
    LL_New:= PLinkedListBase(kalloc(sizeof(TLinkedListBase)));
    LL_New^.ElementSize:= ElementSize;
    LL_New^.Count:= 0;
    LL_New^.Head:= nil;
end;

function LL_Add(LinkedList : PLinkedListBase) : Void;
var
    Element : PLinkedList;
    Base    : PLinkedList;
    Count   : uint32;

begin
    if LinkedList^.Head = nil then begin
        Element:= PLinkedList(kalloc(sizeof(TLinkedList)));
        Element^.Previous:= nil;
        Element^.Next:= nil;
        Element^.Data:= kalloc(LinkedList^.ElementSize);
        memset(uint32(Element^.Data), 0, LinkedList^.ElementSize);
        LinkedList^.Head:= Element;
        LinkedList^.Count:= LinkedList^.Count + 1;
        LL_Add:= Element^.Data;
    end else begin
        Base:= LinkedList^.Head;
        Count:= 1;
        While Base^.Next <> nil do begin
            Base:= Base^.Next;
            Count:= Count + 1;
        end;
        Element:= PLinkedList(kalloc(sizeof(TLinkedList)));
        Base^.Next:= Element;
        Element^.Previous:= Base;
        Element^.Next:= nil;
        Element^.Data:= kalloc(LinkedList^.ElementSize);
        memset(uint32(Element^.Data), 0, LinkedList^.ElementSize);
        LinkedList^.Count:= LinkedList^.Count + 1;
        LL_Add:= Element^.Data;
    end;
end;

function LL_Delete(LinkedList : PLinkedListBase; idx : uint32) : boolean;
var
    Prev, Next : PLinkedList;
    Base : PLinkedList;
    i : uint32;

begin
    Base:= LinkedList^.Head;
    i:= 0;
    while (i < idx) and (Base <> nil) do begin
        i:= i + 1;
        Base:= Base^.Next;
    end;    
    if Base = nil then begin
        LL_Delete:= false;
        exit;
    end;
    Prev:= Base^.Previous;
    Next:= Base^.Next;
    if Prev = nil then begin
        LinkedList^.Head:= Next;
    end else begin
        Prev^.Next:= Next;
    end;
    if Next <> nil then begin
        Next^.Previous:= Prev;
    end;
    LinkedList^.Count:= LinkedList^.Count - 1;
    kfree(void(Base^.Data));
    kfree(void(Base));
    LL_Delete:= True;
end;

function LL_Size(LinkedList : PLinkedListBase) : uint32;
begin
    LL_Size:= LinkedList^.Count;
end;

function LL_Insert(LinkedList : PLinkedListBase; idx : uint32) : void;
var
    i : uint32;
    Base : PLinkedList;
    Prev, Next : PLinkedList;
    Element    : PLinkedList;

begin
    LL_Insert:= nil;
    if idx > LinkedList^.Count then exit;
    Base:= LinkedList^.Head;
    i:=0;
    while (i < idx) and (Base <> nil) do begin
        i:= i + 1;
        Base:= Base^.Next;
    end;
    if i = 0 then begin
        Element:= PLinkedList(kalloc(sizeof(TLinkedList)));
        Element^.Data:= kalloc(LinkedList^.ElementSize);
        memset(uint32(Element^.Data), 0, LinkedList^.ElementSize);
        Element^.Next:= LinkedList^.Head;
        Element^.Previous:= nil;
        LinkedList^.Head:= Element;
        LinkedList^.Count:= LinkedList^.Count + 1;
        LL_Insert:= Element^.Data;
    end else begin
        if Base = nil then exit;
        Prev:= Base^.Previous;
        Next:= Base;
        Element:= PLinkedList(kalloc(sizeof(TLinkedList)));
        Element^.Data:= kalloc(LinkedList^.ElementSize);
        memset(uint32(Element^.Data), 0, LinkedList^.ElementSize);
        Element^.Previous:= Prev;
        Element^.Next:= Next;
        if Prev = nil then begin
            LinkedList^.Head:= Element;
        end else begin
            Prev^.Next:= Element;
        end;
        if Next <> nil then begin
            Next^.Previous:= Element;
        end;
        LinkedList^.Count:= LinkedList^.Count + 1;
        LL_Insert:= Element^.Data;
    end;
end;

function LL_Get(LinkedList : PLinkedListBase; idx : uint32) : void;
var
    i : uint32;
    Base : PLinkedList;

begin
    LL_Get:= nil;
    if idx >= LinkedList^.Count then exit;
    Base:= LinkedList^.Head;
    i:=0;
    while (i < idx) and (Base <> nil) do begin
        i:= i + 1;
        Base:= Base^.Next;
    end;
    if Base = nil then exit;
    LL_Get:= Base^.Data;
end;

procedure LL_Free(LinkedList : PLinkedListBase);
begin
    while LL_Size(LinkedList) > 0 do begin
        LL_Delete(LinkedList, 0);
    end;
    kfree(void(LinkedList));
end;

function LL_FromString(str : pchar; delimter : char) : PLinkedListBase;
var
    list       : PLinkedListBase;
    i          : uint32 = 0;  
    out_str    : pchar;
    elm        : puint32;
    head       : pchar;
    tail       : pchar;
    size       : uint32;
    null_delim : boolean;

begin
    list := LL_New(sizeof(uint32));
    LL_FromString:= list; 

    head:= str;
    tail:= head;

    null_delim:= false;
    while not null_delim do begin
        if (head^ = delimter) or (head^ = char(0)) then begin
            if head^ = char(0) then null_delim:= true;
            size:= uint32(head) - uint32(tail);
            if size > 0 then begin
                elm:= puint32(LL_Add(list));
                out_str:= stringNew(size + 1); //maybe
                memcpy(uint32(tail), uint32(out_str), size);
                elm^:= uint32(out_str);
            end;
            tail:= head+1;
        end;
        inc(head);
    end;
end;

{ String Linked List }

procedure STRLL_Add(LinkedList : PLinkedListBase; str : pchar);
var
    ptr : puint32;

begin
    ptr:= puint32(LL_Add(LinkedList));
    ptr^:= uint32(str);
end;

function STRLL_Get(LinkedList : PLinkedListBase; idx : uint32) : pchar;
var
    ptr : puint32;

begin
    ptr:= puint32(LL_Get(LinkedList, idx));
    STRLL_Get:= nil;
    if ptr <> nil then begin
        STRLL_Get:= pchar(ptr^);
    end;
end;

function STRLL_New : PLinkedListBase;
begin
    STRLL_New:= LL_New(sizeof(uint32));   
end;

function STRLL_Size(LinkedList : PLinkedListBase) : uint32;
begin
    STRLL_Size:= LL_Size(LinkedList);
end;

procedure STRLL_Delete(LinkedList : PLinkedListBase; idx : uint32);
var
    ptr : pchar;

begin
    tracer.push_trace('lists.STRLL_Delete (Don''t add static strings)');
    ptr:= STRLL_get(LinkedList, idx);
    kfree(void(ptr));
    LL_Delete(LinkedList, idx);
end;

procedure STRLL_Free(LinkedList : PLinkedListBase);
begin
    STRLL_Clear(LinkedList);
    LL_Free(LinkedList);
end;

procedure STRLL_Clear(LinkedList : PLinkedListBase);
begin
    while STRLL_Size(LinkedList) > 0 do STRLL_Delete(LinkedList, 0);
end;

function STRLL_FromString(str : pchar; delimter : char) : PLinkedListBase;
var
    list       : PLinkedListBase;
    i          : uint32 = 0;  
    out_str    : pchar;
    elm        : puint32;
    head       : pchar;
    tail       : pchar;
    size       : uint32;
    null_delim : boolean;

begin
    list := LL_New(sizeof(uint32));
    STRLL_FromString:= list; 

    head:= str;
    tail:= head;

    null_delim:= false;
    while not null_delim do begin
        if (head^ = delimter) or (head^ = char(0)) then begin
            if head^ = char(0) then null_delim:= true;
            size:= uint32(head) - uint32(tail);
            if size > 0 then begin
                elm:= puint32(LL_Add(list));
                out_str:= stringNew(size + 1); //maybe
                memcpy(uint32(tail), uint32(out_str), size);
                elm^:= uint32(out_str);
            end;
            tail:= head+1;
        end;
        inc(head);
    end;
end;

end.