unit ThreadsC;
//*******************************************************
// Threaded linked list class.
//*******************************************************
// Copyright (C) 1998 John Wiley & Sons, Inc.
// All rights reserved. See additional copyright
// information in Readme.txt.
//*******************************************************

interface

uses
    Dialogs, SysUtils, Classes;

type
    // Types of thread moves.
    TMoveType = (mtNextName, mtPrevName, mtNextSSN,
                 mtNextJobClass, mtPrevJobClass);

    // The linked list cells.
    PThreadedListCell = ^TThreadedListCell;
    TThreadedListCell = record
        // The data.
        LastName  : String[20];
        FirstName : String[20];
        SSN       : String[11];
        Gender    : String[1];
        JobClass  : Integer;

        // Thread pointers.
        NextName     : PThreadedListCell;
        PrevName     : PThreadedListCell;
        NextSSN      : PThreadedListCell;
        NextJobClass : PThreadedListCell;
        PrevJobClass : PThreadedListCell;
    end;

    // The linked list class.
    TThreadedList = class(TObject)
        private
            TopSentinel    : TThreadedListCell; // The top list sentinel (not a pointer).
            BottomSentinel : TThreadedListCell; // The bottom list sentinel (not a pointer).
            NumItems       : Longint;           // For convenience.
            CurrentCell    : PThreadedListCell; // Currently selected item.

        public
            constructor Create;
            destructor Destroy; override;
            procedure Add(new_last_name, new_first_name,
                new_ssn, new_gender : String;
                new_job_class : Integer);
            procedure ClearList;
            function CurrentItem : String;
            function EndOfList : Boolean;
            procedure MoveFirst(move_type : TMoveType);
            procedure MoveNext(move_type : TMoveType);
    end;

implementation

// Initialize the sentinels so they point to each other.
// Initialize the sentinel values.
constructor TThreadedList.Create;
begin
    // Allocate memory and perform inherited initialization.
    inherited Create;

    // Point the sentinels at each other.
    TopSentinel.NextName := @BottomSentinel;
    BottomSentinel.PrevName := @TopSentinel;
    TopSentinel.NextSSN := @BottomSentinel;
    TopSentinel.NextJobClass := @BottomSentinel;
    BottomSentinel.PrevJobClass := @TopSentinel;

    // The top sentinel values are already initialized to
    // small values (empty strings). Initialize the
    // bottom sentinel values.
    BottomSentinel.LastName  := '~~~~~~~~~~~~~~~~~~~~';
    BottomSentinel.FirstName := '~~~~~~~~~~~~~~~~~~~~';
    BottomSentinel.SSN := '~~~~~~~~~~~';
    BottomSentinel.Gender := '~';
    BottomSentinel.JobClass := 32767;
end;

// Free any allocated memory.
destructor TThreadedList.Destroy;
begin
    ClearList;
    inherited Destroy;
end;

// Add a new record in the threads.
procedure TThreadedList.Add(new_last_name, new_first_name,
    new_ssn, new_gender : String; new_job_class : Integer);
var
    cell_ptr, new_cell : PThreadedListCell;
    combined_name      : String;
begin
    // Create the new cell.
    New(new_cell);
    new_cell.LastName  := new_last_name;
    new_cell.FirstName := new_first_name;
    new_cell.SSN := new_ssn;
    new_cell.Gender := new_gender;
    new_cell.JobClass := new_job_class;

    // Insert in the name threads.
    // Find the cell after this one.
    cell_ptr := @TopSentinel;
    combined_name := Format('%s, %s',
        [new_last_name, new_first_name]);
    while (Format('%s, %s', [cell_ptr^.LastName, cell_ptr^.FirstName])
        < combined_name) do
            cell_ptr := cell_ptr^.NextName;
    new_cell^.NextName := cell_ptr;
    new_cell^.PrevName := cell_ptr^.PrevName;
    new_cell^.PrevName^.NextName := new_cell;
    cell_ptr^.PrevName := new_cell;

    // Insert in the SSN thread.
    // Find the cell before this one.
    cell_ptr := @TopSentinel;
    while (cell_ptr^.NextSSN^.SSN < new_ssn) do
        cell_ptr := cell_ptr^.NextSSN;
    new_cell^.NextSSN := cell_ptr^.NextSSN;
    cell_ptr^.NextSSN := new_cell;

    // Insert in the job class threads.
    // Find the cell before this one.
    cell_ptr := @TopSentinel;
    while (cell_ptr^.JobClass < new_job_class) do
        cell_ptr := cell_ptr^.NextJobClass;
    new_cell^.NextJobClass := cell_ptr;
    new_cell^.PrevJobClass := cell_ptr^.PrevJobClass;
    new_cell^.PrevJobClass^.NextJobClass := new_cell;
    cell_ptr^.PrevJobClass := new_cell;

    NumItems := NumItems + 1;
end;

// Remove all items from the list.
procedure TThreadedList.ClearList;
var
    target : PThreadedListCell;
begin
    while (TopSentinel.NextName <> @BottomSentinel) do
    begin
        target := TopSentinel.NextName;
        TopSentinel.NextName := target^.NextName;
        Dispose(target);
    end;
    NumItems := 0;

    // Point the sentinels at each other again.
    TopSentinel.NextName := @BottomSentinel;
    BottomSentinel.PrevName := @TopSentinel;
    TopSentinel.NextSSN := @BottomSentinel;
    TopSentinel.NextJobClass := @BottomSentinel;
    BottomSentinel.PrevJobClass := @TopSentinel;
end;

// Return a textual representation of the current item.
function TThreadedList.CurrentItem : String;
const
    CR = #13#10;
begin
    if (EndOfList) then
        raise EInvalidOperation.Create(
            'No current cell selected.')
    else
        Result :=
            Format(
                'Name: %s, %s' + CR +
                'SSN: %s' + CR +
                'Gender: %s' + CR +
                'Job Class: %d' + CR + CR,
                [CurrentCell^.LastName,
                 CurrentCell^.FirstName,
                 CurrentCell^.SSN,
                 CurrentCell^.Gender,
                 CurrentCell^.JobClass]);
end;

// Return true if the current item is beyond
// the end of the list.
function TThreadedList.EndOfList : Boolean;
begin
    EndOfList :=
        ((CurrentCell = @TopSentinel) or
         (CurrentCell = @BottomSentinel));
end;

// Move CurrentCell to the first cell
// in the selected thread.
procedure TThreadedList.MoveFirst(move_type : TMoveType);
begin
    case move_type of
        mtNextName     : CurrentCell := TopSentinel.NextName;
        mtPrevName     : CurrentCell := BottomSentinel.PrevName;
        mtNextSSN      : CurrentCell := TopSentinel.NextSSN;
        mtNextJobClass : CurrentCell := TopSentinel.NextJobClass;
        mtPrevJobClass : CurrentCell := BottomSentinel.PrevJobClass;
    end;
end;

// Move CurrentCell to the next cell
// in the selected thread.
procedure TThreadedList.MoveNext(move_type : TMoveType);
begin
    if (not EndOfList) then
        case move_type of
            mtNextName     : CurrentCell := CurrentCell^.NextName;
            mtPrevName     : CurrentCell := CurrentCell^.PrevName;
            mtNextSSN      : CurrentCell := CurrentCell^.NextSSN;
            mtNextJobClass : CurrentCell := CurrentCell^.NextJobClass;
            mtPrevJobClass : CurrentCell := CurrentCell^.PrevJobClass;
        end;
end;

end.
