unit uDifferences;
{$INCLUDE 'DirSync3.inc'}

interface

uses
  SysUtils, Classes, Generics.Collections, Generics.Defaults,
  uDirSyncConsts, uFiles;

type
  TDifferenceItem = class
  private
    fFileName: string;
    fIsDirectory: boolean;
    fDifferenceType: TDifferenceType;
    fUsedMatch: TUsedMatch;
    function GetAsString: string;
  public
    property FileName: string read fFileName write fFileName;
    property IsDirectory: boolean read fIsDirectory write fIsDirectory;
    property DifferenceType: TDifferenceType read fDifferenceType write fDifferenceType;
    property UsedMatch: TUsedMatch read fUsedMatch write fUsedMatch;
    property AsString: string read GetAsString;
    function SetAsString(const Value: string): boolean;
  end;

  TDifferenceList = class(TObjectList<TDifferenceItem>)
  private
    function GetHasDifference(DiffType: TDifferenceType): boolean;
  public
    procedure LoadFromFile(const FileName: string);
    property HasDifference[DiffType: TDifferenceType]: boolean read GetHasDifference;
  end;

implementation

const
  DIRECTORY_SUFFIX = '\*';
  DIRECTORY_SUFFIX_LENGTH = Length(DIRECTORY_SUFFIX);

{ TDifferenceFileItem }

function TDifferenceItem.GetAsString: string;
const
  DirectorySuffix: array[boolean] of string = ('', DIRECTORY_SUFFIX);
begin
  Result := DifferenceTypes[DifferenceType] + UsedMatches[UsedMatch] + #9 + FileName + DirectorySuffix[IsDirectory];
end;

function TDifferenceItem.SetAsString(const Value: string): boolean;
const
  DIFFERENCE_LENGTH = 4;
var
  Direction, FN, Match: string;
  CurLen, CurPos, UsedMatchPos, FNLen: integer;
  DiffType: TDifferenceType;
  UsedMatch, UsedMatchIterator: TUsedMatch;
  IsDirectory: boolean;
begin
  Result := False;
  CurLen := Length(Value);
  if CurLen > Succ(DIFFERENCE_LENGTH) then begin
    Direction := Copy(Value, 1, DIFFERENCE_LENGTH);
    for DiffType := Low(TDifferenceType) to High(TDifferenceType) do
      if Direction = DifferenceTypes[DiffType] then begin
        if DiffType <> dtEqual then begin
          CurPos := Succ(DIFFERENCE_LENGTH);
          UsedMatchPos := CurPos;
          while (CurPos <= CurLen) and (Value[CurPos] <> #9) do
            Inc(CurPos);
          UsedMatch := umUnknown;
          if CurPos > UsedMatchPos then begin
            Match := Copy(Value, UsedMatchPos, CurPos - UsedMatchPos);
            for UsedMatchIterator := Low(TUsedMatch) to High(TUsedMatch) do
              if Match = UsedMatches[UsedMatchIterator] then begin
                UsedMatch := UsedMatchIterator;
                Break;
              end;
          end;
          FN := Copy(Value, Succ(CurPos), MaxInt);
          IsDirectory := False;
          FNLen := Length(FN);
          if FNLen > 2 then
            if Copy(FN, FNLen - Pred(DIRECTORY_SUFFIX_LENGTH), DIRECTORY_SUFFIX_LENGTH) = DIRECTORY_SUFFIX then begin
              IsDirectory := True;
              SetLength(FN, FNLen - DIRECTORY_SUFFIX_LENGTH);
            end;
          Self.fFileName := FN;
          Self.fIsDirectory := IsDirectory;
          Self.fDifferenceType := DiffType;
          Self.fUsedMatch := UsedMatch;
          Result := True;
        end;
        Break;
      end;
  end;
end;

{ TDifferenceList }

function TDifferenceList.GetHasDifference(DiffType: TDifferenceType): boolean;
var
  i: integer;
begin
  Result := False;
  for i := 0 to Pred(Count) do
    if Items[i].DifferenceType = DiffType then begin
      Result := True;
      Break;
    end;
end;

procedure TDifferenceList.LoadFromFile(const FileName: string);
const
  DIFFERENCE_LENGTH = 4;
var
  Str: TTextStream;
  Line: string;
  Item: TDifferenceItem;
begin
  Clear;
  Str := TTextStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    while Str.ReadLine(Line) do begin
      Item := TDifferenceItem.Create;
      try
        if Item.SetAsString(Line) then begin
          Self.Add(Item);
        end
        else
          FreeAndNil(Item);
      except
        FreeAndNil(Item);
        Raise;
      end;
    end;
  finally
    FreeAndNil(Str);
  end;
end;

end.
