{**************************************************************************************************}
{                                                                                                  }
{ Project JEDI Code Library (JCL)                                                                  }
{                                                                                                  }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
{ you may not use this file except in compliance with the License. You may obtain a copy of the    }
{ License at http://www.mozilla.org/MPL/                                                           }
{                                                                                                  }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF   }
{ ANY KIND, either express or implied. See the License for the specific language governing rights  }
{ and limitations under the License.                                                               }
{                                                                                                  }
{ The Original Code is JclEDI_ANSIX12_Ext.pas.                                                     }
{                                                                                                  }
{ The Initial Developer of the Original Code is Raymond Alexander.                                 }
{ Portions created by Raymond Alexander are Copyright Raymond Alexander. All rights reserved.      }
{                                                                                                  }
{ Contributor(s):                                                                                  }
{   Raymond Alexander (rayspostbox3), Robert Rossmair                                              }
{                                                                                                  }
{**************************************************************************************************}
{                                                                                                  }
{ EDI ANSI X12 - Standard Exchange Format (*.sef) File Extensions                                  }
{                                                                                                  }
{ This unit is still in development                                                                }
{                                                                                                  }
{ Unit owner: Raymond Alexander                                                                    }
{ Date created: March 1, 2004                                                                      }
{ Additional Info:                                                                                 }
{   E-Mail at RaysDelphiBox3 att hotmail dott com                                                  }
{   For latest EDI specific demos see http://sourceforge.net/projects/edisdk                       }
{   See home page for latest news & events and online help.                                        }
{                                                                                                  }
{**************************************************************************************************}

// $Id: JclEDI_ANSIX12_Ext.pas,v 1.9 2005/03/08 08:33:16 marquardt Exp $

unit JclEDI_ANSIX12_Ext;

{$I jcl.inc}

{$IFDEF SUPPORTS_WEAKPACKAGEUNIT}
  {$WEAKPACKAGEUNIT ON}
{$ENDIF SUPPORTS_WEAKPACKAGEUNIT}

interface

uses
  SysUtils, Classes, Contnrs, JclResources, 
  JclEDI, JclEDI_ANSIX12, JclEDISEF;

type
  //  EDI Transaction Set Document and related types and classes
  TEDI_ANSIX12_Document = class(TEDITransactionSetLoop)
  private
    FEDISEFSet: TEDISEFSet;
  protected
    FErrorOccured: Boolean;
    FEDITSDOptions: TEDITransactionSetDocumentOptions;
    FEDILoopStack: TEDILoopStack;
    // References
    FEDITransactionSet: TEDITransactionSet;
    FEDITransactionSetSpec: TObjectList;
    function ValidateSegSpecIndex(DataSegmentId: string; SpecStartIndex: Integer): Integer;
    function AdvanceSegSpecIndex(DataIndex, SpecStartIndex, SpecEndIndex: Integer): Integer;
    procedure AddLoopToDoc(StackRecord: TEDILoopStackRecord;
      SegmentId, OwnerLoopId, ParentLoopId: string; var EDIObject: TEDIObject);
    procedure SetSpecificationPointers(DataSegment: TEDISegment; SpecSegment: TEDISEFSegment);
  protected
    procedure ValidateData(TSDocument: TEDI_ANSIX12_Document;
      LoopStack: TEDILoopStack;
      DataSegment: TEDISegment;
      SpecSegment: TEDISEFSegment;
      var DataIndex, SpecIndex: Integer;
      var ErrorOccured: Boolean); virtual;
  public
    constructor Create(Parent: TEDIDataObject; TransactionSet: TEDITransactionSet;
      SEFSet: TEDISEFSet); reintroduce;
    destructor Destroy; override;
    //
    //  ToDo:  More procedures and functions to manage internal structures
    //
    procedure FormatDocument; virtual;
  published
    property EDITSDOptions: TEDITransactionSetDocumentOptions read FEDITSDOptions
      write FEDITSDOptions;
    property ErrorOccured: Boolean read FErrorOccured;
  end;

implementation

constructor TEDI_ANSIX12_Document.Create(Parent: TEDIDataObject;
  TransactionSet: TEDITransactionSet; SEFSet: TEDISEFSet);
begin
  inherited Create(Parent);
  FEDILoopStack := TEDILoopStack.Create;
  FEDILoopStack.OnAddLoop := AddLoopToDoc;
  FEDITransactionSet := TransactionSet;
  FEDISEFSet := SEFSet; 
  FEDITransactionSetSpec := SEFSet.GetSegmentObjectList;
  FEDITSDOptions := [];
end;

destructor TEDI_ANSIX12_Document.Destroy;
begin
  FreeAndNil(FEDILoopStack);
  FEDITransactionSet := nil;
  FEDITransactionSetSpec.Free;
  inherited Destroy;
end;

procedure TEDI_ANSIX12_Document.FormatDocument;
var
  I, J: Integer;
  LSR: TEDILoopStackRecord;
  DataSegment: TEDISegment;
  SpecSegment: TEDISEFSegment;
begin
  I := 0;
  J := 0;
  if doLinkSpecToDataObject in FEDITSDOptions then
  begin
    FEDISEFSet.BindTextSets(FEDISEFSet.SEFFile.TEXTSETS);
    FEDISEFSet.BindSegmentTextSets;
  end;
  // Initialize the stack
  FEDILoopStack.Flags := FEDILoopStack.Flags - [ediLoopRepeated];
  LSR := FEDILoopStack.ValidateLoopStack(FEDITransactionSet.Segment[I].SegmentID,
    NA_LoopId, NA_LoopId, 0, Self);
  //
  while (I <= FEDITransactionSet.SegmentCount - 1) and
    (J <= FEDITransactionSetSpec.Count - 1) do
  begin
    FEDILoopStack.Flags := FEDILoopStack.Flags - [ediLoopRepeated];
    DataSegment := FEDITransactionSet.Segment[I];
    // If loop has repeated then move the spec index back
    J := ValidateSegSpecIndex(DataSegment.SegmentID, J);
    // Check current segment against segment spec
    SpecSegment := TEDISEFSegment(FEDITransactionSetSpec[J]);
    if DataSegment.SegmentID = SpecSegment.SegmentID then
    begin
      // Retrieve the correct record to use from the stack
      LSR := FEDILoopStack.ValidateLoopStack(SpecSegment.SegmentID, SpecSegment.OwnerLoopId,
        SpecSegment.ParentLoopId, J, LSR.EDIObject);
      //
      // Debug - Keep the following here in case someone wants to debug what happens to the stack.
      // ShowMessage('Current Data Segment: [' + IntToStr(I) + '] ' + DataSegment.SegmentID + #13#10 +
      //             'Current Spec Segment: [' + IntToStr(J) + '] ' + SpecSegment.SegmentID + #13#10 +
      //             FEDILoopStack.Debug);
      //
      // Do error checking and data validation in decendent class
      ValidateData(Self, FEDILoopStack, DataSegment, SpecSegment, I, J, FErrorOccured);
      if FErrorOccured then
        Exit;
      // Process Segment Id
      TEDITransactionSetLoop(LSR.EDIObject).AppendSegment(DataSegment);
      //
      if doLinkSpecToDataObject in FEDITSDOptions then
      begin
        SpecSegment.BindTextSets(SpecSegment.SEFFile.TEXTSETS);
        SpecSegment.BindElementTextSets;
        SetSpecificationPointers(DataSegment, SpecSegment);
      end;
      // Move to the next data segment
      Inc(I);
    end
    else
    begin
      // Do error checking and data validation in decendent class
      ValidateData(Self, FEDILoopStack, DataSegment, SpecSegment, I, J, FErrorOccured);
      if FErrorOccured then
        Exit;
      //
      // Debug - Keep the following here in case someone wants to debug what happens to the stack.
      // ShowMessage('Current Data Segment: [' + IntToStr(I) + '] ' + DataSegment.SegmentID + #13#10 +
      //             'Current Spec Segment: [' + IntToStr(J) + '] ' + SpecSegment.SegmentID + #13#10 +
      //             FEDILoopStack.Debug);
      //
      // Move to the next specification segment
      J := AdvanceSegSpecIndex(I, J, FEDITransactionSetSpec.Count - 1); //Inc(J);
    end;
  end;
end;

procedure TEDI_ANSIX12_Document.ValidateData(TSDocument: TEDI_ANSIX12_Document;
  LoopStack: TEDILoopStack; DataSegment: TEDISegment; SpecSegment: TEDISEFSegment;
  var DataIndex, SpecIndex: Integer; var ErrorOccured: Boolean);
begin
  ErrorOccured := False;
end;

procedure TEDI_ANSIX12_Document.SetSpecificationPointers(DataSegment: TEDISegment;
  SpecSegment: TEDISEFSegment);
var
  I, J: Integer;
begin
  DataSegment.SpecPointer := SpecSegment;
  J := SpecSegment.Elements.Count - 1;
  for I := 0 to DataSegment.ElementCount - 1 do
  begin
    if I > J then
      raise EJclEDIError.CreateResFmt(@RsEDIError058,
        [IntToStr(I), DataSegment.SegmentId,
         IntToStr(DataSegment.GetIndexPositionFromParent)]);
    DataSegment.Element[I].SpecPointer := SpecSegment.Elements[I];
  end;
end;

procedure TEDI_ANSIX12_Document.AddLoopToDoc(StackRecord: TEDILoopStackRecord;
  SegmentId, OwnerLoopId, ParentLoopId: string; var EDIObject: TEDIObject);
var
  I: Integer;
  Loop: TEDITransactionSetLoop;
begin
  Loop := TEDITransactionSetLoop(StackRecord.EDIObject);
  I := Loop.AddLoop(OwnerLoopId, ParentLoopId);
  EDIObject := TEDITransactionSetLoop(Loop[I]);
end;

function TEDI_ANSIX12_Document.ValidateSegSpecIndex(DataSegmentId: string;
  SpecStartIndex: Integer): Integer;
var
  I: Integer;
begin
  Result := SpecStartIndex;
  // Find the segment in the stack to determine if a loop has repeated
  for I := High(FEDILoopStack.Stack) downto Low(FEDILoopStack.Stack) do
  begin
    if (DataSegmentId = FEDILoopStack.Stack[I].SegmentId) and
      (FEDILoopStack.Stack[I].OwnerLoopId <> NA_LoopId) then
    begin
      FEDILoopStack.Flags := FEDILoopStack.Flags + [ediLoopRepeated];
      Result := FEDILoopStack.Stack[I].SpecStartIndex;
      Break;
    end;
  end;
end;

function TEDI_ANSIX12_Document.AdvanceSegSpecIndex(DataIndex, SpecStartIndex,
  SpecEndIndex: Integer): Integer;
var
  DataSegment: TEDISegment;
  TestSegment: TEDISEFSegment;
  I: Integer;
begin
  Result := SpecEndIndex + 1;
  DataSegment := FEDITransactionSet.Segment[DataIndex];
  for I := SpecStartIndex + 1 to SpecEndIndex do
  begin
    TestSegment := TEDISEFSegment(FEDITransactionSetSpec[I]);
    // Find matching segment
    if ((DataSegment.SegmentID) = (TestSegment.SegmentID)) then
    begin
      Result := I;
      Break;
    end;
  end;
end;

end.
