unit concattool;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  StdCtrls, ComCtrls, ExtCtrls, Buttons
  , strutils
  , Contnrs
  , LazFileUtils //required for ExtractFileNameOnly
  ;

type

  { TConcatToolForm }

  TConcatToolForm = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    ProcessStatusMemo: TMemo;
    InputFileListMemo: TMemo;
    ResetDirectoryButton: TBitBtn;
    SourceDirectoryButton: TBitBtn;
    SourceDirectoryEdit: TEdit;
    Memo1: TMemo;
    ProgressBar1: TProgressBar;
    SelectDirectoryDialog1: TSelectDirectoryDialog;
    StartButton: TButton;
    StatusBar1: TStatusBar;
    procedure ResetDirectoryButtonClick(Sender: TObject);
    procedure StartButtonClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure SourceDirectoryButtonClick(Sender: TObject);
  private

  public

  end;

var
  ConcatToolForm: TConcatToolForm;

implementation
uses
  appsettings, Unit1
;

type
  TFileDetails = class
    Name: String;
    Size, Time: int64;
  end;

const
  Section='ConcatAnalyze';

var
  SourceDirectory:String;

  { TForm8 }

procedure FileListAppendFileNames(const AFileList: TObjectList; const APath: TFileName);
var
  LDetails: TFileDetails;
  LSearchRec: TSearchRec;
begin
  if FindFirst(APath + '*.dat', 0, LSearchRec) = 0 then begin
    try
      repeat
        LDetails := TFileDetails.Create;
        LDetails.Name := LSearchRec.Name;
        LDetails.Size := LSearchRec.Size;
        LDetails.Time := LSearchRec.Time;
        //Do not include previously saved output file in input list
        if not AnsiContainsStr(LDetails.Name, '_concat') then
           AFileList.Add(LDetails);
      until FindNext(LSearchRec) <> 0;
    finally
      FindClose(LSearchRec);
    end;
  end;
end;

function CompareName(A, B: Pointer): Integer;
begin
  Result := AnsiCompareFilename(TFileDetails(A).Name, TFileDetails(B).Name);
end;

function CompareSize(A, B: Pointer): Integer;
begin
  Result := TFileDetails(A).Size - TFileDetails(B).Size;
end;

function CompareTime(A, B: Pointer): Integer;
begin
  Result := TFileDetails(A).Time - TFileDetails(B).Time;
end;


procedure FillList(Directory:String);
var
  LIndex: Integer;
  LFileList: TObjectList;
begin
  LFileList := TObjectList.Create;
  Directory:=Directory;
  try
    ConcatToolForm.InputFileListMemo.Clear;
    ConcatToolForm.ProcessStatusMemo.Clear;
    FileListAppendFileNames(LFileList, Directory);
    LFileList.Sort(@CompareName);
    for LIndex := 0 to LFileList.Count - 1 do begin
      ConcatToolForm.InputFileListMemo.Append(TFileDetails(LFileList[LIndex]).Name);
    end;
  finally
    { Update the progress bar maximum. }
    ConcatToolForm.ProgressBar1.Max:=LFileList.Count;
    ConcatToolForm.ProgressBar1.Position:=0;

    FreeAndNil(LFileList);
  end;
end;

procedure TConcatToolForm.SourceDirectoryButtonClick(Sender: TObject);
begin
  SelectDirectoryDialog1.FileName:=SourceDirectory;
  if SelectDirectoryDialog1.Execute then begin
    SourceDirectory:=RemoveMultiSlash(SelectDirectoryDialog1.FileName+DirectorySeparator);
    SourceDirectoryEdit.Text:=SourceDirectory;
  end;

  //Save directory name in registry
  vConfigurations.WriteString(Section,'SourceDirectory',SourceDirectory);

  FillList(SourceDirectory);
end;

{ Concatenate files in input file list.}
procedure TConcatToolForm.StartButtonClick(Sender: TObject);
Var
    Count : Longint;
    Str: String;
    InFile,OutFile: TextFile;
    InputFileName:String;
    OutputPathFileName, InputPathFileName:String;
    WorkingPath, OutputPath:String;
    WriteAllowable: Boolean = True; //Allow output file to be written or not.

    LIndex: Integer;
    LFileList: TObjectList;

Begin

  {Update the file list in case it recently changed, and set the progress bar maximum.}
  FillList(SourceDirectory);

  WorkingPath:=RemoveMultiSlash(SourceDirectory + DirectorySeparator);
  OutputPath:=WorkingPath;

  { So far there ar no conditions to prevent writing files.
    The output directory either already exists, or has been created.
    The output files will overwrite previous output files.}
  WriteAllowable:=True;

  if WriteAllowable then begin

  { Process the files }

  Count:=0;
  LFileList := TObjectList.Create;

  try
    FileListAppendFileNames(LFileList, SourceDirectory);
    LFileList.Sort(@CompareName);

    { Define Output file to be the same as the input filename with a special extension. }
    OutputPathFileName:=OutputPath+LazFileUtils.ExtractFileNameWithoutExt(TFileDetails(LFileList[0]).Name)+'_concat.dat';
    AssignFile(OutFile, OutputPathFileName);
    Rewrite(OutFile); //Open file for writing

    for LIndex := 0 to LFileList.Count - 1 do begin
      InputFileName:=TFileDetails(LFileList[LIndex]).Name;
      Inc(Count);

      {Start reading file.}

      { Define Input file. }
      InputPathFileName:=WorkingPath+InputFileName;
      AssignFile(InFile, InputPathFileName);
      ProcessStatusMemo.Append('Processing: '+InputFileName);
      Application.ProcessMessages;
      //ProcessStatusMemo.Update;

      {$I+}
      try
         Reset(InFile);

         StatusBar1.Panels.Items[0].Text:='Reading Input file';

         repeat
           // Read one line at a time from the file.
           Readln(InFile, Str);

           StatusBar1.Panels.Items[0].Text:='Processing : '+Str;

            begin
               { Copy over comment lines from first file only. }
               if (((LIndex = 0) or ((LIndex>0)) and (not AnsiStartsStr('#',Str)))) then
              //if ((LIndex = 0) or (LIndex>0))then
               WriteLn(OutFile,Str);

             end;
         until(EOF(InFile)); // EOF(End Of File) The the program will keep reading new lines until there is none.
         CloseFile(InFile);

         StatusBar1.Panels.Items[0].Text:='Finished file'+InputPathFileName;

       except
         on E: EInOutError do
         begin
          MessageDlg('Error', 'File handling error occurred. Details: '+E.ClassName+'/'+E.Message, mtError, [mbOK],0);
         end;
       end;

      ProgressBar1.Position:=Count;
      ProgressBar1.Update;

    end;//End of files processing
  finally
    Flush(OutFile);
    CloseFile(OutFile);

    FreeAndNil(LFileList);
  end;

  StatusBar1.Panels.Items[0].Text:='Finished all files. Results stored in :'+OutputPath;
  ProcessStatusMemo.Append('Finished processing files.');
  ProcessStatusMemo.Append('Results stored in: ');
  ProcessStatusMemo.Append(' '+OutputPath);

end;//End of WriteAllowable check.

end;

procedure TConcatToolForm.ResetDirectoryButtonClick(Sender: TObject);
begin
  {Reset}
  SourceDirectory:=RemoveMultiSlash(appsettings.LogsDirectoryDefault());
  SourceDirectoryEdit.Text:=SourceDirectory;

  {Save selection}
  vConfigurations.WriteString(Section,'SourceDirectory',SourceDirectory);

  {List files}
  FillList(SourceDirectory);
end;

procedure TConcatToolForm.FormShow(Sender: TObject);
begin
  SourceDirectory:=RemoveMultiSlash(vConfigurations.ReadString(Section, 'SourceDirectory', '')+DirectorySeparator);
  SourceDirectoryEdit.Text:=SourceDirectory;

  FillList(SourceDirectory);
end;

initialization
  {$I concattool.lrs}

end.

