{$MODESWITCH DUPLICATELOCALS}
{$MODESWITCH AUTODEREF}
{
  TODO support for storing inverted textures for fast tga loading
  TODO ПОЛНЫЙ РЕФАКТОРИНГ ВСЕГО ЭТОГО МОДУЛЯ И ЕГО ИСПОЛЬЗОВАНИЕ!!!!!!11111111111
}
unit dtexture2d;

interface

uses
  dstrnum,
  derror,
  dscreenbuffer,
  dstream,
  dstreamrewinder,
  dtypes,
  dvector2i,
  dvector4f,
  dcolors;

const
  TEXTURE_2D_ENCODING_RGBA      = 0;
  TEXTURE_2D_ENCODING_RGB       = 1; // unsupported
  TEXTURE_2D_ENCODING_GRAY      = 2; // unsupported
  TEXTURE_2D_ENCODING_MONO      = 3; // unsupported

type
PTexture2d = ^TTexture2d;
TTexture2d = object
protected
  FData: Pointer;
  FWidth: integer;
  FHeight: Integer;
  FEncoding: LongInt;
public
  procedure Init; overload; inline;
  procedure InitRGBA(W, H: LongInt); overload;
  procedure InitRGBA(ScreenBuffer: PScreenBuffer); overload;
  procedure Done; inline;
  procedure FreeData;
  function IsRGBA: Boolean; inline;
  property Encoding: LongInt read FEncoding;
  property Data: Pointer read FData;
  property Width: Integer read FWidth;
  property Height: Integer read FHeight;
end;

PTexture2dRgba = ^TTexture2dRgba;
TTexture2dRgba = object(TTexture2d)
public
  constructor InitOwnership(W, H: LongInt; Data: Pointer);
  procedure Reinit(W, H: LongInt);
  procedure SetPixel(X, Y: Integer; Color: TUint32); inline;
  function GetPixel(X, Y: Integer): TUint32; inline;
  property Pixels[X, Y: Integer]: TUint32 read GetPixel write SetPixel; default;
end;

PTexture2dFormat = ^TTexture2dFormat;
TTexture2dFormat = object
  constructor Init;
  destructor Done; virtual;
  function CheckFormat(Stream: PStream): Boolean; virtual; abstract;
  function Load(Stream: PStream; var Texture: TTexture2dRgba): Boolean; virtual; abstract;
  function Name: AnsiString; virtual;
end;

TCollisionBitmap = object
public
  //procedure Init(const Texture: TTexture2d);
  //procedure Done;
  //function Collide(const P: TVec2i): Boolean;
  //function Collide(const Delta: TVec2i; const Bitmap: TCollisionBitmap): Boolean;
end;

procedure RegistTexture2dFormat(Format: PTexture2dFormat);
function LoadTexture2d(Data: TData;
                       var T: TTexture2dRgba;
                       Error: PError = nil): Boolean; overload;

function DetectPartPositionInReference(Tex: PTexture2dRgba; const TexPos, TexSize: TVec2i;
                                       Ref: PTexture2dRgba; out BestPos: TVec2i;
                                       MinimalPercent: LongInt = 1): Boolean;

implementation

var
  Formats: array of PTexture2dFormat;

procedure TTexture2d.Init;
begin
  FData := nil;
end;

procedure TTexture2d.InitRGBA(W, H: LongInt);
begin
  FEncoding := TEXTURE_2D_ENCODING_RGBA;
  FWidth := W;
  FHeight := H;
  FData := GetMem(Align(W * H * 32, 8) div 8)
end;

procedure TTexture2d.InitRGBA(ScreenBuffer: PScreenBuffer);
begin
  InitRGBA(ScreenBuffer.Width, ScreenBuffer.Height);
  Move(ScreenBuffer.Data^, FData^, FWidth * FHeight * 4);
end;

procedure TTexture2d.Done;
begin
  FreeData;
end;

procedure TTexture2d.FreeData;
begin
  if FData <> nil then
    FreeMem(FData);
  FData := nil;
end;

function TTexture2d.IsRGBA: Boolean;
begin
  Result := FEncoding = TEXTURE_2D_ENCODING_RGBA;
end;

constructor TTexture2dRgba.InitOwnership(W, H: LongInt; Data: Pointer);
begin
  FWidth := W;
  FHeight := H;
  FData := Data;
end;

procedure TTexture2dRgba.Reinit(W, H: LongInt);
begin
  FreeData;
  InitRGBA(W, H);
end;

procedure TTexture2dRgba.SetPixel(X, Y: Integer; Color: TUint32);
begin
  PArrayOfUint32(FData)^[Y * FWidth + X] := Color;
end;

function TTexture2dRgba.GetPixel(X, Y: Integer): TUint32;
begin
  Result := PArrayOfUint32(FData)^[Y * FWidth + X];
end;

constructor TTexture2dFormat.Init;
begin
end;

destructor TTexture2dFormat.Done;
begin
end;

function TTexture2dFormat.Name: AnsiString;
begin
  Exit('');
end;

procedure RegistTexture2dFormat(Format: PTexture2dFormat);
begin
  SetLength(Formats, Length(Formats) + 1);
  Formats[High(Formats)] := Format;
end;

procedure FreeFormats;
var
  I: Integer;
begin
  for I := 0 to High(Formats) do
    Formats[I].Done;
end;

function LoadTexture2d(Data: TData;
                       var T: TTexture2dRgba;
                       Error: PError = nil): Boolean; overload;
var
  I: Integer;
  Rewinder: PStreamRewinder;
  Temp: TTexture2d;
begin
  Result := False;
  Rewinder := TStreamRewinder.Create(Data.Stream, False);
  SetupOK(Error);
  for I := 0 to High(Formats) do begin
    if Formats[I].CheckFormat(Rewinder) then begin
      Rewinder.Rewind;
      Rewinder.StopBuffering;
      Result := Formats[I].Load(Rewinder, T);
      if (Error <> nil) and (not Result) then begin
        Error^.Id := 2;
        Error^.Msg := 'could not read';
      end;
      Break;
    end else
      Rewinder.Rewind;
  end;
  if (Error <> nil) and (Error^.Id = OK) then begin
    Error^.Id := 1;
    Error^.Msg := 'could not find decoder';
  end;
  Rewinder.Free;
  Data.Done;
end;

function DetectPartPositionInReference(Tex: PTexture2dRgba; const TexPos, TexSize: TVec2i;
                                       Ref: PTexture2dRgba; out BestPos: TVec2i;
                                       MinimalPercent: LongInt = 1): Boolean;
var
  BestScore, CurrentScore: LongInt;
  I, J, K, L: LongInt;
begin
  BestScore := 0;
  BestPos := Vec2i(0, 0);
  for I := 0 to Ref.Width - TexSize.X - 1 do begin
    for J := 0 to Ref.Height - TexSize.Y - 1 do begin
      CurrentScore := 0;
      for K := 0 to TexSize.X - 1 do begin
        for L := 0 to TexSize.Y - 1 do begin
          if GetAlpha(Tex^[TexPos.X + K, TexPos.Y + L]) = 0 then begin
            Inc(CurrentScore);
            continue;
          end;
          Inc(CurrentScore, Ord(Ref^[I + K, J + L] = Tex^[TexPos.X + K, TexPos.Y + L]));
        end;
      end;
      if CurrentScore > BestScore then begin
        BestScore := CurrentScore;
        BestPos := Vec2i(I, J);
      end;
    end;
  end;
  Result := (100 * BestScore / (TexSize.X * TexSize.Y)) >= MinimalPercent;
end;

finalization
  FreeFormats;
end.
