#include "global.h"
#include "LightsManager.h"
#include "GameState.h"
#include "RageTimer.h"
#include "arch/Lights/LightsDriver.h"
#include "RageUtil.h"
#include "GameInput.h"	// for GameController
#include "InputMapper.h"
#include "Game.h"
#include "PrefsManager.h"

#include "TimingData.h"
#include "GameState.h"
#include "song.h"


static const CString CabinetLightNames[NUM_CABINET_LIGHTS] = {
	"MarqueeUpLeft",
	"MarqueeUpRight",
	"MarqueeLrLeft",
	"MarqueeLrRight",
	"ButtonsLeft",
	"ButtonsRight",
	"BassLeft",
	"BassRight",
};
XToString( CabinetLight );

static const CString LightsModeNames[NUM_LIGHTS_MODES] = {
	"Blank",
	"Joining",
	"Menu",
	"Frozen",
	"Gameplay",
	"Cleared",
	"Testing",
};
XToString( LightsMode );


LightsManager*	LIGHTSMAN = NULL;	// global and accessable from anywhere in our program

LightsManager::LightsManager(CString sDriver)
{
	m_LightsMode = LIGHTSMODE_JOINING;

	// 1/4 of a beat, or a 16th note's length
	m_fLightsBeatFraction = 0.25;

	m_pDriver = MakeLightsDriver(sDriver);

	ZERO( m_fSecsLeftInCabinetLightBlink );
	ZERO( m_fSecsLeftInGameButtonBlink );
}

LightsManager::~LightsManager()
{
	SAFE_DELETE( m_pDriver );
}

void LightsManager::Update( float fDeltaTime )
{

	// if we're frozen, do nothing at all.
	if( m_LightsMode == LIGHTSMODE_FREEZE )
		return;

	// update lights falloff
	{
		FOREACH_CabinetLight( cl )
			fapproach( m_fSecsLeftInCabinetLightBlink[cl], 0, fDeltaTime );
		FOREACH_GameController( gc )
			FOREACH_GameButton( gb )
				fapproach( m_fSecsLeftInGameButtonBlink[gc][gb], 0, fDeltaTime );
	}

	//
	// Set new lights state cabinet lights
	//
	{
		ZERO( m_LightsState.m_bCabinetLights );
		ZERO( m_LightsState.m_bGameButtonLights );
	}

	switch( m_LightsMode )
	{
	case LIGHTSMODE_BLANK:
	case LIGHTSMODE_JOINING: // nothing interesting here, it just lights the game pads
		break;
	case LIGHTSMODE_MENU:
		{
			FOREACH_CabinetLight( cl )
				m_LightsState.m_bCabinetLights[cl] = false;

			static int iLight;

			if( lightsTimer.Ago() > 0.2727272 )
			{
				++iLight;
				wrap ( iLight, 4 );
				lightsTimer.Touch(); // reset
			}


			switch( iLight )
			{
			case 1:	m_LightsState.m_bCabinetLights[LIGHT_MARQUEE_UP_LEFT]  = true;	break;
			case 2:	m_LightsState.m_bCabinetLights[LIGHT_MARQUEE_UP_RIGHT] = true;	break;
			case 3:	m_LightsState.m_bCabinetLights[LIGHT_MARQUEE_LR_LEFT]  = true;	break;
			case 0:	m_LightsState.m_bCabinetLights[LIGHT_MARQUEE_LR_RIGHT] = true;	break;
			default:	ASSERT(0);
			}

			// buttons are on on if either of the lower two marquee lights are
			
			FOREACH_PlayerNumber( pn )
				if( GAMESTATE->m_bSideIsJoined[pn] )
					m_LightsState.m_bCabinetLights[LIGHT_BUTTONS_LEFT+pn] = 
						m_LightsState.m_bCabinetLights[LIGHT_MARQUEE_LR_LEFT] ||
						m_LightsState.m_bCabinetLights[LIGHT_MARQUEE_LR_RIGHT];
		}
		break;
	case LIGHTSMODE_GAMEPLAY:
		// cabinet button blinking is intercepted at BlinkCabinetLight().
		FOREACH_CabinetLight( cl )
			m_LightsState.m_bCabinetLights[cl] = m_fSecsLeftInCabinetLightBlink[cl] > 0;
		break;
	case LIGHTSMODE_ALL_CLEARED:
		{
			FOREACH_CabinetLight( cl )
				m_LightsState.m_bCabinetLights[cl] = true;
		}
		break;
	case LIGHTSMODE_TEST:
		{
			int iSec = (int)RageTimer::GetTimeSinceStart();
			FOREACH_CabinetLight( cl )
			{
				bool bOn = (iSec%NUM_CABINET_LIGHTS) == cl;
				m_LightsState.m_bCabinetLights[cl] = bOn;
			}
		}
		break;
	default:
		ASSERT(0);
	}

	// on for the first half of the timer. (30 / 220)
	bool bBlinkOn = (lightsTimer.Ago() < 0.1363636);

	// blink menu buttons on every beat if we're not joined yet and still have a chance
	FOREACH_PlayerNumber( pn )
		if( !GAMESTATE->m_bSideIsJoined[pn] && GAMESTATE->PlayersCanJoin() && GAMESTATE->EnoughCreditsToJoin() )
			m_LightsState.m_bCabinetLights[LIGHT_BUTTONS_LEFT+pn] = bBlinkOn;

	//
	// Update game controller lights
	//
	// FIXME:  Works only with Game==dance
	// FIXME:  lights pads for players who aren't playing
	switch( m_LightsMode )
	{
	case LIGHTSMODE_BLANK:
	case LIGHTSMODE_ALL_CLEARED:
		break;
	case LIGHTSMODE_JOINING:
		{
			FOREACH_GameController( gc )
			{
				bool bOn = GAMESTATE->m_bSideIsJoined[gc];

				FOREACH_GameButton( gb )
					m_LightsState.m_bGameButtonLights[gc][gb] = bOn;
			}
		}
		break;
	case LIGHTSMODE_MENU:
	case LIGHTSMODE_GAMEPLAY:
		{
			if( m_LightsMode == LIGHTSMODE_GAMEPLAY  &&  PREFSMAN->m_bBlinkGameplayButtonLightsOnNote )
			{
				//
				// Blink on notes.
				//
				FOREACH_GameController( gc )
					FOREACH_GameButton( gb )
						m_LightsState.m_bGameButtonLights[gc][gb] = m_fSecsLeftInGameButtonBlink[gc][gb] > 0 ;
			}
			else
			{
				//
				// Blink on button pressess.
				//
				FOREACH_GameController( gc )
				{
					// don't blink unjoined sides
					if( !GAMESTATE->m_bSideIsJoined[gc] )
						continue;

					FOREACH_GameButton( gb )
					{
						bool bOn = INPUTMAPPER->IsButtonDown( GameInput(gc,gb) );
						m_LightsState.m_bGameButtonLights[gc][gb] = bOn;
					}
				}
			}
		}
		break;
	case LIGHTSMODE_TEST:
		{
			int iSec = (int)RageTimer::GetTimeSinceStart();

			int iNumGameButtonsToShow = GAMESTATE->GetCurrentGame()->GetNumGameplayButtons();

			FOREACH_GameController( gc )
			{
				FOREACH_GameButton( gb )
				{
					bool bOn = gb==(iSec%iNumGameButtonsToShow);
					m_LightsState.m_bGameButtonLights[gc][gb] = bOn;
				}
			}
		}
		break;
	default:
		ASSERT(0);
	}

	// apply new light values we set above
	m_pDriver->Set( &m_LightsState );
}

void LightsManager::BlinkCabinetLight( CabinetLight cl )
{
	// we always handle the buttons ourselves. don't allow blinks.
	if( cl == LIGHT_BUTTONS_LEFT || cl == LIGHT_BUTTONS_RIGHT )
		return;

	float m_fLightBPM = GAMESTATE->m_pCurSong->GetBPMAtBeat( GAMESTATE->m_fSongBeat + 0.25 );
	ASSERT( m_fLightBPM != 0 );
	// skew it so we smoothly transition if a string of 16ths comes up
	m_fSecsLeftInCabinetLightBlink[cl] = (( m_fLightsBeatFraction * 60.5 ) / m_fLightBPM);
	
}

void LightsManager::BlinkGameButton( GameInput gi )
{
	// same formula as before.
	float m_fLightBPM = GAMESTATE->m_pCurSong->GetBPMAtBeat( GAMESTATE->m_fSongBeat + 0.25 );
	ASSERT( m_fLightBPM != 0 );

	m_fSecsLeftInGameButtonBlink[gi.controller][gi.button] =  (( m_fLightsBeatFraction * 60.5 ) / m_fLightBPM );
}

void LightsManager::BlinkHereWeGo( float fTime )
{
	// once I track down the formula, we can pass an fTime.
	// until then, default is 1.25 seconds.

	// values 0 to 3, which are the marquee lights
	FOREACH_CabinetLight( cl )
		if( cl <= LIGHT_MARQUEE_LR_RIGHT )
                        m_fSecsLeftInCabinetLightBlink[cl] = fTime;
}

void LightsManager::SetLightsMode( LightsMode lm )
{
	m_LightsMode = lm;
}

LightsMode LightsManager::GetLightsMode()
{
	return m_LightsMode;
}

/*
 * (c) 2003-2004 Chris Danford
 * All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, and/or sell copies of the Software, and to permit persons to
 * whom the Software is furnished to do so, provided that the above
 * copyright notice(s) and this permission notice appear in all copies of
 * the Software and that both the above copyright notice(s) and this
 * permission notice appear in supporting documentation.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
 * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
 * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
 * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */
