unit ubgralape;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, BGRABitmap, BGRABitmapTypes, lptypes, lpcompiler;

function RegisterBitmap(ABitmap: TBGRABitmap): integer;
procedure UnregisterBitmap(AIndex: integer);
procedure EnsureInvalidate(AIndex: integer);
procedure SetTargetBitmap(AIndex: integer);
procedure AddScriptSystemTypes(Compiler: TLapeCompiler);
procedure AddScriptSystemFunctions(Compiler: TLapeCompiler);
procedure FreeBitmaps;

implementation

uses FileUtil, Graphics, GraphType, BGRAPolygon, BGRAFillInfo;

var
  bitmaps: array of record
    Bitmap: TBGRABitmap;
    Registered: boolean;
    Invalidated: boolean;
    LockedCount: Int32or64;
  end;
  target: TBGRABitmap;
  targetIndex: integer;

function NewBitmapEntry: integer;
var i: integer;
begin
  for i:= 0 to high(bitmaps) do
    if bitmaps[i].Bitmap = nil then
      begin
        result := i;
        bitmaps[i].LockedCount:= 0;
        exit;
      end;
  result := length(bitmaps);
  setlength(bitmaps,length(bitmaps)*2+1);
  bitmaps[result].LockedCount:= 0;
end;

procedure FreeBitmap(AIndex: integer);
begin
  if (AIndex >= 0) and (AIndex < length(bitmaps)) then
    if not bitmaps[AIndex].Registered then
      begin
        if bitmaps[AIndex].LockedCount > 0 then
          raise exception.Create('Bitmap is locked');
        FreeAndNil(bitmaps[AIndex].Bitmap);
        bitmaps[AIndex].Invalidated:= false;
      end;
end;

function RegisterBitmap(ABitmap: TBGRABitmap): integer;
begin
  result := NewBitmapEntry;
  bitmaps[result].Bitmap := ABitmap;
  bitmaps[result].Invalidated := false;
  bitmaps[result].Registered := true;
end;

procedure UnregisterBitmap(AIndex: integer);
begin
  if (AIndex >= 0) and (AIndex < length(bitmaps)) then
    begin
      EnsureInvalidate(AIndex);
      if not bitmaps[AIndex].Registered then
        raise Exception.Create('This bitmap has not been registered');
      if target = bitmaps[AIndex].Bitmap then
        begin
          target := nil;
          targetIndex := -1;
        end;
      bitmaps[AIndex].Bitmap := nil;
      bitmaps[AIndex].Registered := false;
    end;
end;

procedure EnsureInvalidate(AIndex: integer);
begin
  if (AIndex >= 0) and (AIndex < length(bitmaps)) then
    begin
      if bitmaps[AIndex].Invalidated then
        begin
          bitmaps[AIndex].Bitmap.InvalidateBitmap;
          bitmaps[AIndex].Invalidated := false;
        end;
    end;
end;

procedure WillInvalidateBitmap(AIndex: integer);
begin
  if (AIndex >= 0) and (AIndex < length(bitmaps)) then
    Bitmaps[AIndex].Invalidated := true;
end;

procedure SetTargetBitmap(AIndex: integer);
begin
  if (AIndex < 0) or (AIndex >= length(bitmaps)) or (bitmaps[AIndex].Bitmap = nil) then
    raise exception.create('Bitmap does not exist');
  target := bitmaps[AIndex].Bitmap;
  targetIndex := AIndex;
end;

function GetBitmap(AIndex: integer): TBGRABitmap;
begin
  if (AIndex < 0) or (AIndex >= length(bitmaps)) or (Bitmaps[AIndex].Bitmap = nil) then
    raise exception.Create('Bitmap does not exist');
  result := Bitmaps[AIndex].Bitmap;
end;

function GetScriptSystemInlineFunctions: string; forward;

///////////////////////////// Function implementation ///////////////////////////

{$I basic_functions.inc}
{$I basic_geometry_functions.inc}
{$I extended_geometry_functions.inc}
{$I text_functions.inc}
{$I color_functions.inc}

/////////////////////////// Function list /////////////////////////////////////////////

procedure AddScriptSystemFunctions(Compiler: TLapeCompiler);
begin
  RegisterBasicFunctions(Compiler);
  RegisterBasicGeometryFunctions(Compiler);
  RegisterExtendedGeometryFunctions(Compiler);
  RegisterTextFunctions(Compiler);
  RegisterColorFunctions(Compiler);
  Compiler.addDelayedCode(GetScriptSystemInlineFunctions, '',false,true);
end;

procedure FreeBitmaps;
var i: integer;
begin
  for i := 0 to High(bitmaps) do
    if (bitmaps[i].Bitmap <> nil) and not bitmaps[i].Registered then
      begin
        bitmaps[i].LockedCount := 0;
        FreeBitmap(i);
      end;
end;

////////////////////////////// Load script system //////////////////////////////

var
  scriptSystemFunctions,scriptSystemTypes: TStringList;

function GetScriptSystemInlineFunctions: string;
var i: integer;
begin
  result := LineEnding;
  for i := 0 to scriptSystemFunctions.Count-1 do
    result += scriptSystemFunctions[i]+LineEnding;
  textAlignment:= taLeftJustify;
end;

procedure AddScriptSystemTypes(Compiler: TLapeCompiler);
var line: string;
  i,idxEq: integer;
begin
  for i := 0 to scriptSystemTypes.Count-1 do
  begin
    line := scriptSystemTypes[i];
    idxEq := pos('=',line);
    if idxEq <> 0 then
      Compiler.addGlobalType(trim(copy(line,idxEq+1,length(line)-idxEq)),trim(copy(line,1,idxEq-1)));
  end;
end;

procedure LoadScriptSystem;
var
  scriptSystem: TStringList;
  i: integer;
  dest: TStringList;
begin
  scriptSystemFunctions := TStringList.Create;
  scriptSystemTypes := TStringList.Create;
  dest := nil;

  scriptSystem := TStringList.Create;
  scriptSystem.LoadFromFile('bgralapesys.pas');

  for i := 0 to scriptSystem.Count-1 do
  begin
    if CompareText(Trim(scriptSystem[i]),'implementation') = 0 then
      dest := scriptSystemFunctions else
    if CompareText(Trim(scriptSystem[i]),'type') = 0 then
      dest := scriptSystemTypes else
    if CompareText(Trim(scriptSystem[i]),'end.') = 0 then break
    else
      if Assigned(dest) then dest.Add(scriptSystem[i]);
  end;

  scriptSystem.Free;
end;

procedure FreeScriptSystem;
begin
  scriptSystemTypes.Free;
  scriptSystemFunctions.Free;
end;

initialization

  LoadScriptSystem;
  Randomize;

finalization

  FreeScriptSystem;

end.

