{

   This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
}
unit UUndoManage;

interface

uses Windows, Messages, SysUtils, Variants, Graphics,
  Classes;

type

  PUndoData = ^TUndoData;
  TUndoData = record
    bitmap:TBitmap;
    tmpfile:string;
    targetrect:TRect;
    name:string;
    resized:boolean;
  end;

  TOnInsert = procedure(Sender: TObject;PUndoData1:PUndoData) of object;
  TOnDelete = procedure(Sender: TObject;idx:integer) of object;

  TUndoManage = class
  private
    undolist:TList;
    undocount:integer;
    FOnInsert:TOnInsert;
    FOnDelete:TOnDelete;
    FPrefix:string;

    function ableundo:boolean;
  public
    undoidx:integer;
    maxundocount:integer;

    constructor Create(idx:integer;prefix:string='');
    destructor Destroy; override;

    function addundo(bitmap:TBitmap;UndoData1:TUndoData;thumbimage:boolean=true):PUndoData;
    function getundo(var bitmap:TBitmap;undopos:integer):boolean;

    function getcount:integer;
    function getitem(idx:integer):PUndoData;
    procedure undolistclear;

  published
    property OnInsert: TOnInsert read FOnInsert write FOnInsert;
    property OnDelete: TOnDelete read FOnDelete write FOnDelete;
  end;

implementation

uses Uconfig, Ufunction, Ufrmmain;

{TUndoManage}
constructor TUndoManage.Create(idx:integer;prefix:string='');
begin
  undolist:=TList.Create;
  undocount:=0;
  if prefix<>'' then begin
   undoidx:=-1;
   FPrefix:=prefix;
  end else
   undoidx:=idx;
  maxundocount:=50;
end;

destructor TUndoManage.Destroy;
begin
  undolistclear;
  undolist.Free;
  inherited;
end;

procedure TUndoManage.undolistclear;
var
 i:integer;
 PUndoData1:PUndoData;
begin
  for i:=undolist.Count-1 downto 0 do begin
    PUndoData1:=PUndoData(undolist.Items[i]);
    if assigned(FOnDelete) then FOnDelete(self,i);    
    if sysutils.FileExists(PUndoData1.tmpfile) then begin
      deletefile(PUndoData1.tmpfile);
      sleep(0);
    end;
    PUndoData1.bitmap.Free;
    dispose(PUndoData1);
  end;
  undolist.Clear;
end;

function TUndoManage.getcount:integer;
begin
  result:=undolist.Count;
end;

function TUndoManage.getitem(idx:integer):PUndoData;
begin
  result:=PUndoData(undolist.Items[idx]);
end;

procedure FastImageRescale(thumbsize:integer;Source,target:TBitmap);
var
  NewWidth,NewHeight: Integer;
  w,h:integer;
begin
  w:=Source.Width;
  h:=Source.Height;

  if w > h then begin
    NewWidth:=thumbsize;
    NewHeight:=Round((thumbsize) * h / w);
  end else begin
    NewWidth:=Round((thumbsize) * w / h);
    NewHeight:=thumbsize;
  end;
  if NewWidth > w then NewWidth:=w;
  if NewHeight > h then NewHeight:=h;
  if NewWidth<=0 then NewWidth:=1;
  if NewHeight<=0 then NewHeight:=1;

  target.PixelFormat := pf24Bit; //pf32Bit
  target.Width := NewWidth;
  target.Height := NewHeight;
  target.Palette := Source.Palette;
  SetStretchBltMode(target.Canvas.Handle, COLORONCOLOR);
  StretchBlt(target.Canvas.Handle, 0, 0, NewWidth, NewHeight, Source.Canvas.Handle, 0, 0,
               Source.Width, Source.Height, SRCCOPY);
end;

function TUndoManage.addundo(bitmap:TBitmap;UndoData1:TUndoData;thumbimage:boolean=true):PUndoData;
var
 PUndoData1:PUndoData;
 stream1,stream2:tmemorystream;
begin
  if undolist.Count > maxundocount then begin
      PUndoData1:=PUndoData(undolist.Items[0]);
      if assigned(FOnDelete) then FOnDelete(self,0);      
      if sysutils.FileExists(PUndoData1.tmpfile) then begin
        deletefile(PUndoData1.tmpfile);
        sleep(0);
      end;
      dispose(PUndoData1);
      undolist.Delete(0);
  end;

  New(PUndoData1);

  PUndoData1^:=UndoData1;

  sysutils.ForceDirectories(appdata_path+'\'+thisregname+'\temp');
  if undoidx>=0 then
    PUndoData1.tmpfile:=appdata_path+'\'+thisregname+'\temp\'+
      format('%d_%d.bmp',[undoidx,undocount])
  else
    PUndoData1.tmpfile:=appdata_path+'\'+thisregname+'\temp\'+
      format('%s_%d.bmp',[FPrefix,undocount]);

  if sysutils.FileExists(PUndoData1.tmpfile) then begin
     deletefile(PUndoData1.tmpfile);
     sleep(0);
  end;
  //compress
{
  stream1:=tmemorystream.Create;
  stream2:=tmemorystream.Create;
  bitmap.SaveToStream(stream1);
  Bzip2Compress(stream1,stream2);
  stream2.SaveToFile(PUndoData1.tmpfile);
  stream2.Free;
  stream1.Free;
}
  bitmap.SaveToFile(PUndoData1.tmpfile);

  PUndoData1.bitmap:=TBitmap.Create;
  if thumbimage then
    FastImageRescale(80,UndoData1.bitmap,PUndoData1.bitmap);

  undolist.Add(PUndoData1);
  inc(undocount);

  if assigned(FOnInsert) then FOnInsert(self, PUndoData1);
  result:=PUndoData1;
end;

function TUndoManage.getundo(var bitmap:TBitmap;undopos:integer):boolean;
var
  i:integer;
  PUndoData1:PUndoData;
  tmpbitmap:TBitmap;
  stream1,stream2:tmemorystream;
begin
  result:=ableundo;
  if result=false then exit;

  tmpbitmap:=TBitmap.Create;
  try
   for i:=undolist.Count-1 downto undopos-1 do
    if i>=0 then begin
     PUndoData1:=PUndoData(undolist.Items[i]);
     if assigned(FOnDelete) then FOnDelete(self,i);
     if sysutils.FileExists(PUndoData1.tmpfile) then begin
       //decompress
{       
       stream1:=tmemorystream.Create;
       stream2:=tmemorystream.Create;
       stream1.LoadFromFile(PUndoData1.tmpfile);
       Bzip2Decompress(stream1,stream2);
       stream2.Position:=0;
       tmpbitmap.LoadFromStream(stream2);
       stream2.Free;
       stream1.Free;
}
       tmpbitmap.LoadFromFile(PUndoData1.tmpfile);
       if PUndoData1.resized then
         bitmap.Assign(tmpbitmap)
       else
         bitmap.Canvas.CopyRect(PUndoData1.targetrect,tmpbitmap.Canvas,
            rect(0,0,tmpbitmap.Width,tmpbitmap.Height));
       deletefile(PUndoData1.tmpfile);
       sleep(0);
     end;
     PUndoData1.bitmap.Free;
     dispose(PUndoData1);
     undolist.Delete(i);
   end;
  finally
    tmpbitmap.Free;
  end;
  result:=true;
end;

function TUndoManage.ableundo:boolean;
begin
  if undolist.Count <= 0 then result:=false
  else result:=true;
end;

end.
