{* UltraStar Deluxe - Karaoke Game
 *
 * UltraStar Deluxe is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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; see the file COPYING. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 *}

unit ULuaParty;

interface

{$IFDEF FPC}
  {$MODE Delphi}
{$ENDIF}

{$I switches.inc}

uses ULua;

{ lua c functions from Party table. Enables creating of party modes w/ lua scripts }

{ Party.Register - register party mode at party manager
  arguments: info: table
                   Name: String;          //< Name used as identifier (language strings, etc.). Has to be set.
                   CanNonParty: Boolean   //< mode is playable when not in party mode. defaulted to false if not set
                   CanParty: Boolean      //< mode is playable in party mode. defaulted to false if not set
                   PlayerCount: Table     //< playable with one, two, three etc. players per team. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)
                   TeamCount: Table       //< playable with one, two, three etc. different teams. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)

                   BeforeSongSelect: String   //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
                   AfterSongSelect: String    //< name of global that will be called after song is selected (if nil, not callable or returns true, default action will be executed)

                   BeforeSing: String         //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
                   OnSing: String             //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
                   AfterSing: String          //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)}
function ULuaParty_Register(L: Plua_State): Integer; cdecl;

{ Party.GameFinished - returns true if no party game is running or all rounds
  of current game were played }
function ULuaParty_GameFinished(L: Plua_State): Integer; cdecl;

(* Party.SetRoundRanking - sets ranking of current party round,
  arguments: Ranking: table
  ranking of team i is the value (integer from 1 to number of teams) of the
  table with index [i: number].
  you may call this function in the following way:
    Party.SetRoundRanking({3, 1, 2});
    this means: team 1 is ranked third, team 2 is ranked first and team 3 is
    ranked second.
  if no party game is started or party game is finished
  it will raise an error *)
function ULuaParty_SetRoundRanking(L: Plua_State): Integer; cdecl;

{ Party.GetTeams - returns a table with all information and structure as
  in the TPartyGame.Teams array }
function ULuaParty_GetTeams(L: Plua_State): Integer; cdecl;

{ Party.SetTeams - changes all fields from TPartyGame.Teams that have been
  set in the table given as first argument}
function ULuaParty_SetTeams(L: Plua_State): Integer; cdecl;

const
  ULuaParty_Lib_f: array [0..4] of lual_reg = (
    (name:'Register'; func:ULuaParty_Register),
    (name:'GameFinished'; func:ULuaParty_GameFinished),
    (name:'SetRoundRanking'; func:ULuaParty_SetRoundRanking),
    (name:'GetTeams'; func:ULuaParty_GetTeams),
    (name:'SetTeams'; func:ULuaParty_SetTeams)
  );

implementation
uses ULuaCore, ULuaUtils, UParty, SysUtils;


{ Party.Register - register party mode at party manager
  arguments: info: table
                   Name: String;          //< Name used as identifier (language strings, etc.). Has to be set.
                   CanNonParty: Boolean   //< mode is playable when not in party mode. defaulted to false if not set
                   CanParty: Boolean      //< mode is playable in party mode. defaulted to false if not set
                   PlayerCount: Table     //< playable with one, two, three etc. players per team. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)
                   TeamCount: Table       //< playable with one, two, three etc. different teams. defaulted to no restrictions if not set. (use table constructor e.g. {1, 2, 3) means playable w/ 1, 2 or three players)

                   BeforeSongSelect: String   //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
                   AfterSongSelect: String    //< name of global that will be called after song is selected (if nil, not callable or returns true, default action will be executed)

                   BeforeSing: String         //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
                   OnSing: String             //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)
                   AfterSing: String          //< name of global that will be called before song select screen is shown (if nil, not callable or returns true, default action will be executed)}
function ULuaParty_Register(L: Plua_State): Integer; cdecl;
  var
    Info: TParty_ModeInfo;
    Key: String;
    P: TLuaPlugin;
begin
  Result := 0;
  
  // check for table on stack
  luaL_checkType(L, 1, LUA_TTABLE);

  // get parent id
  P := Lua_GetOwner(L);


  // set mode info to default
  Party.DefaultModeInfo(Info);


  // set parent in info rec and pop it from stack
  Info.Parent := P.Id;

  // go through table elements
  lua_pushNil(L);
  while (lua_Next(L, 1) <> 0) do
  begin
    Key := lowercase(lua_ToString(L, -2));

    if (Key = 'name') and lua_isString(L, -1) then
      Info.Name := lua_toString(L, -1)
    else if (Key = 'cannonparty') and lua_isBoolean(L, -1) then
      Info.CanNonParty := lua_toBoolean(L, -1)
    else if (Key = 'canparty') and lua_isBoolean(L, -1) then
      Info.CanParty := lua_toBoolean(L, -1)
    else if (Key = 'playercount') and lua_isTable(L, -1) then
      Info.PlayerCount := lua_toBinInt(L, -1)
    else if (Key = 'teamcount') and lua_isTable(L, -1) then
      Info.TeamCount := lua_toBinInt(L, -1)
    else if (Key = 'beforesongselect') and lua_isString(L, -1) then
      Info.Functions.BeforeSongSelect := lua_toString(L, -1)
    else if (Key = 'aftersongselect') and lua_isString(L, -1) then
      Info.Functions.AfterSongSelect := lua_toString(L, -1)
    else if (Key = 'beforesing') and lua_isString(L, -1) then
      Info.Functions.BeforeSing := lua_toString(L, -1)
    else if (Key = 'onsing') and lua_isString(L, -1) then
      Info.Functions.OnSing := lua_toString(L, -1)
    else if (Key = 'aftersing') and lua_isString(L, -1) then
      Info.Functions.AfterSing := lua_toString(L, -1);

    // pop value from stack so key is on top
    lua_pop(L, 1);
  end;

  // clear stack from table
  lua_pop(L, lua_gettop(L));

  if not Party.RegisterMode(Info) then
    luaL_error(L, PChar('can''t register party mode at party manager in Party.Register. Is Info.Name defined or is there another mode with this name?'));
end;

{ Party.GameFinished - returns true if no party game is running or all rounds
  of current game were played }
function ULuaParty_GameFinished(L: Plua_State): Integer; cdecl;
begin
  // clear stack
  lua_pop(L, lua_gettop(L));

  // push result
  lua_pushBoolean(L, Party.GameFinished);

  //we return one value
  Result := 1;
end;

{ Party.SetRoundRanking - sets ranking of current party round,
  if no party game is started or party game is finished
  it will raise an error }
function ULuaParty_SetRoundRanking(L: Plua_State): Integer; cdecl;
var
  R: AParty_TeamRanking;
  I: Integer;
  Rank: Integer;
begin
  Result := 0;

  luaL_checktype(L, 1, LUA_TTABLE);

  lua_checkstack(L, 1);

  SetLength(R, Length(Party.Teams));

  for I := 0 to High(R) do
  begin
    lua_pushInteger(L, (I+1));
    lua_gettable(L, 1);

    R[I].Rank := Length(R);
    R[I].Team := I;
    if (lua_isnumber(L, -1)) then
    begin
      Rank := lua_toInteger(L, -1);
      if (Rank >= 1) and (Rank <= Length(R)) then
        R[I].Rank := Rank
    end;

    lua_pop(L, 1);
    
  end;

  // pop table
  lua_pop(L, 1);

  if (not Party.SetRanking(R)) then
    luaL_error(L, PChar('cann''t set party round ranking. Is party started and not finished yet?'));
end;

{ Party.GetTeams - returns a table with all information and structure as
  in the TPartyGame.Teams array }
function ULuaParty_GetTeams(L: Plua_State): Integer; cdecl;
  var
    Team: Integer;
    Player: Integer;
begin
  // clear stack
  lua_pop(L, lua_gettop(L));

  // ensure we have enough stack slots left
  lua_checkstack(L, 7);

  // create the table we want to return
  lua_createtable(L, Length(Party.Teams), 0);

  // add the teams
  for Team := 0 to High(Party.Teams) do
  begin
    // push key for current teams value. lua array beggins at 1
    lua_pushInteger(L, Team + 1);

    // push table containing team info and players table
    lua_createtable(L, 0, 5);

    // team name
    lua_pushString(L, PChar(Party.Teams[Team].Name));
    lua_setField(L, -2, 'Name');

    // team score
    lua_pushInteger(L, Party.Teams[Team].Score);
    lua_setField(L, -2, 'Score');

    // team jokers left
    lua_pushInteger(L, Party.Teams[Team].JokersLeft);
    lua_setField(L, -2, 'JokersLeft');

    // team nextPlayer
    lua_pushInteger(L, Party.Teams[Team].NextPlayer);
    lua_setField(L, -2, 'NextPlayer');

    // team players table
    lua_createtable(L, Length(Party.Teams[Team].Players), 0);

    //add players
    for Player := 0 to High(Party.Teams[Team].Players) do
    begin
      // push key for current players value. lua array beggins at 1
      lua_pushInteger(L, Player + 1);

      // push table containing player info
      lua_createTable(L, 0, 2);

      // player name
      lua_PushString(L, PChar(Party.Teams[Team].Players[Player].Name));
      lua_SetField(L, -2, 'Name');

      // players times played
      lua_PushInteger(L, Party.Teams[Team].Players[Player].TimesPlayed);
      lua_SetField(L, -2, 'TimesPlayed');

      // add value - key - pair to teams player table
      lua_setTable(L, -3);
    end;

    lua_setField(L, -2, 'Players');

    // add value - key - pair to returned table
    lua_setTable(L, -3);
  end;

  // we return 1 value (the first table)
  Result := 1;
end;

{ Party.SetTeams - changes all fields from TPartyGame.Teams that have been
  set in the table given as first argument}
function ULuaParty_SetTeams(L: Plua_State): Integer; cdecl;

  procedure Do_Player(Team, Player: Integer);
    var
      Key: String;
  begin
    if (Player >= 0) and (Player <= High(Party.Teams[Team].Players)) then
    begin
      // go through table elements
      lua_pushNil(L);
      while (lua_Next(L, -2) <> 0) do
      begin
        Key := lowercase(lua_ToString(L, -2));

        if (Key = 'name') and lua_isString(L, -1) then
          Party.Teams[Team].Players[Player].Name := lua_toString(L, -1)
        else if (Key = 'timesplayed') and lua_isNumber(L, -1) then
          Party.Teams[Team].Players[Player].TimesPlayed := lua_toInteger(L, -1);

        // pop value from stack so key is on top
        lua_pop(L, 1);
      end;
    end;
  end;

  procedure Do_Players(Team: Integer);
  begin
    // go through table elements
    lua_pushNil(L);
    while (lua_Next(L, -2) <> 0) do
    begin
      // check if key is a number and value is a table
      if (lua_isNumber(L, -2)) and (lua_isTable(L, -1)) then
        Do_Player(Team, lua_toInteger(L, -2));

      // pop value from stack so key is on top
      lua_pop(L, 1);
    end;
  end;

  procedure Do_Team(Team: Integer);
    var
      Key: String;
  begin
    if (Team >= 0) and (Team <= High(Party.Teams)) then
    begin
      // go through table elements
      lua_pushNil(L);
      while (lua_Next(L, -2) <> 0) do
      begin
        Key := lowercase(lua_ToString(L, -2));

        if (Key = 'name') and lua_isString(L, -1) then
          Party.Teams[Team].Name := lua_toString(L, -1)
        else if (Key = 'score') and lua_isNumber(L, -1) then
          Party.Teams[Team].Score := lua_toInteger(L, -1)
        else if (Key = 'jokersleft') and lua_isNumber(L, -1) then
          Party.Teams[Team].JokersLeft := lua_toInteger(L, -1)
        else if (Key = 'currentplayer') and lua_isNumber(L, -1) then
          Party.Teams[Team].NextPlayer := lua_toInteger(L, -1)
        else if (Key = 'players') and lua_isTable(L, -1) then
          Do_Players(Team);

        // pop value from stack so key is on top
        lua_pop(L, 1);
      end;
    end;
  end;
begin
  Result := 0;
  
  // check for table on stack
  luaL_checkType(L, 1, LUA_TTABLE);


  // go through table elements
  lua_pushNil(L);
  while (lua_Next(L, 1) <> 0) do
  begin
    // check if key is a number and value is a table
    if (lua_isNumber(L, -2)) and (lua_isTable(L, -1)) then
      Do_Team(lua_toInteger(L, -2));

    // pop value from stack so key is on top
    lua_pop(L, 1);
  end;

  // clear stack from table
  lua_pop(L, lua_gettop(L));
end;

end.