unit Frm_MainVCL;

interface

uses  System.SysUtils, System.Variants, System.Classes, System.IOUtils,
      System.StrUtils,
      Winapi.Windows, Winapi.Messages,
      Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
      Vcl.Samples.Spin;

type
  TfrmMain = class(TForm)
    memoLog: TMemo;
    speNumberOfPlayers: TSpinEdit;
    btnGenerate: TButton;
    txtFolder: TEdit;
    lblFolder: TLabel;
    btnFolder: TButton;
    procedure btnGenerateClick(Sender: TObject);
  private
    { Private declarations }
    procedure Log(Msg : String);

    function  DeviceDescription_Create(SrceFQFilename, DestFQFilename : String; PlayerCount : Integer) : Boolean; overload;
    function  DeviceDescription_Create_FR(Folder : String; PlayerCount : Integer) : Boolean;
    function  DeviceDescription_Create_RTI(Folder : String; PlayerCount : Integer) : Boolean;

    function  ConfigSettings_PlayerList(PlayerCount : Integer) : TStringList;
    function  ConfigSettings_Create(SrceFQFilename, DestFQFilename : String; PlayerCount : Integer) : Boolean; overload;
    function  ConfigSettings_Create(Folder : String; PlayerCount : Integer) : Boolean; overload;

    function  SystemFunctions_PlayerList(PlayerCount : Integer; IncludeSelected : Boolean) : TStringList;
    function  SystemFunctions_Create(SrceFQFilename, DestFQFilename : String; PlayerCount : Integer) : Boolean; overload;
    function  SystemFunctions_Create(Folder : String; PlayerCount : Integer) : Boolean; overload;

    function  SystemVariables_IndividualPlayer(PlayerCount : Integer) : TStringList;
    function  SystemVariables_SyncPlayerList(PlayerCount : Integer) : TStringList;
    function  SystemVariables_SelPlayerList(PlayerCount : Integer) : TStringList;
    function  SystemVariables_Create(SrceFQFilename, DestFQFilename : String; PlayerCount : Integer) : Boolean; overload;
    function  SystemVariables_Create(Folder : String; PlayerCount : Integer) : Boolean; overload;

    function  SystemEvents_PlayerList(PlayerCount : Integer) : TStringList;
    function  SystemEvents_Create(SrceFQFilename, DestFQFilename : String; PlayerCount : Integer) : Boolean; overload;
    function  SystemEvents_Create(Folder : String; PlayerCount : Integer) : Boolean; overload;
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

const
  FILENAME_DEVICEDESCRIPTION_FR  = '0_DeviceDescription_FR.xml'; // For reading player count!
  FILENAME_DEVICEDESCRIPTION_RTI = '0_DeviceDescription_RTI.xml'; // For reading player count!
//  FILENAME_DEVICEDESCRIPTION  = '0_DeviceDescription.xml'; // For reading player count!
//  FILENAME_CONFIGSETTINGS_FR  = '1_ConfigSettings_FR.xml';
//  FILENAME_CONFIGSETTINGS_RTI = '1_ConfigSettings_RTI.xml';
  FILENAME_CONFIGSETTINGS     = '1_ConfigSettings.xml';
  FILENAME_SYSTEMFUNCTIONS    = '2_SystemFunctions.xml';
  FILENAME_SYSTEMVARIABLES    = '3_SystemVariables.xml';
  FILENAME_SYSTEMEVENTS       = '4_SystemEvents.xml';

procedure TfrmMain.Log(Msg : String);
begin
  memoLog.Lines.Add(Msg);
end;

{$REGION 'Device Description'}
function  TfrmMain.DeviceDescription_Create(SrceFQFilename, DestFQFilename : String; PlayerCount : Integer) : Boolean;
var
  x : Integer;
  Line : String;
  Srce, Dest : TStringList;
begin
  Result := False;
  if not TFile.Exists(SrceFQFilename) then begin
    Log('ERROR: DeviceDescription source "' + SrceFQFilename + '" not found.');
    Exit;
  end;

  Srce := TStringList.Create;
  Dest := TStringList.Create;
  try
    Srce.LoadFromFile(SrceFQFilename);
    for x := 0 to Srce.Count -1 do begin
      Line := Srce[x];
      if AnsiContainsStr(Line, '%PLAYER_COUNT%') then begin
        Line := ReplaceStr(Line, '%PLAYER_COUNT%', IntToStr(PlayerCount));
        Log('Added player count of ' + IntToStr(PlayerCount) + ' to DeviceDescription at ' + IntToStr(x));
      end;
      Dest.Add(Line);
    end;
    Dest.SaveToFile(DestFQFilename);
    Result := True;
  finally
    Srce.Free;
    Dest.Free;
  end;
end;

function  TfrmMain.DeviceDescription_Create_FR(Folder : String; PlayerCount : Integer) : Boolean;
var
  Srce, Dest : String;
begin
  Result := False;
  if not TDirectory.Exists(Folder) then Exit;

  Srce := IncludeTrailingPathDelimiter(Folder) + 'Template_' + FILENAME_DEVICEDESCRIPTION_FR;
  Dest := IncludeTrailingPathDelimiter(Folder) + FILENAME_DEVICEDESCRIPTION_FR;
  Result := DeviceDescription_Create(Srce, Dest, PlayerCount);
end;

function  TfrmMain.DeviceDescription_Create_RTI(Folder : String; PlayerCount : Integer) : Boolean;
var
  Srce, Dest : String;
begin
  Result := False;
  if not TDirectory.Exists(Folder) then Exit;

  Srce := IncludeTrailingPathDelimiter(Folder) + 'Template_' + FILENAME_DEVICEDESCRIPTION_RTI;
  Dest := IncludeTrailingPathDelimiter(Folder) + FILENAME_DEVICEDESCRIPTION_RTI;
  Result := DeviceDescription_Create(Srce, Dest, PlayerCount);
end;
{$ENDREGION}

{$REGION 'Config Settings'}
function  TfrmMain.ConfigSettings_PlayerList(PlayerCount : Integer) : TStringList;
var
  x : Integer;
begin
  Result := TStringList.Create;
  for x := 0 to speNumberOfPlayers.Value -1 do begin
    Result.Add('    <setting name="Player ' + IntToStr(x +1) + ' Name"         type="string" variable="Player' + IntToStr(x) + '_Name"  sourceid="Player' + IntToStr(x +1) + '"  newsourcedefault="Player ' + IntToStr(x +1) + '"  condition="$Player_Count > ' + IntToStr(x) + '" />');
    Result.Add('    <setting name="Player ' + IntToStr(x +1) + ' MAC Address"  type="string" variable="Player' + IntToStr(x) + '_MAC"   sourceid="Player' + IntToStr(x +1) + '"  default="xx:xx:xx:xx:xx:xx"  condition="$Player_Count > ' + IntToStr(x) + '" description="The MAC address from the configuration utility (Manage/Players). [Format = xx:xx:xx:xx:xx:xx where x is a hex digit from 0-F]" />');
  end;
end;

function  TfrmMain.ConfigSettings_Create(SrceFQFilename, DestFQFilename : String; PlayerCount : Integer) : Boolean;
var
  x : Integer;
  Srce, Dest, Insert : TStringList;
begin
  Result := False;
  if not TFile.Exists(SrceFQFilename) then begin
    Log('ERROR: ConfigSettings source "' + SrceFQFilename + '" not found.');
    Exit;
  end;

  Insert := ConfigSettings_PlayerList(PlayerCount);
  Srce := TStringList.Create;
  Dest := TStringList.Create;
  try
    Srce.LoadFromFile(SrceFQFilename);
    for x := 0 to Srce.Count -1 do begin
      if AnsiContainsText(Srce[x], '<INSERT_PLAYER_LIST/>') then begin
        Dest.AddStrings(Insert);
        Log('Added ' + IntToStr(Insert.Count) + ' player list (sel) lines to ConfigSettings at ' + IntToStr(x));
      end else Dest.Add(Srce[x]);
    end;
    Dest.SaveToFile(DestFQFilename);
    Result := True;
  finally
    Srce.Free;
    Dest.Free;
    Insert.Free;
  end;
end;

function  TfrmMain.ConfigSettings_Create(Folder : String; PlayerCount : Integer) : Boolean;
var
  Srce, Dest : String;
begin
  Result := False;
  if not TDirectory.Exists(Folder) then Exit;

  Srce := IncludeTrailingPathDelimiter(Folder) + 'Template_' + FILENAME_CONFIGSETTINGS;
  Dest := IncludeTrailingPathDelimiter(Folder) + FILENAME_CONFIGSETTINGS;
  Result := ConfigSettings_Create(Srce, Dest, PlayerCount);
end;

//function  TfrmMain.ConfigSettings_FR_Create(Folder : String; PlayerCount : Integer) : Boolean;
//var
//  Srce, Dest : String;
//begin
//  Result := False;
//  if not TDirectory.Exists(Folder) then Exit;
//
//  Srce := IncludeTrailingPathDelimiter(Folder) + 'Template_' + FILENAME_CONFIGSETTINGS_FR;
//  Dest := IncludeTrailingPathDelimiter(Folder) + FILENAME_CONFIGSETTINGS_FR;
//  Result := ConfigSettings_Create(Srce, Dest, PlayerCount);
//end;
//
//function  TfrmMain.ConfigSettings_RTI_Create(Folder : String; PlayerCount : Integer) : Boolean;
//var
//  Srce, Dest : String;
//begin
//  Result := False;
//  if not TDirectory.Exists(Folder) then Exit;
//
//  Srce := IncludeTrailingPathDelimiter(Folder) + 'Template_' + FILENAME_CONFIGSETTINGS_RTI;
//  Dest := IncludeTrailingPathDelimiter(Folder) + FILENAME_CONFIGSETTINGS_RTI;
//  Result := ConfigSettings_Create(Srce, Dest, PlayerCount);
//end;
{$ENDREGION}

{$REGION 'System Functions'}
function  TfrmMain.SystemFunctions_PlayerList(PlayerCount : Integer; IncludeSelected : Boolean) : TStringList;
var
  x : Integer;
begin
  Result := TStringList.Create;
  if IncludeSelected then Result.Add('        <choice name="&lt;selected&gt;" value="-1" condition="len $Player0_MAC"/>');
  for x := 0 to PlayerCount -1 do begin
    Result.Add('        <choice name="%%Player' + IntToStr(x) + '_Name%%" value="' + IntToStr(x) + '" sourceid="Player' + IntToStr(x +1) + '" condition="len $Player' + IntToStr(x) + '_MAC"/>');
  end;
end;

function  TfrmMain.SystemFunctions_Create(SrceFQFilename, DestFQFilename : String; PlayerCount : Integer) : Boolean;
var
  x : Integer;
  Srce, Dest, Insert, InsertSel : TStringList;
begin
  Result := False;
  if not TFile.Exists(SrceFQFilename) then begin
    Log('ERROR: SystemFunctions source "' + SrceFQFilename + '" not found.');
    Exit;
  end;

  Insert    := SystemFunctions_PlayerList(PlayerCount, False);
  InsertSel := SystemFunctions_PlayerList(PlayerCount, True);
  Srce := TStringList.Create;
  Dest := TStringList.Create;
  try
    Srce.LoadFromFile(SrceFQFilename);
    for x := 0 to Srce.Count -1 do begin
      if AnsiContainsText(Srce[x], '<INSERT_PLAYER_LIST_WITH_SELECTED/>') then begin
        Dest.AddStrings(InsertSel);
        Log('Added ' + IntToStr(InsertSel.Count) + ' player list (sel) lines to SystemFunctions at ' + IntToStr(x));
      end else if AnsiContainsText(Srce[x], '<INSERT_PLAYER_LIST/>') then begin
        Dest.AddStrings(Insert);
        Log('Added ' + IntToStr(Insert.Count) + ' player list lines to SystemFunctions at ' + IntToStr(x));
      end else Dest.Add(Srce[x]);
    end;
    Dest.SaveToFile(DestFQFilename);
    Result := True;
  finally
    Srce.Free;
    Dest.Free;
    Insert.Free;
    InsertSel.Free;
  end;
end;

function  TfrmMain.SystemFunctions_Create(Folder : String; PlayerCount : Integer) : Boolean;
var
  Srce, Dest : String;
begin
  Result := False;
  if not TDirectory.Exists(Folder) then Exit;

  Srce := IncludeTrailingPathDelimiter(Folder) + 'Template_' + FILENAME_SYSTEMFUNCTIONS;
  Dest := IncludeTrailingPathDelimiter(Folder) + FILENAME_SYSTEMFUNCTIONS;
  Result := SystemFunctions_Create(Srce, Dest, PlayerCount);
end;
{$ENDREGION}

{$REGION 'System Variables'}
function  TfrmMain.SystemVariables_IndividualPlayer(PlayerCount : Integer) : TStringList;
var
  x : Integer;
begin
  Result := TStringList.Create;
  for x := 0 to speNumberOfPlayers.Value -1 do begin
    Result.Add('  <category name="%%Player' + IntToStr(x) + '_Name%%" sourceid="Player' + IntToStr(x +1) + '" condition="($Player_Count &gt; ' + IntToStr(x) + ') and (len $Player' + IntToStr(x) + '_MAC)">');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Configured Name (ID9)"   sysvar="SysVar_Player' + IntToStr(x) + '_S_UserName"                      type="string"    sample="Player ' + IntToStr(x +1) + '"  />');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Name from System"        sysvar="SysVar_Player' + IntToStr(x) + '_S_RealName"                      type="string"    sample="' + IntToStr(x +1) + '_Zone ' + IntToStr(x +1) + '"  buttontag="Instance ID"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Player Power"            sysvar="SysVar_Player' + IntToStr(x) + '_B_Power"                         type="boolean"   format="B:Off:On"/>');
    Result.Add('');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Transport State"         sysvar="SysVar_Player' + IntToStr(x) + '_I_Transport"                     type="integer"   sample="0"     format="L:0:Stop:1:Pause:2:Play" buttontag="Transport" />');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Playing"                 sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Playing"             type="boolean"   sample="true"  format="B: :Playing"             buttontag="Play,Play/Pause"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Not Playing"             sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_NotPlaying"          type="boolean"   sample="true"  format="B: :NOT Playing"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Paused"                  sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Paused"              type="boolean"   sample="true"  format="B: :Paused"              buttontag="Pause"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Stopped"                 sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Stopped"             type="boolean"   sample="true"  format="B: :Stopped"             buttontag="Stop"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Can Play"                sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_CanPlay"             type="boolean"   sample="true"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Can Pause"               sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_CanPause"            type="boolean"   sample="true"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Can Stop"                sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_CanStop"             type="boolean"   sample="true"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Can Previous"            sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_CanPrev"             type="boolean"   sample="true"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Can Next"                sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_CanNext"             type="boolean"   sample="true"/>');
    Result.Add('');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Position (in seconds)"   sysvar="SysVar_Player' + IntToStr(x) + '_I_Transport_Position"            type="integer"   sample="91" />');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Position (as time)"      sysvar="SysVar_Player' + IntToStr(x) + '_S_Transport_Position_AsString"   type="string"    sample="1:31"                    buttontag="NP Elapsed"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Duration (in seconds)"   sysvar="SysVar_Player' + IntToStr(x) + '_I_Transport_Duration"            type="integer"   sample="197"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Duration (as time)"      sysvar="SysVar_Player' + IntToStr(x) + '_S_Transport_Duration_AsString"   type="string"    sample="3:17"                    buttontag="NP Duration"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Progress (percent)"      sysvar="SysVar_Player' + IntToStr(x) + '_I_Transport_Progress"            type="integer"   sample="73"    min="0" max="100" buttontag="NP Progress"/>');
    Result.Add('');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Volume"                  sysvar="SysVar_Player' + IntToStr(x) + '_I_Transport_Volume"              type="integer"   sample="70"                                         min="0" max="100"  buttontag="Volume"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Mute"                    sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Mute"                type="boolean"   sample="true"  format="B: :Muted"                                      buttontag="Mute,Mute Off"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Repeat"                  sysvar="SysVar_Player' + IntToStr(x) + '_I_Transport_Repeat"              type="integer"   sample="0"     format="L:0:None:1:Song:2:Playlist" buttontag="Repeat"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Repeat None"             sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Repeat_None"         type="boolean"   sample="true"  format="B: :None"                   buttontag="Repeat Off"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Repeat Song"             sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Repeat_Song"         type="boolean"   sample="true"  format="B: :Song"                   buttontag="Repeat 1"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Repeat Playlist"         sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Repeat_Playlist"     type="boolean"   sample="true"  format="B: :Playlist"               buttontag="Repeat On"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Repeat (any)"            sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Repeat_Any"          type="boolean"   sample="true"  format="B: :Repeat"                 buttontag="Repeat"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Shuffle"                 sysvar="SysVar_Player' + IntToStr(x) + '_I_Transport_Shuffle"             type="integer"   sample="0"     format="L:0:None:1:Song:2:Album"    buttontag="Shuffle"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Shuffle None"            sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Shuffle_None"        type="boolean"   sample="true"  format="B: :None"                   buttontag="Shuffle Off"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Shuffle Song"            sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Shuffle_Song"        type="boolean"   sample="true"  format="B: :Song"                   buttontag="Shuffle On"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Shuffle Album"           sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Shuffle_Album"       type="boolean"   sample="true"  format="B: :Album"                  buttontag="Shuffle Album"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Shuffle (any)"           sysvar="SysVar_Player' + IntToStr(x) + '_B_Transport_Shuffle_Any"         type="boolean"   sample="true"  format="B: :Shuffle"                buttontag="Shuffle"/>');
    Result.Add('');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Queue List"              sysvar="SysVar_Player' + IntToStr(x) + '_L_Queue_Entries%"                type="list"                                       buttontag="Queue"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Queue Index"             sysvar="SysVar_Player' + IntToStr(x) + '_I_Queue_Index"                   type="integer"   sample="0"                       buttontag="Queue Index"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Queue Count"             sysvar="SysVar_Player' + IntToStr(x) + '_I_Queue_Count"                   type="integer"   sample="12"                      buttontag="Queue Count"/>');
    Result.Add('');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Now Playing Source"      sysvar="SysVar_Player' + IntToStr(x) + '_S_NowPlaying_Source"             type="string"    sample="Spotify"                          buttontag="NP Source"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Now Playing Station"     sysvar="SysVar_Player' + IntToStr(x) + '_S_NowPlaying_Station"            type="string"    sample="100.9 FM"                         buttontag="NP Station"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Now Playing Track"       sysvar="SysVar_Player' + IntToStr(x) + '_S_NowPlaying_Track"              type="string"    sample="I''ve Got the World on a String"  buttontag="NP Title"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Now Playing Artist"      sysvar="SysVar_Player' + IntToStr(x) + '_S_NowPlaying_Artist"             type="string"    sample="Frank Sinatra"                    buttontag="NP Artist"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Now Playing Album"       sysvar="SysVar_Player' + IntToStr(x) + '_S_NowPlaying_Album"              type="string"    sample="This is Sinatra"                  buttontag="NP Album"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Now Playing Genre"       sysvar="SysVar_Player' + IntToStr(x) + '_S_NowPlaying_Genre"              type="string"    sample="Jazz"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Now Playing Path"        sysvar="SysVar_Player' + IntToStr(x) + '_S_NowPlaying_Path"               type="string"    sample="/path"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Now Playing Art"         sysvar="SysVar_Player' + IntToStr(x) + '_A_NowPlaying_Art"                type="image"                                      buttontag="NP Cover"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Now Playing Art 2"       sysvar="SysVar_Player' + IntToStr(x) + '_A_NowPlaying_Art2"               type="image"                                      buttontag="NP Cover 2"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Can Rate"                sysvar="SysVar_Player' + IntToStr(x) + '_B_NowPlaying_CanRate"            type="boolean"   format="B: :Rateable"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Can Rate Positive"       sysvar="SysVar_Player' + IntToStr(x) + '_B_NowPlaying_CanRatePositive"    type="boolean"   format="B: :Rateable"            buttontag="Thumbs Up"     tagtype="visible"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Can Rate Negative"       sysvar="SysVar_Player' + IntToStr(x) + '_B_NowPlaying_CanRateNegative"    type="boolean"   format="B: :Rateable"            buttontag="Thumbs Down"   tagtype="visible"/>');
    Result.Add('  </category>');
    Result.Add('');
  end;
end;

function  TfrmMain.SystemVariables_SelPlayerList(PlayerCount : Integer) : TStringList;
var
  x : Integer;
begin
  Result := TStringList.Create;
  for x := 0 to speNumberOfPlayers.Value -1 do begin
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Selected" sysvar="SysVar_PlayerS_B_Player' + IntToStr(x) + '%" type="boolean" condition="($Player_Count &gt; ' + IntToStr(x) + ') and (len $Player' + IntToStr(x) + '_MAC)"/>');
  end;
end;

function  TfrmMain.SystemVariables_SyncPlayerList(PlayerCount : Integer) : TStringList;
var
  x : Integer;
begin
  Result := TStringList.Create;
  for x := 0 to speNumberOfPlayers.Value -1 do begin
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Synchronized"      sysvar="SysVar_Player' + IntToStr(x) + '_B_Synced"      type="boolean"   format="B: :Synced"  condition="($Player_Count &gt; ' + IntToStr(x) + ') and (len $Player' + IntToStr(x) + '_MAC)"/>');
    Result.Add('    <variable name="%%Player' + IntToStr(x) + '_Name%% Synced To"         sysvar="SysVar_Player' + IntToStr(x) + '_I_SyncedTo"    type="integer"                        condition="($Player_Count &gt; ' + IntToStr(x) + ') and (len $Player' + IntToStr(x) + '_MAC)"/>');
  end;
end;

function  TfrmMain.SystemVariables_Create(SrceFQFilename, DestFQFilename : String; PlayerCount : Integer) : Boolean;
var
  x : Integer;
  Srce, Dest, InsertPlayer, InsertSyncList, InsertSelList : TStringList;
begin
  Result := False;
  if not TFile.Exists(SrceFQFilename) then begin
    Log('ERROR: SystemVariables source "' + SrceFQFilename + '" not found.');
    Exit;
  end;

  InsertPlayer := SystemVariables_IndividualPlayer(PlayerCount);
  InsertSyncList := SystemVariables_SyncPlayerList(PlayerCount);
  InsertSelList := SystemVariables_SelPlayerList(PlayerCount);
  Srce := TStringList.Create;
  Dest := TStringList.Create;
  try
    Srce.LoadFromFile(SrceFQFilename);
    for x := 0 to Srce.Count -1 do begin
      if AnsiContainsText(Srce[x], '<INSERT_PLAYER_VARIABLES/>') then begin
        Dest.AddStrings(InsertPlayer);
        Log('Added ' + IntToStr(InsertPlayer.Count) + ' player variable lines to SystemVariables at ' + IntToStr(x));
      end else if AnsiContainsText(Srce[x], '<INSERT_SYNC_PLAYER_LIST/>') then begin
        Dest.AddStrings(InsertSyncList);
        Log('Added ' + IntToStr(InsertSyncList.Count) + ' player sync lines to SystemVariables at ' + IntToStr(x));
      end else if AnsiContainsText(Srce[x], '<INSERT_SELECTED_PLAYER_LIST/>') then begin
        Dest.AddStrings(InsertSelList);
        Log('Added ' + IntToStr(InsertSelList.Count) + ' player select lines to SystemVariables at ' + IntToStr(x));
      end else Dest.Add(Srce[x]);
    end;
    Dest.SaveToFile(DestFQFilename);
    Result := True;
  finally
    Srce.Free;
    Dest.Free;
    InsertPlayer.Free;
    InsertSyncList.Free;
    InsertSelList.Free;
  end;
end;

function  TfrmMain.SystemVariables_Create(Folder : String; PlayerCount : Integer) : Boolean;
var
  Srce, Dest : String;
begin
  Result := False;
  if not TDirectory.Exists(Folder) then Exit;

  Srce := IncludeTrailingPathDelimiter(Folder) + 'Template_' + FILENAME_SYSTEMVARIABLES;
  Dest := IncludeTrailingPathDelimiter(Folder) + FILENAME_SYSTEMVARIABLES;
  Result := SystemVariables_Create(Srce, Dest, PlayerCount);
end;
{$ENDREGION}

{$REGION 'System Events'}
function  TfrmMain.SystemEvents_PlayerList(PlayerCount : Integer) : TStringList;
var
  x : Integer;
begin
  Result := TStringList.Create;
  for x := 0 to speNumberOfPlayers.Value -1 do begin
    Result.Add('  <category name="Player %%Player' + IntToStr(x) + '_Name%% (%%Player' + IntToStr(x) + '_MAC%%) Events" sourceid="Player' + IntToStr(x +1) + '" condition="($Player_Count &gt; ' + IntToStr(x) + ') and (len $Player' + IntToStr(x) + '_MAC)">');
    Result.Add('    <event name="Play"            tag="SysEvent_Player' + IntToStr(x) + '_TransportPlay" />');
    Result.Add('    <event name="Pause"           tag="SysEvent_Player' + IntToStr(x) + '_TransportPause" />');
    Result.Add('    <event name="Stop"            tag="SysEvent_Player' + IntToStr(x) + '_TransportStop" />');
    Result.Add('    <event name="Shuffle None"    tag="SysEvent_Player' + IntToStr(x) + '_ShuffleNone" />');
    Result.Add('    <event name="Shuffle Song"    tag="SysEvent_Player' + IntToStr(x) + '_ShuffleSong" />');
    Result.Add('    <event name="Shuffle Album"   tag="SysEvent_Player' + IntToStr(x) + '_ShuffleAlbum" />');
    Result.Add('    <event name="Repeat None"     tag="SysEvent_Player' + IntToStr(x) + '_RepeatNone" />');
    Result.Add('    <event name="Repeat Song"     tag="SysEvent_Player' + IntToStr(x) + '_RepeatSong" />');
    Result.Add('    <event name="Repeat Playlist" tag="SysEvent_Player' + IntToStr(x) + '_RepeatPlaylist"/>');
    Result.Add('    <event name="Source Change"   tag="SysEvent_Player' + IntToStr(x) + '_SourceChange"/>');

    Result.Add('    <event name="Source Change (My Music)"       tag="SysEvent_Player' + IntToStr(x) + '_SourceChange_MyMusic"/>');
    Result.Add('    <event name="Source Change (Internet Radio)" tag="SysEvent_Player' + IntToStr(x) + '_SourceChange_InternetRadio"/>');
    Result.Add('    <event name="Source Change (Favorites)"      tag="SysEvent_Player' + IntToStr(x) + '_SourceChange_Favorites"/>');
    Result.Add('    <event name="Source Change (Airplay 2)"      tag="SysEvent_Player' + IntToStr(x) + '_SourceChange_Airplay2"/>');
    Result.Add('    <event name="Source Change (BBC Sounds)"     tag="SysEvent_Player' + IntToStr(x) + '_SourceChange_BBCSounds"/>');
    Result.Add('    <event name="Source Change (iHeartRadio)"    tag="SysEvent_Player' + IntToStr(x) + '_SourceChange_iHeartRadio"/>');
    Result.Add('    <event name="Source Change (Pandora)"        tag="SysEvent_Player' + IntToStr(x) + '_SourceChange_Pandora"/>');
    Result.Add('    <event name="Source Change (Qobuz)"          tag="SysEvent_Player' + IntToStr(x) + '_SourceChange_Qobuz"/>');
    Result.Add('    <event name="Source Change (SiriusXM)"       tag="SysEvent_Player' + IntToStr(x) + '_SourceChange_SiriusXM"/>');
    Result.Add('    <event name="Source Change (SoundMachine)"   tag="SysEvent_Player' + IntToStr(x) + '_SourceChange_SoundMachine"/>');
    Result.Add('    <event name="Source Change (Spotify)"        tag="SysEvent_Player' + IntToStr(x) + '_SourceChange_Spotify"/>');
    Result.Add('  </category>');
    Result.Add('');
  end;
end;

function  TfrmMain.SystemEvents_Create(SrceFQFilename, DestFQFilename : String; PlayerCount : Integer) : Boolean;
var
  x : Integer;
  Srce, Dest, Insert : TStringList;
begin
  Result := False;
  if not TFile.Exists(SrceFQFilename) then begin
    Log('ERROR: SystemEvents source "' + SrceFQFilename + '" not found.');
    Exit;
  end;

  Insert := SystemEvents_PlayerList(PlayerCount);
  Srce := TStringList.Create;
  Dest := TStringList.Create;
  try
    Srce.LoadFromFile(SrceFQFilename);
    for x := 0 to Srce.Count -1 do begin
      if AnsiContainsText(Srce[x], '<INSERT_PLAYER_CATEGORY/>') then begin
        Dest.AddStrings(Insert);
        Log('Added ' + IntToStr(Insert.Count) + ' player event lines to SystemEvents at ' + IntToStr(x));
      end else Dest.Add(Srce[x]);
    end;
    Dest.SaveToFile(DestFQFilename);
    Result := True;
  finally
    Srce.Free;
    Dest.Free;
    Insert.Free;
  end;
end;

function  TfrmMain.SystemEvents_Create(Folder : String; PlayerCount : Integer) : Boolean;
var
  Srce, Dest : String;
begin
  Result := False;
  if not TDirectory.Exists(Folder) then Exit;

  Srce := IncludeTrailingPathDelimiter(Folder) + 'Template_' + FILENAME_SYSTEMEVENTS;
  Dest := IncludeTrailingPathDelimiter(Folder) + FILENAME_SYSTEMEVENTS;
  Result := SystemEvents_Create(Srce, Dest, PlayerCount);
end;
{$ENDREGION}

procedure TfrmMain.btnGenerateClick(Sender: TObject);
var
  Folder : String;
begin
  memoLog.Lines.Clear;

  Folder := 'P:\FusionRD\Software\FusionMusicSystem_Generation2\3rd Party Drivers\RTI v2.0\Music Server\Source\Configuration\';
  Folder := IncludeTrailingPathDelimiter(Trim(txtFolder.Text));

  Log('Working in folder: ' + Folder);
  if TDirectory.Exists(Folder) then begin
    if not DeviceDescription_Create_FR(Folder, speNumberOfPlayers.Value) then Log('ERROR: Can''t create DeviceDescription (FR)');
    if not DeviceDescription_Create_RTI(Folder, speNumberOfPlayers.Value) then Log('ERROR: Can''t create DeviceDescription (RTI)');
    if not ConfigSettings_Create(Folder, speNumberOfPlayers.Value) then Log('ERROR: Can''t create ConfigSettings');
    if not SystemFunctions_Create(Folder, speNumberOfPlayers.Value) then Log('ERROR: Can''t create SystemFunctions');
    if not SystemVariables_Create(Folder, speNumberOfPlayers.Value) then Log('ERROR: Can''t create SystemVariables');
    if not SystemEvents_Create(Folder, speNumberOfPlayers.Value) then Log('ERROR: Can''t create SystemEvents');
  end else Log('ERROR: Folder not found');
end;

end.
