unit fwImage;

{$IFDEF FPC}
  {$MODE DELPHI}
{$ENDIF}

interface

uses SysUtils,
     Classes,
     ContNrs,
     Math,

     fwLogging,
     fwResource,
     
	   fwCustomEngine;

type
  // Objekt, welches nur einen Bildausschnitt beschreibt und daher immer
  // zu einem anderen Objekt gehoeren muss.
  TfwImageSection  = class(TfwCEDrawable)
  protected
    fOrigin          : TCERect;
    fImage           : TfwCESurface;
    function  GetWidth() : Integer; override;
    function  GetHeight() : Integer; override;
  public
    constructor Create(aOwner : TfwCESurface);

    procedure SetOrigin(const aRect : TCERect); overload; virtual;
    procedure SetOrigin(aX, aY, aWidth, aHeight : Integer); overload; virtual;

    procedure Draw(aScreen : TCESurface; const dst : TCERect); overload; override;
    procedure Draw(aScreen : TCESurface; aX, aY, aWidth, aHeight : Integer); overload; override;
    procedure Draw(aScreen : TCESurface; aX, aY : Integer); overload; override;
    property Image : TfwCESurface read fImage;
  end;

  // Liste von zusammengehoerigen Bildauschnitten
  TfwImageSections = class(TfwCEDrawable)
  protected
    fAutoLoop        : Boolean;
    fPosition        : Integer;
    fImage           : TfwCESurface;
    fItems           : TObjectlist;
    function  GetItems(aIndex : Integer) : TfwCEDrawable;
    function  GetCount : Integer;
    function  GetActive : TfwCEDrawable;
    function  GetBOL : Boolean;
    function  GetEmpty : Boolean;
    function  GetEOL : Boolean;
    procedure SetPosition(aPosition : Integer); virtual;
  public
    constructor Create(aImage : TfwCESurface);
    destructor Destroy; override;

    procedure Add(aSection : TfwImageSection); overload; virtual;
    function  Add(const aRect : TCERect) : TfwImageSection; overload; virtual;
    function  Add(aX, aY, aWidth, aHeight : Integer) : TfwImageSection; overload; virtual;
    procedure Add(aX, aY, aWidth, aHeight, aStepX, aStepY, aCount : Integer); overload; virtual;

    procedure Draw(aScreen : TCESurface; const dst : TCERect); overload; override;
    procedure Draw(aScreen : TCESurface; aX, aY, aWidth, aHeight : Integer); overload; override;
    procedure Draw(aScreen : TCESurface; aX, aY : Integer); overload; override;

    procedure First; virtual;
    procedure Prev; virtual;
    procedure Next; virtual;
    procedure Last; virtual;

    property Active : TfwCEDrawable read GetActive;
    property AutoLoop : Boolean read fAutoLoop write fAutoLoop;
    property BOL : Boolean read GetBOL;
    property Count : Integer read GetCount;
    property Empty : Boolean read GetEmpty;
    property EOL : Boolean read GetEOL;
    property Position : Integer read fPosition write SetPosition;
    property Items[aIndex : Integer] : TfwCEDrawable read GetItems; default;
  end;

  TfwImages     = class(TfwResourceManager)
  protected
    fColorKeyEnabled : Boolean;
    fCKRed           : Byte;
    fCKGreen         : Byte;
    fCKBlue          : Byte;
    function GetItem(aIndex : Integer) : TfwCESurface;
  public
    constructor Create(aOwnsObjects : Boolean = False); overload;

    procedure SetColorKey(aEnabled : Boolean; aRed : Byte = 0; aGreen : Byte = 0; aBlue : Byte = 0);

    function  CreateImage(aArea : Integer = 0) : TfwCESurface;

    function  LoadFromFile(aImage : TfwCESurface; aName, aFilename : String) : TfwCESurface; overload;
    function  LoadFromFile(const aName, aFilename : String) : TfwCESurface; overload;

    property Items[aIndex : Integer] : TfwCESurface read GetItem; default;
    property ColorKeyEnabled : Boolean read fColorKeyEnabled;
  end;

implementation

// TfwImageSection -------------------------------------------------------
function TfwImageSection.GetWidth() : Integer;
begin
  Result := fOrigin.w;
end;

function  TfwImageSection.GetHeight() : Integer;
begin
  Result := fOrigin.h;
end;

constructor TfwImageSection.Create(aOwner : TfwImage);
begin
  inherited Create(0);

  fImage    := aOwner;
  fOrigin.x := 0;
  fOrigin.y := 0;
  fOrigin.w := 0;
  fOrigin.h := 0;
end;

procedure TfwImageSection.SetOrigin(const aRect : TCERect);
begin
  fOrigin := aRect;
end;

procedure TfwImageSection.SetOrigin(aX, aY, aWidth, aHeight : Integer);
begin
  fOrigin.x := aX;
  fOrigin.y := aY;
  fOrigin.w := aWidth;
  fOrigin.h := aHeight;
end;

procedure TfwImageSection.Draw(aScreen : TCESurface; const dst : TCERect);
begin
  if Assigned(fImage) then begin
    fImage.fSurface.Blit(aScreen, fOrigin, Dst);
  end;
end;

procedure TfwImageSection.Draw(aScreen : TCESurface; aX, aY, aWidth, aHeight : Integer);
var
  Dst              : TCERect;
begin
  if Assigned(fImage) then begin
    Dst.x := aX;
    Dst.y := aY;
    Dst.w := aWidth;
    Dst.h := aHeight;

    Draw(aScreen, Dst);
  end;
end;

procedure TfwImageSection.Draw(aScreen : TCESurface; aX, aY : Integer);
begin
  if Assigned(fImage) then begin
    Draw(aScreen, aX, aY, fOrigin.w, fOrigin.h);
  end;
end;


// TfwImageSections ------------------------------------------------------
function TfwImageSections.GetItems(aIndex : Integer) : TfwBlittable;
begin
  Result := (fItems[aIndex] as TfwBlittable);
end;

function TfwImageSections.GetCount : Integer;
begin
  Result := fItems.Count;
end;

function TfwImageSections.GetActive : TfwBlittable;
begin
  Result := nil;
  if (fPosition >= 0) and (fPosition < fItems.Count) then begin
    Result := Items[fPosition];
  end;
end;

function TfwImageSections.GetBOL : Boolean;
begin
  Result := Empty;
  if not Result then begin
    Result := (fPosition < 0) or (fPosition >= fItems.Count);
  end;
end;

function TfwImageSections.GetEOL : Boolean;
begin
  Result := Empty;
  if not Result then begin
    Result := (fPosition < 0) or (fPosition >= fItems.Count) or Empty;
  end;
end;

function TfwImageSections.GetEmpty : Boolean;
begin
  Result := (fItems.Count = 0) or (not Assigned(fImage));
end;

procedure TfwImageSections.SetPosition(aPosition : Integer);
begin
  if (aPosition >= 0) and (aPosition < fItems.Count) then begin
    fPosition := aPosition;
  end;
end;

constructor TfwImageSections.Create(aImage : TfwImage);
begin
  inherited Create(0);

  fAutoLoop := True;
  fPosition := 0;
  fItems    := TObjectlist.Create(True);
  fImage    := aImage;
end;

destructor TfwImageSections.Destroy;
begin
  FreeAndNil(fItems);
  fPosition := 0;

  inherited Destroy;
end;

procedure TfwImageSections.Add(aSection : TfwImageSection);
begin
  fItems.Add(aSection);
end;

function TfwImageSections.Add(const aRect : TCERect) : TfwImageSection;
begin
  Result := TfwImageSection.Create(fImage);
  if Assigned(Result) then begin
    Result.SetOrigin(aRect);
    fItems.Add(Result);
  end;
end;

function TfwImageSections.Add(aX, aY, aWidth, aHeight : Integer) : TfwImageSection;
begin
  Result := TfwImageSection.Create(fImage);
  if Assigned(Result) then begin
    Result.SetOrigin(aX, aY, aWidth, aHeight);
    fItems.Add(Result);
  end;
end;

procedure TfwImageSections.Add(aX, aY, aWidth, aHeight, aStepX, aStepY, aCount : Integer);
var
  iLoop            : Integer;
begin
  for iLoop := 0 to Pred(aCount) do begin
    Add(aX + (aStepX * iLoop), aY + (aStepY * iLoop), aWidth, aHeight);
  end;
end;

procedure TfwImageSections.Draw(aScreen : TCESurface; const dst : TCERect);
var
  Blit             : TfwBlittable;   
begin
  Blit := Active;
  if Assigned(Blit) then begin
    Blit.Draw(aScreen, Dst);
  end;
end;

procedure TfwImageSections.Draw(aScreen : TCESurface; aX, aY, aWidth, aHeight : Integer);
var
  Blit             : TfwBlittable;   
begin
  Blit := Active;
  if Assigned(Blit) then begin
    Blit.Draw(aScreen, aX, aY, aWidth, aHeight);
  end;
end;

procedure TfwImageSections.Draw(aScreen : TCESurface; aX, aY : Integer);
var
  Blit             : TfwBlittable;
begin
  Blit := Active;
  if Assigned(Blit) then begin
    Blit.Draw(aScreen, aX, aY);
  end;
end;

procedure TfwImageSections.First;
begin
  fPosition := -1;
  if not Empty then begin
    if (fItems.Count > 0) then begin
      fPosition := 0;
    end;
  end;
end;

procedure TfwImageSections.Prev;
begin
  if not Empty then begin
    if (fPosition >= 0) then begin
      Dec(fPosition);

      if fAutoLoop then begin
        if (fPosition < 0) then begin
          fPosition := Pred(fItems.Count);
        end;
      end;
    end;
  end;
end;

procedure TfwImageSections.Next;
begin
  if not Empty then begin
    if (fPosition < fItems.Count) then begin
      Inc(fPosition);

      if fAutoLoop then begin
        if (fPosition >= fItems.Count) then begin
          fPosition := 0;
        end;
      end;
    end;
  end;
end;

procedure TfwImageSections.Last;
begin
  fPosition := -1;
  if not Empty then begin
    fPosition := Max(fItems.Count - 1, 0);
  end;
end;

// TfwImages ----------------------------------------------------------
function TfwImages.GetItem(aIndex : Integer) : TfwCESurface;
begin
  Result := (inherited Items[aIndex] as TfwCESurface);
end;

constructor TfwImages.Create(aOwnsObjects : Boolean = False);
begin
  inherited Create(aOwnsObjects);

  fColorKeyEnabled := False;
end;

procedure TfwImages.SetColorKey(aEnabled : Boolean; aRed : Byte = 0; aGreen : Byte = 0; aBlue : Byte = 0); 
begin
  fColorKeyEnabled := aEnabled;
  if fColorKeyEnabled then begin
    fCKRed    := aRed;
    fCKGreen  := aGreen;
    fCKBlue   := aBlue;
  end;
end;

function TfwImages.CreateImage(aArea : Integer = 0) : TfwCESurface;
begin
  Result := TfwCESurface.Create(aArea);
  if Assigned(Result) then begin
    if fColorKeyEnabled then begin
      Result.SetColorKey(fCKRed, fCKGreen, fCKBlue);
    end;
    Add(Result);
  end;
end;

function TfwImages.LoadFromFile(aImage : TfwCESurface; aName, aFilename : String) : TfwCESurface;
begin
  Result := aImage;
  if Assigned(Result) then begin
    try
      if not Result.LoadFromFile(aName, aFilename) then begin
        FreeAndNil(Result);

        Exit;
      end;

      if fColorKeyEnabled then begin
        (Result as TfwCESurface).SetColorKey(fCKRed, fCKGreen, fCKBlue);
      end;
    except
      FreeAndNil(Result);
      raise;
    end;
  end;
end;

function TfwImages.LoadFromFile(const aName, aFilename : String) : TfwCESurface;
begin
  Result := TfwCESurface.Create();
  if Assigned(Result) then begin
    if Result.LoadFromFile(aName, aFilename) then begin
      Add(Result);
    end;
  end;
end;

end.
