--[[
 * 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: https://ultrastardx.svn.sourceforge.net/svnroot/ultrastardx/trunk/game/plugins/teamduel.usdx $
 * $Id: teamduel.usdx 2558 2010-06-23 22:20:13Z whiteshark0 $
 *]]

-- some values to adjust the creation of PlayerChanges
local MinPercentage = 0.06;  -- minimal amount of points between changes (in percent)
local MaxPercentage = 0.12; -- maximal amount of points between changes (in percent)
-- position of big progress bar in the center
local BarPos = {};
BarPos.Top = 30
BarPos.Bottom = 50
BarPos.Left = 300
BarPos.Right = 500

function plugin_init()
  register('party mode: teamduel', '1.00', 'USDX Team', 'http://www.UltrastarDeluxe.org');

  require('math', 'Usdx.Party', 'Usdx.ScreenSing', 'Usdx.Gl', 'Usdx.TextGl');
  local Mode = {}

  Mode.Name = 'teamduel';
  Mode.CanParty = true;
  Mode.PlayerCount = {2,3,4,5,6};

  Mode.BeforeSing = 'BeforeSing'
  Mode.OnSing = 'Sing';

  Usdx.Party.Register(Mode)

  ScreenSing.GetBeat();

  return true;
end

-- called everytime a singing session w/ this party mode is startet
-- we just hook ScreenSing.SongLoaded to prepare the mic changes here
function BeforeSing()
  hSongLoaded = Usdx.Hook('ScreenSing.SongLoaded', 'PrepareChanges');

  -- execute default action (e.g. set correct singing playercount)
  return true
end;

-- adds a new SentenceChange at Line to the PlayerChanges array
function AddChange(Line)
  PlayerChanges[#PlayerChanges + 1] = {};
  PlayerChanges[#PlayerChanges].OnBeat = Lines[Line].Start;

  PlayerChanges[#PlayerChanges].NextPlayer = {}
  for i = 1, #Teams do
    repeat
      PlayerChanges[#PlayerChanges].NextPlayer[i] = math.random(#Teams[i].Players);
    until (1 == #PlayerChanges) or (PlayerChanges[#PlayerChanges].NextPlayer[i] ~= PlayerChanges[#PlayerChanges-1].NextPlayer[i]) or (#Teams[i].Players == 1);
  end;
end;

function PrepareChanges()
  Lines = ScreenSing.GetSongLines();
  Teams = Party.GetTeams();

  -- get sum of hittable beats (per line and total)
  local TotalBeats = 0;
  local LineValue = {};
  for i = 1, #Lines do
    for j = 1, #Lines[i].Notes do
      LineValue[i] = (LineValue[i] or 0) + Lines[i].Notes[j].Length * Lines[i].Notes[j].NoteType;
    end;
    TotalBeats = TotalBeats + LineValue[i];
  end;

  -- calculate changes
  PlayerChanges = {};
  -- fallback if there are only freestyle notes
  -- random count of lines between changes
  if (TotalBeats == 0) then
    local i = 1;
    repeat
      if i > 1 then
        AddChange(i);
      end

      local step = math.ceil((MinPercentage + (MaxPercentage - MinPercentage) * math.random()) * #Lines);
      if step < 1 then
        step = 1;
      end;

      i = i + step;
    until i >= #Lines;
  else -- calculate changes by amount of hittable beats
    local i = 1;
    local BeatsToChange = math.ceil((MinPercentage + (MaxPercentage - MinPercentage) * math.random()) * TotalBeats);
    local Beats = 0;

    repeat
      Beats = Beats + LineValue[i];
      if Beats >= BeatsToChange then
        AddChange(i);
        BeatsToChange = BeatsToChange + math.ceil((MinPercentage + (MaxPercentage - MinPercentage) * math.random()) * TotalBeats);
      end
      i = i + 1;
    until i >= #Lines;
  end;

  -- free lines
  Lines = nil;

  -- init NextPlayerChange
  NextPlayerChange = 1;

  -- calculate OSD position for players
  do
    local RBRect = ScreenSing.GetRBRect();
    OSD = {};

    for i = 1, #RBRect do
      OSD[i] = {};
      OSD[i].Left   = RBRect[i].x;
      OSD[i].Right  = RBRect[i].x + RBRect[i].w;
      OSD[i].Top    = RBRect[i].y + RBRect[i].h;
      OSD[i].Bottom = RBRect[i].y + RBRect[i].h + math.max(RBRect[i].h, 13);
    end;
  end;

  -- remove hook
  hSongLoaded:Unhook();
  hSongLoaded = nil;
end

function DrawPlayerText(i, Text)
  Gl.Disable('GL_TEXTURE_2D');

  -- background
  Gl.Color (0, 0, 0, 1);
  Gl.Begin('GL_QUADS');
    Gl.Vertex(OSD[i].Left, OSD[i].Top);
    Gl.Vertex(OSD[i].Left, OSD[i].Bottom);
    Gl.Vertex(OSD[i].Right, OSD[i].Bottom);
    Gl.Vertex(OSD[i].Right, OSD[i].Top);
  Gl.End();

  -- text
  Gl.Color(1, 0, 0, 1);
  TextGl.Style(1);
  TextGl.Size(18);
  TextGl.Italic(false);
  local PosX = (OSD[i].Left + OSD[i].Right) / 2;
  PosX = PosX - TextGl.Width(Text) / 2;

  TextGl.Pos(PosX, OSD[i].Top - 3);

  TextGl.Print(Text);
end;

-- draws the progress bar for player i
function DrawPlayerProgress(i, Progress)
  Gl.Disable('GL_TEXTURE_2D');

  -- background
  Gl.Color (0, 0, 0, 1);
  Gl.Begin('GL_QUADS');
    Gl.Vertex(OSD[i].Left, OSD[i].Top);
    Gl.Vertex(OSD[i].Left, OSD[i].Bottom);
    Gl.Vertex(OSD[i].Right, OSD[i].Bottom);
    Gl.Vertex(OSD[i].Right, OSD[i].Top);
  Gl.End();

  -- bar
  Gl.Color(1, 0, 0, 1);
  Gl.Begin('GL_QUADS');
    Gl.Vertex(OSD[i].Left + 2, OSD[i].Top + 2);
    Gl.Vertex(OSD[i].Left + 2, OSD[i].Bottom - 2);
    Gl.Vertex(OSD[i].Left + 2 + (OSD[i].Right - OSD[i].Left - 4) * Progress, OSD[i].Bottom - 2);
    Gl.Vertex(OSD[i].Left + 2 + (OSD[i].Right - OSD[i].Left - 4) * Progress, OSD[i].Top + 2);
  Gl.End();
end;

-- draws the big progress bar in the screen center
function DrawCenterProgress(Progress)
  Gl.Disable('GL_TEXTURE_2D');

  -- background
  Gl.Color (0, 0, 0, 1);
  Gl.Begin('GL_QUADS');
    Gl.Vertex(BarPos.Left, BarPos.Top);
    Gl.Vertex(BarPos.Left, BarPos.Bottom);
    Gl.Vertex(BarPos.Right, BarPos.Bottom);
    Gl.Vertex(BarPos.Right, BarPos.Top);
  Gl.End();

  -- bar
  Gl.Color(1, 0, 0, 1);
  Gl.Begin('GL_QUADS');
    Gl.Vertex(BarPos.Left + 2, BarPos.Top + 2);
    Gl.Vertex(BarPos.Left + 2, BarPos.Bottom - 2);
    Gl.Vertex(BarPos.Left + 2 + (BarPos.Right - BarPos.Left - 4) * Progress, BarPos.Bottom - 2);
    Gl.Vertex(BarPos.Left + 2 + (BarPos.Right - BarPos.Left - 4) * Progress, BarPos.Top + 2);
  Gl.End();
end;

function Sing()
  if (NextPlayerChange <= #PlayerChanges) then
    local BeatsToNextChange = PlayerChanges[NextPlayerChange].OnBeat - ScreenSing.GetBeat();
    local TimeToNextChange = ScreenSing.BeatsToSeconds(BeatsToNextChange);

    -- draw next player text or progress bar
    if (TimeToNextChange <= 0) then
      --there is a change
      NextPlayerChange = NextPlayerChange + 1;

    elseif (TimeToNextChange <= 5) then
      for i = 1, #Teams do
        DrawPlayerProgress(i, 1 - TimeToNextChange/5);
      end;
    elseif (TimeToNextChange <= 6.5) then
      for i = 1, #Teams do
        DrawPlayerText(i, Teams[i].Players[PlayerChanges[NextPlayerChange].NextPlayer[i]].Name);
      end;
    elseif (TimeToNextChange <= 8) then
      for i = 1, #Teams do
        DrawPlayerText(i, 'Next Player');
      end;
    elseif (TimeToNextChange <= 9.5) then
      for i = 1, #Teams do
        DrawPlayerText(i, Teams[i].Players[PlayerChanges[NextPlayerChange].NextPlayer[i]].Name);
      end;
    elseif (TimeToNextChange <= 11) then
      for i = 1, #Teams do
        DrawPlayerText(i, 'Next Player');
      end;
    end

    if (TimeToNextChange <= 11) then
      DrawCenterProgress(1 - TimeToNextChange/11);
    end;
  end;
end