// TODO Rename it to DEngAssets
{$MODE OBJFPC}
{$MODESWITCH result}
{$MODESWITCH autoderef}
{$MODESWITCH duplicatelocals}
unit DEngStreamAssets;

interface

uses
  dtypes,
  dunpacker,
  {$IFDEF ANDROID}asset_manager, {$ELSE}dstreamfile,{$ENDIF}
  dstream;

type
PStreamAsset = ^TStreamAsset;
{$IFDEF ANDROID}
TStreamAsset = object(TStream)
private
  FAsset: PAAsset;
public
  constructor Init(const FileName: TString);
  destructor Done; virtual;
  function Create(const FileName: TString): PStreamAsset; static; inline;
protected
  function DoRead(P: Pointer; Size: Integer): Integer; virtual;
  function DoSeek(Offset: TInt64; Origin: TSeekOrigin): TInt64; virtual;
  function DoGetPosition: Int64; virtual;
  function DoEof: Boolean; virtual;
  function DoGetSize: Int64; virtual;
end;
{$ELSE}
TStreamAsset = TStreamFile;
{$ENDIF}

function Asset(const FileName: TString): TData;

// not thread safe
procedure MountDojPack(Data: TData);
procedure UnmountDojPack(const Path: TString);
procedure UnmountAllDojPacks;

implementation

uses
  Deng,
{$IFDEF ANDROID}
  dstrnum,
  ctypes,
  log,
{$ENDIF}
  DWinApi;

type
TMountedPack = record
  Name: AnsiString;
  Unpacker: TDojUnpacker;
end;

var
  MountedPacks: array of TMountedPack;

{$IFDEF ANDROID}
constructor TStreamAsset.Init(const FileName: TString);
begin
  inherited Init;
  FAsset := AAssetManager_open(_AndroidState^.activity^.assetManager, PAnsiChar(FileName), AASSET_MODE_RANDOM);
  FWritable := False;
  FName := FileName;
end;

destructor TStreamAsset.Done;
begin
  AAsset_close(FAsset);
end;

function TStreamAsset.Create(const FileName: TString): PStreamAsset;
begin
  Result := New(PStreamAsset, Init(FileName));
end;

function TStreamAsset.DoRead(P: Pointer; Size: Integer): Integer;
begin
  Result := AAsset_read(FAsset, P, Size);
end;

function TStreamAsset.DoSeek(Offset: TInt64; Origin: TSeekOrigin): TInt64;
const
  SEEK_SET = 0; // TODO
  SEEK_CUR = 1; // TODO
  SEEK_END = 2; // TODO
var
  Whence: coff_t;
begin
  case Origin of
    SO_BEGINNING: Whence := SEEK_SET;
    SO_CURRENT: Whence := SEEK_CUR;
    SO_END: Whence := SEEK_END;
  else
    LOGW(PAnsiChar('Unknown origin value: ' + IntToStr(Ord(Origin))));
    Whence := SEEK_SET;
  end;
  LOGW(PAnsiChar('AAsset_seek: ' + IntToStr(Offset) + ' ' + IntToStr(Whence)));
  Result := AAsset_seek(FAsset, Offset, Whence);
end;

function TStreamAsset.DoGetPosition: Int64;
begin
  Result := AAsset_seek(FAsset, 0, 1); // TODO
end;

function TStreamAsset.DoEof: Boolean;
begin
  Result := AAsset_getRemainingLength(FAsset) <= 0; // TODO can we do it better?
end;

function TStreamAsset.DoGetSize: Int64;
begin
  Result := AAsset_getLength(FAsset); // TODO 64-bit version?
end;

function Asset(const FileName: TString): TData;
begin
  Result := AsData(TStreamAsset.Create(FileName));
end;

{$ELSE}
function Asset(const FileName: TString): TData;
begin
  Result := Data(FileName);
end;

{$ENDIF}

procedure MountDojPack(Data: TData);
var
  Name: AnsiString;
  I: LongInt;
begin
  Name := Data.Stream.Name;
  // First check we have not mount the pack yet
  if Name <> '' then begin
    for I := 0 to High(MountedPacks) do begin
      if (MountedPacks[I].Name <> '') and (MountedPacks[I].Name = Name) then begin
        // Pack has been already mounted, we do not needed to duplicate it
        Exit;
      end;
    end;
  end;
  // Now add the pack
  SetLength(MountedPacks, Length(MountedPacks) + 1);
  MountedPacks[High(MountedPacks)].Name := Name;
  MountedPacks[High(MountedPacks)].Unpacker.Init(Data);
  DEng.LogTagged('R', 'MOUNTED ' + Name);
end;

procedure UnmountDojPack(const Path: TString);
var
  I, J: LongInt;
begin
  for I := 0 to High(MountedPacks) do begin
    if (MountedPacks[I].Name <> '') and (MountedPacks[I].Name = Path) then begin
      MountedPacks[I].Unpacker.Done;
      for J := I to High(MountedPacks) - 1 do
        MountedPacks[J] := MountedPacks[J + 1];
      SetLength(MountedPacks, Length(MountedPacks) - 1);
      DEng.LogTagged('R', 'UNMOUNTED ' + Path);
    end;
  end;
end;

procedure UnmountAllDojPacks;
var
  I: LongInt;
  Name: AnsiString;
begin
  for I := High(MountedPacks) downto 0 do begin
    Name := MountedPacks[I].Name;
    MountedPacks[I].Unpacker.Done;
    DEng.LogTagged('R', 'UNMOUNTED ' + Name);
  end;
  SetLength(MountedPacks, 0);
end;

end.
