{* 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 UScreenSingModi;

interface

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

{$I switches.inc}

uses
  UMenu,
  UMusic,
  SDL,
  SysUtils,
  UFiles,
  UTime,
  USongs,
  UIni,
  ULog,
  UTexture,
  ULyrics,
  TextGL,
  gl,
  UPath,
  UThemes,
  UScreenSing,
  ModiSDK;

type
  TScreenSingModi = class(TScreenSing)
    protected
    
    public
      Winner: byte; //Who Wins
      PlayerInfo: TPlayerInfo;
      TeamInfo:   TTeamInfo;

      constructor Create; override;
      procedure OnShow; override;
      //procedure onShowFinish; override;
      function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override;
      function Draw: boolean; override;
      procedure Finish; override;
  end;

type
  TCustomSoundEntry = record
    Filename : IPath;
    Stream   : TAudioPlaybackStream;
  end;

var
  //Custom Sounds
  CustomSounds: array of TCustomSoundEntry;

//Procedured for Plugin
function LoadTex(const Name: PChar; Typ: TTextureType): TsmallTexture;
  {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF}
//function Translate (const Name: PChar): PChar;
//  {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF}
//Procedure to Print Text
procedure Print(const Style, Size: byte; const X, Y: real; const Text: PChar);
  {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF}
//Procedure that loads a Custom Sound
function LoadSound(const Name: PChar): cardinal;
  {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF}
//Plays a Custom Sound
procedure PlaySound(const Index: cardinal);
  {$IFDEF MSWINDOWS} stdcall; {$ELSE} cdecl; {$ENDIF}

//Utilys
function ToSentences(Const Lines: TLines): TSentences;

implementation

uses
  Classes, 
  Math,
  UDLLManager,
  UDraw,
  UGraphic,
  UGraphicClasses,
  ULanguage,
  UNote,
  UPathUtils,
  URecord, 
  USkins;

// Method for input parsing. If false is returned, GetNextWindow
// should be checked to know the next window to load;
function TScreenSingModi.ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean;
begin
  Result := true;
  if (PressedDown) then
  begin // Key Down
    case PressedKey of

      SDLK_ESCAPE,
      SDLK_BACKSPACE :
        begin
          Finish;
          AudioPlayback.PlaySound(SoundLib.Back);
          FadeTo(@ScreenPartyScore);
        end;

      else
        Result := inherited ParseInput(PressedKey, CharCode, PressedDown);
    end;
  end;
end;

constructor TScreenSingModi.Create;
begin
  inherited Create;

end;

function ToSentences(Const Lines: TLines): TSentences;
var
  I, J: integer;
begin
  Result.Current     := Lines.Current;
  Result.High        := Lines.High;
  Result.Number      := Lines.Number;
  Result.Resolution  := Lines.Resolution;
  Result.NotesGAP    := Lines.NotesGAP;
  Result.TotalLength := Lines.ScoreValue;

  SetLength(Result.Sentence, Length(Lines.Line));
  for I := low(Result.Sentence) to high(Result.Sentence) do
  begin
    Result.Sentence[I].Start      := Lines.Line[I].Start;
    Result.Sentence[I].StartNote  := Lines.Line[I].Note[0].Start;
    Result.Sentence[I].Lyric      := Lines.Line[I].Lyric;
    Result.Sentence[I].End_       := Lines.Line[I].End_;
    Result.Sentence[I].BaseNote   := Lines.Line[I].BaseNote;
    Result.Sentence[I].HighNote   := Lines.Line[I].HighNote;
    Result.Sentence[I].TotalNotes := Lines.Line[I].TotalNotes;

    SetLength(Result.Sentence[I].Note, Length(Lines.Line[I].Note));
    for J := low(Result.Sentence[I].Note) to high(Result.Sentence[I].Note) do
    begin
      Result.Sentence[I].Note[J].Color     := Lines.Line[I].Note[J].Color;
      Result.Sentence[I].Note[J].Start     := Lines.Line[I].Note[J].Start;
      Result.Sentence[I].Note[J].Length    := Lines.Line[I].Note[J].Length;
      Result.Sentence[I].Note[J].Tone      := Lines.Line[I].Note[J].Tone;
      //Result.Sentence[I].Note[J].Text      := Lines.Line[I].Note[J].Text;
      Result.Sentence[I].Note[J].FreeStyle := (Lines.Line[I].Note[J].NoteType = ntFreestyle);
    end;
  end;
end;

procedure TScreenSingModi.OnShow;
var
  I: integer;
begin
  inherited;

  PlayersPlay := TeamInfo.NumTeams;

  if DLLMan.Selected.LoadSong then //Start with Song
  begin
    inherited;
  end
  else //Start Without Song
  begin
    AudioInput.CaptureStart;
  end;

//Set Playerinfo
  PlayerInfo.NumPlayers := PlayersPlay;
  for I := 0 to PlayerInfo.NumPlayers-1 do
  begin
    PlayerInfo.Playerinfo[I].Name    := PChar(Ini.Name[I]);
    PlayerInfo.Playerinfo[I].Score   := 0;
    PlayerInfo.Playerinfo[I].Bar     := 50;
    PlayerInfo.Playerinfo[I].Enabled := true;
  end;

  for I := PlayerInfo.NumPlayers to high(PlayerInfo.Playerinfo) do
  begin
    PlayerInfo.Playerinfo[I].Score:=  0;
    PlayerInfo.Playerinfo[I].Bar :=  0;
    PlayerInfo.Playerinfo[I].Enabled := false;
  end;

  {Case PlayersPlay of
    1: begin
      PlayerInfo.Playerinfo[0].PosX := Static[StaticP1ScoreBG].Texture.X;
      PlayerInfo.Playerinfo[0].PosY := Static[StaticP1ScoreBG].Texture.Y + Static[StaticP1ScoreBG].Texture.H;
    end;
    2,4: begin
      PlayerInfo.Playerinfo[0].PosX := Static[StaticP1TwoPScoreBG].Texture.X;
      PlayerInfo.Playerinfo[0].PosY := Static[StaticP1TwoPScoreBG].Texture.Y + Static[StaticP1TwoPScoreBG].Texture.H;
      PlayerInfo.Playerinfo[2].PosX := Static[StaticP1TwoPScoreBG].Texture.X;
      PlayerInfo.Playerinfo[2].PosY := Static[StaticP1TwoPScoreBG].Texture.Y + Static[StaticP1TwoPScoreBG].Texture.H;
      PlayerInfo.Playerinfo[1].PosX := Static[StaticP2RScoreBG].Texture.X;
      PlayerInfo.Playerinfo[1].PosY := Static[StaticP2RScoreBG].Texture.Y + Static[StaticP2RScoreBG].Texture.H;
      PlayerInfo.Playerinfo[3].PosX := Static[StaticP2RScoreBG].Texture.X;
      PlayerInfo.Playerinfo[3].PosY := Static[StaticP2RScoreBG].Texture.Y + Static[StaticP2RScoreBG].Texture.H;
    end;
    3,6: begin
      PlayerInfo.Playerinfo[0].PosX := Static[StaticP1ThreePScoreBG].Texture.X;
      PlayerInfo.Playerinfo[0].PosY := Static[StaticP1ThreePScoreBG].Texture.Y + Static[StaticP1ThreePScoreBG].Texture.H;
      PlayerInfo.Playerinfo[3].PosX := Static[StaticP1ThreePScoreBG].Texture.X;
      PlayerInfo.Playerinfo[3].PosY := Static[StaticP1ThreePScoreBG].Texture.Y + Static[StaticP1ThreePScoreBG].Texture.H;
      PlayerInfo.Playerinfo[1].PosX := Static[StaticP2MScoreBG].Texture.X;
      PlayerInfo.Playerinfo[1].PosY := Static[StaticP2MScoreBG].Texture.Y + Static[StaticP2MScoreBG].Texture.H;
      PlayerInfo.Playerinfo[4].PosX := Static[StaticP2MScoreBG].Texture.X;
      PlayerInfo.Playerinfo[4].PosY := Static[StaticP2MScoreBG].Texture.Y + Static[StaticP2MScoreBG].Texture.H;
      PlayerInfo.Playerinfo[2].PosX := Static[StaticP3RScoreBG].Texture.X;
      PlayerInfo.Playerinfo[2].PosY := Static[StaticP3RScoreBG].Texture.Y + Static[StaticP3RScoreBG].Texture.H;
      PlayerInfo.Playerinfo[5].PosX := Static[StaticP3RScoreBG].Texture.X;
      PlayerInfo.Playerinfo[5].PosY := Static[StaticP3RScoreBG].Texture.Y + Static[StaticP3RScoreBG].Texture.H;
    end;
  end;       }

  // play music (I)
  //Music.CaptureStart;
  //Music.MoveTo(AktSong.Start);

  //Init Plugin
  if not DLLMan.PluginInit(TeamInfo, PlayerInfo, ToSentences(Lines[0]), LoadTex, Print, LoadSound, PlaySound) then
  begin
    //Fehler
    Log.LogError('Could not Init Plugin');
    Halt;
  end;

  // Set Background (Little Workaround, maybe change sometime)
  if (DLLMan.Selected.LoadBack) and (DLLMan.Selected.LoadSong) then
    ScreenSing.Tex_Background := Tex_Background;

  Winner := 0;

  //Set Score Visibility
  Scores.Visible := DLLMan.Selected.ShowScore;

  {if PlayersPlay = 1 then
  begin
    Text[TextP1Score].Visible := DLLMan.Selected.ShowScore;
    Static[StaticP1ScoreBG].Visible := DLLMan.Selected.ShowScore;
  end;

  if (PlayersPlay = 2) or (PlayersPlay = 4) then
  begin
    Text[TextP1TwoPScore].Visible := DLLMan.Selected.ShowScore;
    Static[StaticP1TwoPScoreBG].Visible := DLLMan.Selected.ShowScore;

    Text[TextP2RScore].Visible := DLLMan.Selected.ShowScore;
    Static[StaticP2RScoreBG].Visible := DLLMan.Selected.ShowScore;
  end;

  if (PlayersPlay = 3) or (PlayersPlay = 6) then
  begin
    Text[TextP1ThreePScore].Visible := DLLMan.Selected.ShowScore;
    Static[StaticP1ThreePScoreBG].Visible := DLLMan.Selected.ShowScore;

    Text[TextP2MScore].Visible := DLLMan.Selected.ShowScore;
    Static[StaticP2MScoreBG].Visible := DLLMan.Selected.ShowScore;

    Text[TextP3RScore].Visible := DLLMan.Selected.ShowScore;
    Static[StaticP3RScoreBG].Visible := DLLMan.Selected.ShowScore;
  end; }
end;

function TScreenSingModi.Draw: boolean;
var
  Min:    integer;
  Sec:    integer;
  TextStr:  string;
  S, I:   integer;
  T:      integer;
  CurLyricsTime: real;
begin
  Result := false;

  //Set Playerinfo
  PlayerInfo.NumPlayers := PlayersPlay;
  for I := 0 to PlayerInfo.NumPlayers-1 do
  begin
    PlayerInfo.Playerinfo[I].Name := PChar(Player[I].Name);
    if PlayerInfo.Playerinfo[I].Enabled then
    begin
      if (Player[I].ScoreTotalInt <= MAX_SONG_SCORE) then
        PlayerInfo.Playerinfo[I].Score:=  Player[I].ScoreTotalInt;
      PlayerInfo.Playerinfo[I].Bar :=  Round(Scores.Players[I].RBPos * 100);
    end;
  end;

  Background.Draw;

  // draw background picture (if any, and if no visualizations)
  // when we don't check for visualizations the visualizations would
  // be overdrawn by the picture when {UNDEFINED UseTexture} in UVisualizer
  if (DllMan.Selected.LoadSong) and (DllMan.Selected.LoadBack) and (not fShowVisualization) then
    SingDrawBackground;

  // set player names (for 2 screens and only Singstar skin)
  if ScreenAct = 1 then
  begin
    Text[TextP1].Text       := 'P1';
    Text[TextP1TwoP].Text   := 'P1'; // added for ps3 skin
    Text[TextP1ThreeP].Text := 'P1'; // added for ps3 skin
    Text[TextP2R].Text      := 'P2';
    Text[TextP2M].Text      := 'P2';
    Text[TextP3R].Text      := 'P3';
  end

  Else if ScreenAct = 2 then
  begin
    case PlayersPlay of
      4:  begin
            Text[TextP1TwoP].Text := 'P3';
            Text[TextP2R].Text := 'P4';
          end;
      6:  begin
            Text[TextP1ThreeP].Text := 'P4';
            Text[TextP2M].Text := 'P5';
            Text[TextP3R].Text := 'P6';
          end;
    end; // case
  end; // if

    // stereo   <- and where iss P2M? or P3?
    Static[StaticP1].Texture.X := Static[StaticP1].Texture.X + 10*ScreenX;
    Text[TextP1].X := Text[TextP1].X + 10*ScreenX;

    {Static[StaticP1ScoreBG].Texture.X := Static[StaticP1ScoreBG].Texture.X + 10*ScreenX;
    Text[TextP1Score].X := Text[TextP1Score].X + 10*ScreenX;}

    Static[StaticP2R].Texture.X := Static[StaticP2R].Texture.X + 10*ScreenX;
    Text[TextP2R].X := Text[TextP2R].X + 10*ScreenX;

  for S := 1 to 1 do
    Static[S].Texture.X := Static[S].Texture.X + 10*ScreenX;

  for T := 0 to 1 do
    Text[T].X := Text[T].X + 10*ScreenX;

  if DLLMan.Selected.LoadSong then
  begin
    // update static menu with time ...
    CurLyricsTime := LyricsState.GetCurrentTime();
    Min := Round(CurLyricsTime) div 60;
    Sec := Round(CurLyricsTime) mod 60;

    Text[TextTimeText].Text := '';
    if Min < 10 then Text[TextTimeText].Text := '0';
    Text[TextTimeText].Text := Text[TextTimeText].Text + IntToStr(Min) + ':';
    if Sec < 10 then Text[TextTimeText].Text := Text[TextTimeText].Text + '0';
    Text[TextTimeText].Text := Text[TextTimeText].Text + IntToStr(Sec);
  end;

  // update and draw movie
{  if ShowFinish and CurrentSong.VideoLoaded and DllMan.Selected.LoadVideo then
  begin
    UpdateSmpeg; // this only draws
  end;}

  // update and draw movie
  if (ShowFinish and (VideoLoaded or fShowVisualization) and DllMan.Selected.LoadVideo) then
  begin
    if assigned(fCurrentVideoPlaybackEngine) then
    begin
      // Just call this once
      // when Screens = 2
      if (ScreenAct = 1) then
        fCurrentVideoPlaybackEngine.GetFrame(CurrentSong.VideoGAP + LyricsState.GetCurrentTime());

      fCurrentVideoPlaybackEngine.DrawGL(ScreenAct);
    end;
  end;

  // draw static menu (FG)
  DrawFG;

  if ShowFinish then
  begin
    if DllMan.Selected.LoadSong then
    begin
      if (not AudioPlayback.Finished) and ((CurrentSong.Finish = 0) or (LyricsState.GetCurrentTime*1000 <= CurrentSong.Finish)) then
      begin
        //Pause Mod:
        if not Paused then
          Sing(Self);       // analyze song
      end
      else
      begin
        if not FadeOut then
        begin
          Finish;
          FadeOut := true;
          FadeTo(@ScreenPartyScore);
        end;
      end;
    end;
  end;

  // draw custom items
  SingModiDraw(PlayerInfo);  // always draw

  //GoldenNoteStarsTwinkle Mod
    GoldenRec.SpawnRec;
  //GoldenNoteStarsTwinkle Mod

  //Draw Score
  Scores.Draw;

  //Update PlayerInfo
  for I := 0 to PlayerInfo.NumPlayers-1 do
  begin
    if PlayerInfo.Playerinfo[I].Enabled then
    begin
      //PlayerInfo.Playerinfo[I].Bar :=  Player[I].ScorePercent;
      PlayerInfo.Playerinfo[I].Score := Player[I].ScoreTotalInt;
    end;
  end;

  if ((ShowFinish) and (not Paused)) then
  begin
    if not DLLMan.PluginDraw(Playerinfo, Lines[0].Current) then
    begin
      if not FadeOut then
      begin
        Finish;
        FadeOut := true;
        FadeTo(@ScreenPartyScore);
      end;
    end;   
  end;

  //Change PlayerInfo/Changeables
  for I := 0 to PlayerInfo.NumPlayers-1 do
  begin
    if (Player[I].ScoreTotalInt <> PlayerInfo.Playerinfo[I].Score) then
    begin
      //Player[I].ScoreTotal   := Player[I].ScoreTotal + (PlayerInfo.Playerinfo[I].Score - Player[I].ScoreTotalI);
      Player[I].ScoreTotalInt := PlayerInfo.Playerinfo[I].Score;
    end;
    {if (PlayerInfo.Playerinfo[I].Bar <> Player[I].ScorePercent) then
      Player[I].ScorePercentTarget := PlayerInfo.Playerinfo[I].Bar; }
  end;

  // back stereo
  Static[StaticP1].Texture.X := Static[StaticP1].Texture.X - 10*ScreenX;
  Text[TextP1].X := Text[TextP1].X - 10*ScreenX;

  {Static[StaticP1ScoreBG].Texture.X := Static[StaticP1ScoreBG].Texture.X - 10*ScreenX;
  Text[TextP1Score].X := Text[TextP1Score].X - 10*ScreenX;}

  Static[StaticP2R].Texture.X := Static[StaticP2R].Texture.X - 10*ScreenX;
  Text[TextP2R].X := Text[TextP2R].X - 10*ScreenX;

  {Static[StaticP2RScoreBG].Texture.X := Static[StaticP2RScoreBG].Texture.X - 10*ScreenX;
  Text[TextP2RScore].X := Text[TextP2RScore].X - 10*ScreenX;}

  for S := 1 to 1 do
    Static[S].Texture.X := Static[S].Texture.X - 10*ScreenX;

  for T := 0 to 1 do
    Text[T].X := Text[T].X - 10*ScreenX;

  Result := true;
end;

procedure TScreenSingModi.Finish;
begin
inherited Finish;

Winner := DllMan.PluginFinish(PlayerInfo);

//Log.LogError('Winner: ' + InttoStr(Winner));

//DLLMan.UnLoadPlugin;
end;

function LoadTex(const Name: PChar; Typ: TTextureType): TsmallTexture;
var
  TexName: IPath;
  Ext: UTF8String;
  Tex: TTexture;
begin
  //Get texture Name
  TexName := Skin.GetTextureFileName(string(Name));
  //Get File Typ
  Ext := TexName.GetExtension().ToUTF8;
  if (UpperCase(Ext) = '.JPG') then
    Ext := 'JPG'
  else
    Ext := 'BMP';

  Tex := Texture.LoadTexture(false, TexName, UTexture.TTextureType(Typ), 0);

  Result.TexNum := Tex.TexNum;
  Result.W := Tex.W;
  Result.H := Tex.H;
end;
{
function Translate (const Name: PChar): PChar; stdcall;
begin
  Result := PChar(Language.Translate(string(Name)));
end; }

//Procedure to Print Text
procedure Print(const Style, Size: byte; const X, Y: real; const Text: PChar);
begin
  SetFontItalic ((Style and 128) = 128);
  SetFontStyle(Style and 7);
  // FIXME: FONTSIZE
  // used by Hold_The_Line / TeamDuell
  SetFontSize(Size);
  SetFontPos (X, Y);
  glPrint (Language.Translate(string(Text)));
end;

//Procedure that loads a Custom Sound
function LoadSound(const Name: PChar): cardinal;
var
  Stream: TAudioPlaybackStream;
  i: integer;
  Filename: IPath;
  SoundFile: IPath;
begin
  //Search for Sound in already loaded Sounds
  SoundFile := SoundPath.Append(Name);
  for i := 0 to High(CustomSounds) do
  begin
    if (SoundFile.Equals(CustomSounds[i].Filename, true)) then
    begin
      Result := i;
      Exit;
    end;
  end;

  Stream := AudioPlayback.OpenSound(SoundFile);
  if (Stream = nil) then
  begin
    Result := 0;
    Exit;
  end;

  SetLength(CustomSounds, Length(CustomSounds)+1);
  CustomSounds[High(CustomSounds)].Stream := Stream;
  Result := High(CustomSounds);
end;

//Plays a Custom Sound
procedure PlaySound(const Index: cardinal);
begin
  if (Index <= High(CustomSounds)) then
    AudioPlayback.PlaySound(CustomSounds[Index].Stream);
end;

end.