{
    This file is part of Chentrah,
    Copyright (C) 2004-2008 Anton Rzheshevski (chebmaster@mail.ru).

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses/

 **********************************************************************

    This file contains classes for working with file lists  and directory trees.
      
 **********************************************************************}

{$mode delphi}
{$longstrings on}
 
unit un_filelist;

interface

uses SysUtils, Classes;//, cl_strings;

type
  TFileList = class(TStringList)
  private
    SortedByDate: boolean;
    function _time(ind: integer): TDateTime;
    function _size(ind: integer): integer;
    function _attr(ind: integer): integer;
    function _sorted: boolean;
    function _msdostimestamp(index: integer): integer;
    procedure _setmsdostimestamp(index, value: integer);
    function _names(index: integer): string;
    function _only_name(index: integer): string;
  public
    constructor Create(pathmask: string); virtual; overload;
    constructor Create(pathmask: string; attr: longint); overload;
    procedure SortByName;
    procedure Sort; OVERRIDE;
    procedure SortByDate; //default
    procedure Merge(MergeTo: TFileList); //calls free
    destructor Destroy; Override;
    procedure SetName(c: integer; s: string);
    function IndexOf(s: string): integer;
    Property Time[index: integer]: TDateTime read _time;
    property MSDOSTimeStamp[index: integer]: integer read _msdostimestamp write _setmsdostimestamp;
    Property Size[index: integer]: integer read _size;
    Property Attributes[index: integer]: integer read _attr;
    Property Sorted: boolean read _sorted;
    Property Names[index: integer]: string read _names write SetName;
    property OnlyName[index: integer]: string read _only_name;
  end;

  TDirectoryList = class; //forward declaration

  TDirectory = class
    Constructor Create(path: string);
    Destructor Destroy; Override;
  private
    _files: TFileList;
    _dirs: TDirectoryList;
    _name, _path: string;
  public
    property Files: TFileList read _files;
    property Directories: TDirectoryList read _dirs;
    property Name: string read _name;
    property Path: string read _path;
  end;

  TDirectoryList = class(TStringList)
  private
    function _d(ind: integer): TDirectory;
  public
    constructor Create (path: string);
    destructor Destroy; Override;
    property Dir[index: integer]:TDirectory read _d;
  end;
  
  { TFullFileList }

  TFullFileList = class(TFileList)
  protected
    FBasePath: string;
//    function _relnames(index: integer): string;
  public
    constructor Create(path: string); override;
//    Property RelativeNames[index: integer]: string read _relnames;
  end;

implementation

type
  TFileRecord = class
  public
    Time: TDateTime;
    timestamp,
    Size,
    Attributes: Integer;
    Fullname: string;
  end;

{ TFileList }

function TFileList._attr(ind: integer): integer;
begin
  Result:=TFileRecord(Objects[ind]).Attributes;
end;

function TFileList._size(ind: integer): integer;
begin
  Result:=TFileRecord(Objects[ind]).Size;
end;

function TFileList._sorted: boolean;
begin
  Result:=not SortedByDate;
end;

function TFileList._time(ind: integer): TDateTime;
begin
  Result:=TFileRecord(Objects[ind]).time;
end;

function TFileList._msdostimestamp(index: integer): integer;
begin
  Result:=TFileRecord(Objects[index]).timestamp;
end;

procedure TFileList._setmsdostimestamp(index, value: integer);
begin
  TFileRecord(Objects[index]).timestamp:= value;
end;

constructor TFileList.Create(pathmask: string);
begin
  Create(pathmask, faAnyFile);
end;

constructor TFileList.Create(pathmask: string; attr: longint);
var
  SearchRec: TSearchRec;
  r: TFileRecord;
begin
  inherited Create;
  If FindFirst(pathmask, attr, SearchRec) = 0
  then Repeat
    r:=TFileRecord.Create;

 // ************ crashed in Linux (Mandrake 9.0, FPC 1.9.x)
 //   r.Time:={$ifdef unix} 0; {$else}FileDateToDateTime(SearchRec.Time);{$endif}
    r.Time:=FileDateToDateTime(SearchRec.Time);
    r.TimeStamp:=SearchRec.Time;
    r.Size:=SearchRec.Size;
    r.Attributes:=SearchRec.Attr;
    r.Fullname:=ExtractFilePath(pathmask) + SearchRec.Name;
    AddObject(intToHex(SearchRec.Time, 8) + SearchRec.Name, r);
    r:=nil;
  Until FindNext(SearchRec)<>0;
  SortByDate;
end;

destructor TFileList.Destroy;
var j: integer;
begin
  For j:=0 to Count-1 do begin
    Objects[j].Free;
    Objects[j]:=nil;
  end;
  inherited;
end;

procedure TFileList.Merge(MergeTo: TFileList); //calls free
var c: integer;
begin
  For c:=0 to Count- 1 do MergeTo.AddObject(names[c], Objects[c]);
  Self.Clear;
  Self.Free;
end;

procedure TFileList.Sort;
begin
  SortByName;
end;

procedure TFileList.SortByDate;
var j: integer;
begin
  if Count < 1 then exit;
  For j:=0 to Count-1 do
    Strings[j]:=IntToHex(MSDOSTimeStamp[j], 8) + Strings[j];
  inherited Sort;
  For j:=0 to Count-1 do
    Strings[j]:=Copy(Strings[j], 9, Length(Strings[j]) - 8);
  SortedByDate:=True;
end;

procedure TFileList.SortByName;
begin
  SortedByDate:=false;
  inherited;
end;


function TFileList._names(index: integer): string;
begin
  if not Assigned(Objects[index]) then exit('');
  Result:=TFileRecord(Objects[index]).Fullname;
end;

function TFileList._only_name(index: integer): string;
begin
  Result:= Copy(Strings[index], 9, Length(Strings[index]) - 8);
end;

procedure TFileList.SetName(c: integer; s: string);
begin
  TFileRecord(Objects[c]).Fullname:=s;
  Strings[c]:=Copy(Strings[c], 1, 8) + s;
end;

{ TDirectory }

constructor TDirectory.Create(path: string);
var c, p: integer;
begin
  inherited Create;
  _files:=TFileList.Create(path+ '*.*');
  _path:=path;
  c:=Length(_path) - 1;
  p:=1;
  while (c > 1) and not (_path[c] in ['/','\']) do dec(c);
  if (_path[c] in ['/','\']) then p:=c + 1;
  _name:=Copy(_path, p, Length(_path) - p); //+cuts trailing slash
  _dirs:=TDirectoryList.Create(path);
end;

destructor TDirectory.Destroy;
begin
  _files.Free;
  _dirs.Free;
  inherited;
end;

{ TDirectoryList }

function TDirectoryList._d(ind: integer): TDirectory;
begin
  Result:=TDirectory(Objects[ind]);
end;

constructor TDirectoryList.Create(path: string);
var
  SearchRec: TSearchRec;
  d: TDirectory;
begin
  inherited Create;
  If FindFirst(path+'*.*', faDirectory, SearchRec) = 0
  then Repeat
    if (SearchRec.Name <> '.') and (SearchRec.Name <> '..')
    then begin
      d:=TDirectory.Create(path + SearchRec.Name + DirectorySeparator);
      AddObject(d.Path, d);
    end;
  Until FindNext(SearchRec)<>0;
  Sort;
end;

destructor TDirectoryList.Destroy;
var j: integer;
begin
  For j:=0 to Count-1 do begin
    Objects[j].Free;
    Objects[j]:=nil;
  end;
  inherited;
end;

function TFileList.IndexOf(s: string): integer;
var c: integer;
begin
  Result:=-1;
  For c:=Count - 1 downto 0 do
    if UpperCase(ExtractFileName(Names[c])) = UpperCase(s)
      then Result:=c;
end;

{ TFullFileList }
{
function TFullFileList._relnames(index: integer): string;
begin
  Result:=StrReplace(ExtractRelativePath(FBasePath, ExtractFilePath(Names[index]))
             + ExtractFileName(Names[index]), '\', '/');
end;
}

constructor TFullFileList.Create(path: string);
var
  D: TDirectoryList;
  L: TFullFileList;
  c, i: integer;
begin
  FBasePath:=path;
  inherited Create(path + '*');
  D:=TDirectoryList.Create(path);
  For c:=0 to D.Count - 1 do begin
    L:=TFullFileList.Create(D[c]);
    L.Merge(Self);
  end;
end;



end.
