unit Game.Chivalry;

interface
uses
  System.Generics.Collections,

  Game.Base,
  Game.Intf;


type
  TChivalryGame = class(TCustomGame, IGameNetwork, IGameName, IGameMapList)
  private
    FServerPort: Integer;
    FPeerPort: Integer;
    FQueryPort: Integer;

    FServerName: string;
    FMessageOfTheDay: string;

    FPredefinedMapList: TObjectList<TGameMap>;
    FMapList: TObjectList<TGameMap>;
  protected
    procedure DoLoad; override;
    procedure DoSave; override;

    procedure LoadPredefinedMapList(AList: TList<TGameMap>); virtual; abstract;

    function CreateGameMap(const AMapName: string): TGameMap; virtual;
  public
    constructor Create(const ALocation: string); override;
    destructor Destroy; override;

    { IGameNetwork }
    function GetServerPort: Integer;
    function GetPeerPort: Integer;
    function GetQueryPort: Integer;

    procedure SetServerPort(const Value: Integer);
    procedure SetPeerPort(const Value: Integer);
    procedure SetQueryPort(const Value: Integer);

    property ServerPort: Integer read GetServerPort write SetServerPort;
    property PeerPort: Integer read GetPeerPort write SetPeerPort;
    property QueryPort: Integer read GetQueryPort write SetQueryPort;

    { IGameName }
    function GetServerName: string;
    function GetMessageOfTheDay: string;

    procedure SetServerName(const Value: string);
    procedure SetMessageOfTheDay(const Value: string);

    property ServerName: string read GetServerName write SetServerName;
    property MessageOfTheDay: string read GetMessageOfTheDay write SetMessageOfTheDay;

    { IGameMapList }
    function GetPredefinedMapList: TEnumerable<TGameMap>;
    function GetMapList: TEnumerable<TGameMap>;

    function GetMapCount: Integer;
    function GetMap(Index: Integer): TGameMap;

    procedure AddMap(AMap: TGameMap);
    procedure InsertMap(Index: Integer; AMap: TGameMap);
    procedure DeleteMap(AIndex: Integer);
    procedure RemoveMap(AMap: TGameMap);
    procedure MoveMap(ASourceIndex, ATargetIndex: Integer);

    property PredefinedMapList: TEnumerable<TGameMap> read GetPredefinedMapList;
    property MapList: TEnumerable<TGameMap> read GetMapList;
    property MapCount: Integer read GetMapCount;
    property Map[Index: Integer]: TGameMap read GetMap;
  end;


implementation
uses
  System.Classes,
  System.IniFiles,
  System.SysUtils,

  UDKIniFile;


const
  GameSettingsFileName = 'UDKGame\Config\PCServer-UDKGame.ini';
    GameURL = 'URL';
      GameURLPort = 'Port';                   GameURLPortDefault = 7777;
      GameURLPeerPort = 'PeerPort';           GameURLPeerPortDefault = 7778;

    GameReplicationInfo = 'Engine.GameReplicationInfo';
      GameReplicationInfoServerName = 'ServerName';
      GameReplicationInfoMessageOfTheDay = 'MessageOfTheDay';

    GameAOCGame = 'AOC.AOCGame';
      GameAOCGameMapList = 'Maplist';


  EngineSettingsFileName = 'UDKGame\Config\PCServer-UDKEngine.ini';
    EngineSteam = 'OnlineSubsystemSteamworks.OnlineSubsystemSteamworks';
      EngineSteamQueryPort = 'QueryPort';     EngineSteamQueryPortDefault = 27015;



{ TChivalryGame }
constructor TChivalryGame.Create(const ALocation: string);
begin
  inherited Create(ALocation);

  FPredefinedMapList := TObjectList<TGameMap>.Create(True);
  FMapList := TObjectList<TGameMap>.Create(True);

  LoadPredefinedMapList(FPredefinedMapList);
end;


destructor TChivalryGame.Destroy;
begin
  FreeAndNil(FMapList);
  FreeAndNil(FPredefinedMapList);

  inherited Destroy;
end;


procedure TChivalryGame.DoLoad;
var
  gameSettings: TUDKIniFile;
  engineSettings: TUDKIniFile;
  mapListValues: TStringList;
  mapName: string;
  mapListChanged: Boolean;

begin
  gameSettings := TUDKIniFile.Create(Location + GameSettingsFileName);
  try
    SetServerPort(gameSettings.ReadInteger(GameURL, GameURLPort, GameURLPortDefault));
    SetPeerPort(gameSettings.ReadInteger(GameURL, GameURLPeerPort, GameURLPeerPortDefault));

    SetServerName(gameSettings.ReadString(GameReplicationInfo, GameReplicationInfoServerName, ''));
    SetMessageOfTheDay(gameSettings.ReadString(GameReplicationInfo, GameReplicationInfoMessageOfTheDay, ''));

    mapListChanged := (FMapList.Count > 0);

    { Maplist is special; it occurs multiple times and order matters }
    mapListValues := TStringList.Create;
    try
      gameSettings.ReadDuplicateStrings(GameAOCGame, GameAOCGameMapList, mapListValues);
      for mapName in mapListValues do
      begin
        FMapList.Add(CreateGameMap(mapName));
        mapListChanged := True;
      end;
    finally
      FreeAndNil(mapListValues);
    end;

    if mapListChanged then
      PropertyChanged('MapList');
  finally
    FreeAndNil(gameSettings);
  end;

  engineSettings := TUDKIniFile.Create(Location + EngineSettingsFileName);
  try
    SetQueryPort(engineSettings.ReadInteger(EngineSteam, EngineSteamQueryPort, EngineSteamQueryPortDefault));
  finally
    FreeAndNil(engineSettings);
  end;
end;


procedure TChivalryGame.DoSave;
var
  gameSettings: TUDKIniFile;
  engineSettings: TUDKIniFile;
  map: TGameMap;

begin
  gameSettings := TUDKIniFile.Create(Location + GameSettingsFileName);
  try
    gameSettings.WriteInteger(GameURL, GameURLPort, ServerPort);
    gameSettings.WriteInteger(GameURL, GameURLPeerPort, PeerPort);

    gameSettings.WriteString(GameReplicationInfo, GameReplicationInfoServerName, ServerName);
    gameSettings.WriteString(GameReplicationInfo, GameReplicationInfoMessageOfTheDay, MessageOfTheDay);

    { Remove all Maplist references before rewriting the list }
    gameSettings.DeleteDuplicateKeys(GameAOCGame, GameAOCGameMapList);

    for map in MapList do
      gameSettings.WriteDuplicateString(GameAOCGame, GameAOCGameMapList, map.Name);

    gameSettings.UpdateFile;
  finally
    FreeAndNil(gameSettings);
  end;

  engineSettings := TUDKIniFile.Create(Location + EngineSettingsFileName);
  try
    engineSettings.WriteInteger(EngineSteam, EngineSteamQueryPort, QueryPort);

    engineSettings.UpdateFile;
  finally
    FreeAndNil(engineSettings);
  end;
end;


function TChivalryGame.CreateGameMap(const AMapName: string): TGameMap;
var
  map: TGameMap;

begin
  Result := nil;

  for map in PredefinedMapList do
    if SameText(map.Name, AMapName) then
    begin
      Result := TGameMap.Create(map);
      break;
    end;


  if not Assigned(Result) then
    Result := TGameMap.Create(AMapName, '', '');
end;


function TChivalryGame.GetServerPort: Integer;
begin
  Result := FServerPort;
end;


function TChivalryGame.GetPeerPort: Integer;
begin
  Result := FPeerPort;
end;


function TChivalryGame.GetQueryPort: Integer;
begin
  Result := FQueryPort;
end;


procedure TChivalryGame.SetServerPort(const Value: Integer);
begin
  if Value <> FServerPort then
  begin
    FServerPort := Value;
    PropertyChanged('ServerPort');
  end;
end;


procedure TChivalryGame.SetPeerPort(const Value: Integer);
begin
  if Value <> FPeerPort then
  begin
    FPeerPort := Value;
    PropertyChanged('PeerPort');
  end;
end;


procedure TChivalryGame.SetQueryPort(const Value: Integer);
begin
  if Value <> FQueryPort then
  begin
    FQueryPort := Value;
    PropertyChanged('QueryPort');
  end;
end;


function TChivalryGame.GetServerName: string;
begin
  Result := FServerName;
end;


function TChivalryGame.GetMessageOfTheDay: string;
begin
  Result := FMessageOfTheDay;
end;


procedure TChivalryGame.SetServerName(const Value: string);
begin
  if Value <> FServerName then
  begin
    FServerName := Value;
    PropertyChanged('ServerName');
  end;
end;


procedure TChivalryGame.SetMessageOfTheDay(const Value: string);
begin
  if Value <> FMessageOfTheDay then
  begin
    FMessageOfTheDay := Value;
    PropertyChanged('MessageOfTheDay');
  end;
end;


function TChivalryGame.GetPredefinedMapList: TEnumerable<TGameMap>;
begin
  Result := FPredefinedMapList;
end;


function TChivalryGame.GetMapList: TEnumerable<TGameMap>;
begin
  Result := FMapList;
end;


function TChivalryGame.GetMapCount: Integer;
begin
  Result := FMapList.Count;
end;


function TChivalryGame.GetMap(Index: Integer): TGameMap;
begin
  Result := FMapList[Index];
end;


procedure TChivalryGame.AddMap(AMap: TGameMap);
begin
  FMapList.Add(AMap);
  PropertyChanged('MapList');
end;


procedure TChivalryGame.InsertMap(Index: Integer; AMap: TGameMap);
begin
  FMapList.Insert(Index, AMap);
  PropertyChanged('MapList');
end;


procedure TChivalryGame.DeleteMap(AIndex: Integer);
begin
  FMapList.Delete(AIndex);
  PropertyChanged('MapList');
end;


procedure TChivalryGame.RemoveMap(AMap: TGameMap);
begin
  if FMapList.Remove(AMap) > -1 then
    PropertyChanged('MapList');
end;


procedure TChivalryGame.MoveMap(ASourceIndex, ATargetIndex: Integer);
begin
  if ATargetIndex <> ASourceIndex then
  begin
    FMapList.Move(ASourceIndex, ATargetIndex);
    PropertyChanged('MapList');
  end;
end;

end.
