unit mmisc;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils,bzip2, bzip2comn,bzip2stream, libtar,mufasabase,mufasatypes;

function DecompressBZip2(const input : TStream; const BlockSize : Cardinal = 4096) : TMemoryStream;
function UnTar(const Input : TStream) : TStringArray;overload;
function UnTar(const Input : TStream;const outputdir : string; overwrite : boolean): boolean;overload;

type
  { TProcThread }
  TProcThread = class(TThread)
  public
    StartWait : Cardinal;
    ClassProc : procedure of object;
    NormalProc : procedure;
    constructor Create;
    procedure Execute; override;
  end;

  { TDecompressThread }

  TDecompressThread = class(TThread)
  private
    FFinished : boolean;
    Finput : TStream;
    FBlockSize : Cardinal;
  public
    Result : TMemoryStream;
    constructor Create(const input : TStream; const BlockSize : Cardinal = 4096);
    procedure Execute; override;
    property Finished : boolean read FFinished;
  end;

  { TUntarThread }

  TUntarThread = class(TThread)
  private
    FFinished : boolean;
    Finput : TStream;
    FOverWrite : boolean;
    FOutputDir : string;
  public
    Result : boolean;
    constructor Create(const Input : TStream;const outputdir : string; overwrite : boolean);
    procedure Execute; override;
    property Finished : boolean read FFinished;
  end;

  { TDownloadDecompressThread }

  TDownloadDecompressThread = class(TThread)
  private
    InputURL : string;
    Folder : string;
    FData : string;
    FOverWrite : boolean;
  public
    Done : boolean;
    Succeeded : boolean;
    property Rawdata : string read FData;
    constructor Create(const URL, OutputDir : string; const Overwrite : boolean);
    procedure execute; override;
  end;
implementation

uses
  FileUtil, internets;

function DecompressBZip2(const input: TStream; const BlockSize: Cardinal): TMemoryStream;
var
  Unzipper : TDecompressBzip2Stream;
  Blocks : array of Byte;
  ReadSize : cardinal;
begin
  SetLength(Blocks,BlockSize);
  try
    Unzipper := TDecompressBzip2Stream.Create(input);
  except
    on e : exception do
    begin;
      mDebugLn(e.message);
      exit;
    end;
  end;
  Result := TMemoryStream.Create;
  try
    repeat
      ReadSize := BlockSize;
      ReadSize := Unzipper.read(blocks[0],readsize);  //Read ReadSize amount of bytes.
      Result.Write(Blocks[0],ReadSize);
    until readsize = 0;
  except
    on e : EBzip2 do
     if E.ErrCode <> bzip2_endoffile then
       raise Exception.CreateFmt('Decompression error: %s %d',[e.message,e.errcode]);
  end;
  Unzipper.Free;
end;

function UnTar(const Input : TStream) : TStringArray;overload;
var
  Tar : TTarArchive;
  DirRec : TTarDirRec;
  Len : integer;
begin;
  Tar := TTarArchive.Create(input);
  Tar.reset;
  Len := 0;
  while Tar.FindNext(DirRec) do
  begin
    inc(len);
    SetLength(result,len*2);
    result[len*2-2] := DirRec.Name;
    result[len*2-1] := Tar.ReadFile;
  end;
  Tar.Free;
end;

function UnTar(const Input: TStream; const outputdir: string; overwrite: boolean): boolean; overload;
var
  Tar : TTarArchive;
  Succ : boolean;
  DirRec : TTarDirRec;
  FS : TFileStream;
begin;
  result := false;
  if not DirectoryExists(outputdir) then
    if not CreateDir(outputdir) then
      exit;
  Tar := TTarArchive.Create(input);
  Tar.reset;
  Succ := True;
  while Tar.FindNext(DirRec) do
  begin
    if (DirRec.FileType = ftDirectory) then
    begin;
      if not DirectoryExists(outputdir + DirRec.Name) and not CreateDir(outputdir + DirRec.Name) then
      begin
        Succ := false;
        break;
      end;
    end else if (DirRec.FileType = ftNormal) then
    begin;
      if FileExistsUTF8(outputdir + dirrec.name) and not overwrite then
        continue;
      try
        FS := TFileStream.Create(UTF8ToSys(outputdir +dirrec.name),fmCreate);
        tar.ReadFile(fs);
        FS.Free;
      except
        Succ := false;
        break;
      end;
    end else
      mDebugLn(format('Unknown filetype in archive. %s',[dirrec.name]));
  end;
  Tar.Free;
  Result := Succ;
end;

{ TDownloadDecompressThread }

constructor TDownloadDecompressThread.Create(const URL, OutputDir: string; const Overwrite : boolean);
begin
  inherited Create(true);
  FreeOnTerminate := False;
  InputURL:= URL;
  Folder:= OutputDir;
  Done := false;
  FData := '';
  Succeeded:= false;
  FOverWrite:= Overwrite;
end;

procedure TDownloadDecompressThread.execute;
var
  SStream : TStringStream;
  DecompressData : TMemoryStream;
begin
  Succeeded:= False;
  try
    if ((InputURL = '') or (Folder = '')) then
      Exit;
    FData := Getpage(InputURL);
    if FData = '' then
      Exit; //Goes to finally
    SStream := TStringStream.Create(FData);
    DecompressData := DecompressBZip2(SStream);
    if DecompressData = nil then
      Exit; //Goes to finally
    if not Untar(DecompressData,Folder,FOverwrite) then
      Exit;
    DecompressData.Free;
    SStream.free;
    Succeeded:= True;
  finally
    Done := true;
  end;
end;

constructor TProcThread.Create;
begin
  inherited Create(true);
  FreeOnTerminate:= True;
end;


{ TProcThread }

procedure TProcThread.Execute;
begin
  if startwait <> 0 then
    sleep(StartWait);
  if NormalProc <> nil then
    NormalProc;
  if ClassProc <> nil then
    ClassProc;
end;

constructor TDecompressThread.Create(const input: TStream;
  const BlockSize: Cardinal);
begin
  inherited Create(True);
  FFinished:= False;
  FBlockSize:= BlockSize;
  FInput := Input;
  Result := nil;
end;

{ TDecompressThread }

procedure TDecompressThread.Execute;
begin
  if Finput <> nil then
    result := DecompressBZip2(Finput,FBlocksize);
  Ffinished := True;
end;

{ TUntarThread }

constructor TUntarThread.Create(const Input: TStream; const outputdir: string;
  overwrite: boolean);
begin
  inherited Create(true);
  FFinished:= false;
  FInput := Input;
  FOutputDir:= OutputDir;
  FOverWrite:= overwrite;
  Result:= False;
end;

procedure TUntarThread.Execute;
begin
  if (Finput <> nil) and (FOutputDir <> '') then
    result := UnTar(FInput,Foutputdir,FOverWrite);
  FFinished:= True;
end;

end.