unit ant_config;

//
//  Loading and saving settings (window resolution, window/fullscreen, graphics
//  options etc).
//

{$MODE FPC}
{$MODESWITCH DEFAULTPARAMETERS}
{$MODESWITCH OUT}
{$MODESWITCH RESULT}

interface

uses
  dansiview,
  dstream,
  dstreamfile,
  djson,
  deng,
  ant_args;

type
TConfig = object
private
  FConfigPath: AnsiString;
  FFullScreen: Int32; // 0 for windowed, fullscreen otherwise
  FWidth: Int32; // width and height in windowed mode
  FHeight: Int32;
  FLang: AnsiString; // overrides default user language
  FGameMusic: AnsiString;
  FMasterVolume: Int32;
  FCacheDirectory: AnsiString;
  FSwitchCost: Int32;
  FPassiveDrain: Int32;
  FMaxFood: Int32;

  function ReadJSON: Boolean;
  function SaveJSON: Boolean;
public
  procedure Init;
  procedure Done;

  function Load(const FileName: TAnsiView): Boolean;

  function Save: Boolean;
      // should be called after each sequence of write calls

  function GetFullScreen: Boolean;
  procedure SetFullScreen(F: Boolean);

  function GetWidth: Int32;
  procedure SetWidth(W: Int32);

  function GetHeight: Int32;
  procedure SetHeight(H: Int32);

  function GetLang: AnsiString;
  procedure SetLang(const L: AnsiString);

  function GetGameMusic: AnsiString;
  procedure SetGameMusic(const FileName: AnsiString);

  function GetMasterVolume: Int32;
  procedure SetMasterVolume(Volume: Int32);

  property FullScreen: Boolean read GetFullScreen write SetFullScreen;
  property Width: Int32 read FWidth write FWidth;
  property Height: Int32 read FHeight write FHeight;
  property Lang: AnsiString read FLang write FLang;
  property GameMusic: AnsiString read FGameMusic write FGameMusic;
  property MasterVolume: Int32 read FMasterVolume write FMasterVolume;
  property CacheDirectory: AnsiString read FCacheDirectory write FCacheDirectory;
  property SwitchCost: Int32 read FSwitchCost write FSwitchCost;
  property PassiveDrain: Int32 read FPassiveDrain write FPassiveDrain;
  property MaxFood: Int32 read FMaxFood write FMaxFood;
end;

var
  Config: TConfig;

implementation

function TConfig.ReadJSON: Boolean;
var
  Json: TJson;
begin
  if not ParseJson(FConfigPath, Json, True) then begin
    Log('FAILED reading config from ' + FConfigPath);
    Exit(False);
  end;

  FFullScreen := Json.Get('fullscreen', FFullScreen);
  FWidth := Json.Get('width', FWidth);
  FHeight := Json.Get('height', FHeight);
  FLang := Json.Get('lang', FLang);
  FGameMusic := Json.Get('game_music', FGameMusic);
  FMasterVolume := Json.Get('master_volume', FMasterVolume);
  FCacheDirectory := Json.Get('cache_directory', FCacheDirectory);
  FSwitchCost := Json.Get('switch_cost', FSwitchCost);
  FPassiveDrain := Json.Get('passive_drain', FPassiveDrain);
  FMaxFood := Json.Get('max_food', FMaxFood);

  Json.Done;
  Exit(True);
end;

function TConfig.SaveJSON: Boolean;
var
  F: TextFile;

  function WriteLine(const Line: AnsiString): Boolean;
  begin
  {$I-}
    System.Writeln(F, Line);
  {$I+}
    if IOResult <> 0 then begin
      Log('FAILED writing to ' + FConfigPath);
      Exit(False);
    end;
    Exit(True);
  end;

  function WriteStrValue(const Name, Value: AnsiString): Boolean;
  begin
  {$I-}
    System.Writeln(F, '    "', Name, '": "', Value, '",'); // TODO escape
  {$I+}
    if IOResult <> 0 then begin
      Log('FAILED writing to ' + FConfigPath);
      Exit(False);
    end;
    Exit(True);
  end;

  function WriteI32Value(const Name: AnsiString; Value: Int32): Boolean;
  begin
  {$I-}
    System.Writeln(F, '    "', Name, '": ', Value, ','); // TODO escape
  {$I+}
    if IOResult <> 0 then begin
      Log('FAILED writing to ' + FConfigPath);
      Exit(False);
    end;
    Exit(True);
  end;

begin
  Assign(F, FConfigPath);
{$I-}
  ReWrite(F);
{$I+}
  if IOResult <> 0 then begin
    Log('FAILED opening ' + FConfigPath);
    Exit(False);
  end;

  if not WriteLine('{')
  or not WriteI32Value('fullscreen', FFullScreen)
  or not WriteI32Value('width', FWidth)
  or not WriteI32Value('height', FHeight)
  or not WriteStrValue('lang', FLang)
  or not WriteStrValue('game_music', FGameMusic)
  or not WriteI32Value('master_volume', FMasterVolume)
  or not WriteStrValue('cache_directory', FCacheDirectory)
  or not WriteI32Value('switch_cost', FSwitchCost)
  or not WriteI32Value('passive_drain', FPassiveDrain)
  or not WriteI32Value('max_food', FMaxFood)
  or not WriteLine('    "dummy": ""')
  or not WriteLine('}')
  then
    Exit(False);

{$I-}
  Flush(F);
{$I+}
  if IOResult <> 0 then begin
    Log('FAILED flushing ' + FConfigPath);
    Exit(False);
  end;

  Close(F);
  Exit(True);
end;

procedure TConfig.Init;
begin
end;

procedure TConfig.Done;
begin
end;

function TConfig.Load(const FileName: TAnsiView): Boolean;
begin
  FConfigPath := FileName.ToString;

  // set defaults
  FFullScreen := 1;
  FWidth := 800;
  FHeight := 600;
  FLang := '';
  FGameMusic := '';
  FMasterVolume := 255;
  FCacheDirectory := 'cache';
  FSwitchCost := 1000;
  FPassiveDrain := 1;
  FMaxFood := 10000;

  if not ReadJSON then
    Exit(False);

  // override config values with command line arguments
  if CommandLineArgs.FullScreen <> -1 then
    FFullScreen := CommandLineArgs.FullScreen;
  if CommandLineArgs.Width <> -1 then
    FWidth := CommandLineArgs.Width;
  if CommandLineArgs.Height <> -1 then
    FHeight := CommandLineArgs.Height;
  if CommandLineArgs.Lang <> '' then
    FLang := CommandLineArgs.Lang;

  Exit(True);
end;

function TConfig.Save: Boolean;
begin
  Exit(SaveJSON);
end;

function TConfig.GetFullScreen: Boolean;
begin
  Exit(FFullScreen <> 0);
end;

procedure TConfig.SetFullScreen(F: Boolean);
begin
  FFullScreen := Ord(F);
end;

function TConfig.GetWidth: Int32;
begin
  Exit(FWidth);
end;

procedure TConfig.SetWidth(W: Int32);
begin
  FWidth := W;
end;

function TConfig.GetHeight: Int32;
begin
  Exit(FHeight)
end;

procedure TConfig.SetHeight(H: Int32);
begin
  FHeight := H;
end;

function TConfig.GetLang: AnsiString;
begin
  Exit(FLang);
end;

procedure TConfig.SetLang(const L: AnsiString);
begin
  FLang := L;
end;

function TConfig.GetGameMusic: AnsiString;
begin
  Exit(FGameMusic);
end;

procedure TConfig.SetGameMusic(const FileName: AnsiString);
begin
  FGameMusic := FileName;
end;

function TConfig.GetMasterVolume: Int32;
begin
  Exit(FMasterVolume);
end;

procedure TConfig.SetMasterVolume(Volume: Int32);
begin
  FMasterVolume := Volume;
end;

initialization
  Config.Init;
finalization
  Config.Done;
end.
