{
     Apophysis Copyright (C) 2001-2004 Mark Townsend
     Apophysis Copyright (C) 2005-2006 Ronald Hordijk, Piotr Borys, Peter Sdobnov
     Apophysis Copyright (C) 2007-2008 Piotr Borys, Peter Sdobnov
     
     Apophysis "3D hack" Copyright (C) 2007-2008 Peter Sdobnov
     Apophysis "7X" Copyright (C) 2009-2010 Georg Kiehne
     Apophysis AV "Phoenix Edition" Copyright (C) 2021-2022 Alice V. Koryagina

     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 Global;

interface

uses
  Windows, SysUtils, Classes, SyncObjs, Controls, Graphics, Math,
  cmap, Xform, CommDlg;

type
  TFileType = (ftApo, ftXML); // AV: moved here from ControlPoint

{ Misc }
function OpenSaveFileDialog(Parent: TWinControl;
                            const DefExt,
                            Filter,
                            InitialDir,
                            Title: string;
                            var FileName: string;
                            MustExist,
                            OverwritePrompt,
                            NoChangeDir,
                            DoOpen: Boolean): Boolean;
procedure LoadThumbnailPlaceholder(ThumbnailSize : integer);
function GetEnvVarValue(const VarName: string): string;
function Round6(x: double): double;  inline;
function MiddleColor(const clOne, clTwo: TColor): TColor; // AV
function CheckX64: Boolean;  // AV

const
  APP_NAME: string = 'Apophysis AV';
  APP_VERSION: string = 'Phoenix Edition';
  {$ifdef CPUX64}
  APP_BUILD: string = ' - 64 bit';
  {$else}
  APP_BUILD: string = ' - 32 bit';
  {$endif}

  // MAX_TRANSFORMS: integer = 100;  // AV: why if we have NXFORMS?
  // eps: double = 1E-10; // AV: this one is used nowhere
  prefilter_white: integer = 1024;
  White_level = 200;
  //FT_BMP = 1; FT_PNG = 2; FT_JPG = 3;

const
  crEditArrow  = 20;
  crEditMove   = 21;
  crEditRotate = 22;
  crEditScale  = 23;

var
  MainSeed: integer;
  Transforms: smallint; // Count of Tranforms
  EnableFinalXform: boolean;
  AppPath, AppData: string; // Path of application file
  OpenFile: string; // Name of currently open file
  CanDrawOnResize: boolean; 
  AlwaysCreateBlankFlame : boolean;

  ThumbnailPlaceholder : TBitmap;
  WarnOnMissingPlugin : boolean;
  RandomizeTemplates: boolean;
  LanguageFile : string;
  AvailableLanguages : TStringList;
  PluginPath : string;
  (*
  PreserveWeights: boolean;
  StartupCheckForUpdates : boolean;
  EmbedThumbnails : boolean;
  *)

  { AV: GUI Theme Stuff }
  CurrentStyle: string;
  // theme-aware system colors
  WinColor, BrightColor, MidColor, TextColor: TColor;
  IsDarkTheme, IsLightMenu: boolean;

  { UPR Options }

  UPRSampleDensity: integer;
  UPRFilterRadius: double;
  UPROversample: byte;
  UPRAdjustDensity: boolean;
  UPRColoringIdent: string;
  UPRColoringFile: string;
  UPRFormulaIdent: string;
  UPRFormulaFile: string;
  UPRWidth: Integer;
  UPRHeight: Integer;
  UPRPath: string; // Name and folder of last UPR file

  { Editor }

  UseFlameBackground, UseTransformColors: boolean;
  HelpersEnabled: boolean;
  EditorBkgColor, ReferenceTriangleColor: TColor;
  GridColor1, GridColor2, HelpersColor, FlipColor: TColor;
  ExtEditEnabled, TransformAxisLock, RebuildXaosLinks: boolean;
  ShowAllXforms: boolean;
  EditorPreviewTransparency: integer;
  EnableEditorPreview: boolean;
  AllowResetCoefs, AllowResetLinear, UseTriangleSync: boolean; // AV

  { Display }

  defSampleDensity: Double;
  defGamma, defBrightness, defVibrancy, defContrast, // AV
  defFilterRadius, defGammaThreshold: Double;
  defOversample: byte; // integer;
  FUSE: byte; // AV: moved from ControlPoint and changed to variable
  RhombTR, SquareTR, HexTR: single; // AV: tile radii

  cmap_index: integer; // Index to current gradient

  { Render }

  renderDensity, renderFilterRadius: double;
  renderOversample, renderWidth, renderHeight: integer;
  renderPath: string;
  renderFileFormat: byte; // integer;

  EmbedFlame, SaveInFlame: boolean;  // AV

  NrTreads: smallint;
  UseNrThreads: smallint;  // AV: currently holds Nr CPU cores

  JPEGQuality: smallint;
  PNGTransparency: byte; // integer;
  ShowTransparency: boolean;

  MainPreviewScale: double;
  ExtendMainPreview: boolean;

(*
  renderBitsPerSample: integer;
  InternalBitsPerSample: integer;

  StoreEXIF : boolean;
  StoreParamsEXIF : boolean;
  ExifAuthor : string;
*)

  { Defaults }

  LastOpenFile: string;
  LastOpenFileEntry: integer;
  RememberLastOpenFile: boolean;
  UseSmallThumbnails: boolean;
  ClassicListMode: boolean;
  ConfirmDelete: boolean; // Flag confirmation of entry deletion
  OldPaletteFormat: boolean;
  ConfirmExit, ConfirmStopRender: boolean;
  ConfirmClearScript, ConfirmResetUndo: boolean; // AV
  SavePath, SmoothPalettePath: string;
  RandomPrefix, RandomDate: string;
  RandomIndex: integer;
  FlameFile, GradientFile, GradientEntry, FlameEntry: string;
  ParamFolder: string;
  prevLowQuality, prevMediumQuality, prevHighQuality: double;

  { Settings for smooth palette }
  SmoothPaletteFile, defSmoothPaletteFile: string;
  NumTries, TryLength: integer;
  ImageFolder: string;

  BrowserPath: string; // Stored path of browser open dialog
  EditPrevQual, MutatePrevQual, AdjustPrevQual: byte; // AV: menu item index
  ThumbPrevQual, AnimPrevQual: byte;  // AV
  defFlameFile: string;
  defScriptFile: string; // AV
  SetEngLayout: boolean; // AV
  ScreenShotPath: string;  // AV
  AutoSaveXML, ApplyFlatten: boolean; // AV

  PlaySoundOnRenderComplete: boolean;
  RenderCompleteSoundFile: string;

  SaveIncompleteRenders: boolean;
  ShowRenderStats, ShowRenderImage: boolean;
  LowerRenderPriority: boolean;

 { AV: Animation parameters }
  AnimFPS: word;
  defAnimPrefix: string;
  defFrameExt: shortint;
  CreateAnimFolder: boolean;

  SymmetryType: shortint;
  SymmetryOrder: smallint;
  SymmetryNVars: word;
  Variations: array of boolean;
  FavouriteVariations: array of boolean;

  MainForm_RotationMode: byte; // integer;
  PreserveQuality: boolean;
  FlameEnumMode: byte;  // AV

  { For random gradients }
  MinNodes, MaxNodes, MinHue, MaxHue, MinSat, MaxSat, MinLum, MaxLum: integer;
  randGradient: shortint;
  randGradientFile: string;
  randColorBlend: byte; // AV
  EqualStripes: boolean;
  
  BatchSize: Integer;
  randMinTransforms, randMaxTransforms: smallint;
  mutantMinTransforms, mutantMaxTransforms: smallint;
  KeepBackground: boolean;
  RandBackColor: TColor;  // AV
  
  { Scripting }
  Favorites: TStringList;
  Script: string;
  ScriptPath: string;
  
  flam3Path, helpPath: string;
  ExportWidth, ExportHeight: Integer;
  ExportOversample, ExportFileFormat: byte; // AV
  ExportFilter, ExportDensity, ExportGammaTreshold: Double;
  ExportEstimator, ExportEstimatorMin, ExportEstimatorCurve: double;

  (*  // AV: user cannot change them, anyway
   ReferenceMode: integer;
   Compatibility: integer; //0 = original, 1 = Drave's
   SheepServer, SheepNick, SheepURL, SheepPW: string;
   ExportBatches, ExportJitters: byte;
   ResizeOnLoad: Boolean;
  *)

  OpenFileType: TFileType;
  ShowProgress: Boolean;
  defLibrary: string;
  LimitVibrancy: Boolean;
  DefaultPalette: TColorMap;

  ChaoticaPath: string;
  UseX64IfPossible: boolean;

  AutoOpenLog: Boolean;
  AutoSaveEnabled: Boolean;
  AutoSaveFreq: byte;
  AutoSavePath: string;

  LineCenterColor, LineThirdsColor, LineGRColor : TColor;
  EnableGuides : boolean;

implementation

function GetEnvVarValue(const VarName: string): string;
var
  BufSize: Integer;  // buffer size required for value
begin
  // Get required buffer size (inc. terminal #0)
  BufSize := GetEnvironmentVariable(PChar(VarName), nil, 0);
  if BufSize > 0 then
  begin
    // Read env var value into result string
    SetLength(Result, BufSize - 1);
    GetEnvironmentVariable(PChar(VarName), PChar(Result), BufSize);
  end
  else
    // No such environment variable
    Result := '';
end;

procedure LoadThumbnailPlaceholder(ThumbnailSize : integer);
var
  placeholderIcon: TBitmap;
const
  pi_width = 48;
  pi_height = 48;
begin
  placeholderIcon := TBitmap.Create;
  placeholderIcon.Handle := LoadBitmap(hInstance, 'THUMB_PLACEHOLDER');
  // AV:  replaced a local variable by the global one
  ThumbnailPlaceholder.PixelFormat := pf32bit;
  ThumbnailPlaceholder.HandleType := bmDIB;
  ThumbnailPlaceholder.Width := ThumbnailSize;
  ThumbnailPlaceholder.Height := ThumbnailSize;

  with ThumbnailPlaceholder.Canvas do begin
    Brush.Color := $000000;
    FillRect(Rect(0, 0, ThumbnailPlaceholder.Width, ThumbnailPlaceholder.Height));
    Draw((ThumbnailSize - pi_width) shr 1, (ThumbnailSize - pi_height) shr 1,
        placeholderIcon);
  end;

  placeholderIcon.Free;
end;

function MiddleColor(const clOne, clTwo: TColor): TColor;   // AV
begin
  Result := (((clOne and $ff) + (clTwo and $ff)) shr 1 ) +
    ((((clOne shr 8) and $ff) + ((clTwo shr 8) and $ff))  shr 1 ) shl 8 +
    ((((clOne shr 16) and $ff) + ((clTwo shr 16) and $ff)) shr 1 ) shl 16;
end;

function Round6(x: double): double;
// Really ugly, but it works
begin
  // --Z-- this is ridiculous:
  //   Result := StrToFloat(Format('%.6f', [x]));
  // and yes, this is REALLY ugly :-\
  Result := RoundTo(x, -6);
end;

function CheckX64: Boolean;  // AV
var
    IsWow64Process:
      function(hProcess: THandle; out Wow64Process: boolean): boolean; stdcall;
    Wow64Process: boolean;
begin
{$ifdef CPUX64}
  Result := True;
{$else}
  IsWow64Process := GetProcAddress(GetModuleHandle('kernel32.dll'), 'IsWow64Process');

  Wow64Process := False;
  if Assigned(IsWow64Process) then
    Wow64Process := IsWow64Process(GetCurrentProcess, Wow64Process) and Wow64Process;

  Result := Wow64Process;
{$endif}
end;

function ReplaceStr(Str, SearchStr, ReplaceStr: string): string;
begin
  while Pos(SearchStr, Str) <> 0 do
  begin
    Insert(ReplaceStr, Str, Pos(SearchStr, Str));
    system.Delete(Str, Pos(SearchStr, Str), Length(SearchStr));
  end;
  Result := Str;
end;

function SplitFilter(const fText: String; const fSep: Char; fTrim: Boolean=false; fQuotes: Boolean=false):  TStringList;
var vI: Integer;
    vBuffer: String;
    vOn: Boolean;
begin
  Result := TStringList.Create;
  vBuffer := '';
  vOn := true;
  for vI := 1 to Length(fText) do
  begin
    if (fQuotes and(fText[vI] = fSep)and vOn)or(Not(fQuotes) and (fText[vI] = fSep)) then
    begin
      if fTrim then vBuffer := Trim(vBuffer);
      if vBuffer = '' then vBuffer := fSep; // !!! e.g. split(',**',',')...
      if vBuffer[1] = fSep then
        vBuffer := Copy(vBuffer, 2, Length(vBuffer));
      Result.Add(vBuffer);
      vBuffer := '';
    end;
    if fQuotes then
    begin
      if fText[vI] = '"' then
      begin
        vOn := Not(vOn);
        Continue;
      end;
      if (fText[vI] <> fSep)or((fText[vI] = fSep)and(vOn = false)) then
        vBuffer := vBuffer + fText[vI];
    end else
      if fText[vI] <> fSep then
        vBuffer := vBuffer + fText[vI];
  end;
  if vBuffer <> '' then
  begin
    if fTrim then vBuffer := Trim(vBuffer);
    Result.Add(vBuffer);
  end;
end;

function OpenSaveFileDialog(Parent: TWinControl;
                            const DefExt,
                            Filter,
                            InitialDir,
                            Title: string;
                            var FileName: string;
                            MustExist,
                            OverwritePrompt,
                            NoChangeDir,
                            DoOpen: Boolean): Boolean;
// uses commdlg
var
  ofn: TOpenFileName;
  szFile: array[0..260] of Char;
  fa, fa2: TStringList;
  h,i,j,k,c : integer;
  cs, s : string;
begin
  Result := False;
  FillChar(ofn, SizeOf(TOpenFileName), 0);
  with ofn do
  begin
    lStructSize := SizeOf(TOpenFileName);
    hwndOwner := Parent.Handle;
    lpstrFile := szFile;
    nMaxFile := SizeOf(szFile);
    if (Title <> '') then
      lpstrTitle := PChar(Title);
    if (InitialDir <> '') then
      lpstrInitialDir := PChar(InitialDir);
    StrPCopy(lpstrFile, FileName);
    lpstrFilter := PChar(ReplaceStr(Filter, '|', #0)+#0#0);
    fa := splitFilter(Filter, '|');

    k := 0;
    c := (fa.Count div 2);
    for i := 0 to c - 1 do begin
      j := 2 * i + 1;
      cs := LowerCase(fa.Strings[j]);
      fa2 := splitFilter(cs, ';');
      for h := 0 to fa2.Count - 1 do begin
        cs := fa2.Strings[h];
        s := '*.' + LowerCase(DefExt);
        if (cs = s) then k := i;
      end;
      fa2.Free;  //AV: fixed multiple memory leaks!
    end;
    fa.Free; // AV: fixed memory leak!

    nFilterIndex := k + 1;
    if DefExt <> '' then
      lpstrDefExt := PChar(DefExt);
  end;

  if MustExist then ofn.Flags := ofn.Flags or OFN_FILEMUSTEXIST;
  if OverwritePrompt then ofn.Flags := ofn.Flags or OFN_OVERWRITEPROMPT;
  if NoChangeDir then ofn.Flags := ofn.Flags or OFN_NOCHANGEDIR;

  if DoOpen then
  begin
    if GetOpenFileName(ofn) then
    begin
      Result := True;
      FileName := StrPas(szFile);
    end;
  end
  else
  begin
    if GetSaveFileName(ofn) then
    begin
      Result := True;
      FileName := StrPas(szFile);
    end;
  end
end; // function OpenSaveFileDialog

end.

