require "StandardMouseButton"
require "StandardRadioButton"
require "CBuildObject"
require "CRollHoldChoice"
require "CIrpMan"

require "CTraceEffectMovie"

-- reserve constructor
if ( CGameSlotHold == nil ) then
	CGameSlotHold = 
	{
	}
end

if ( IsRetail() == false ) then
	function CGameSlotHold:OnKeyDown( i_iChar )
		log( "CHAR:" .. i_iChar )
		if ( i_iChar == 191 ) then		-- '/'
			log( "RELOAD LUA!" )
			--console( "library.reloadpackage refinish" )
			console( "lua.dofile CGameSlotHold.lua" )
		elseif ( i_iChar == 188 ) then	-- ','
			log( "RELOAD ART!" )
			console( "library.reloadpackage refinish" )
		end
	end
end	
 
function CGameSlotHold:Create( )

    -- inherit all functions from Movie
    local oRet = root.RefinishGame
    oRet.complete = false
    
	CIrpMan:Attach( oRet )
	
	log( "CREATE!" )
	
	-- bind into our class
	CGameSlotHold:Bind( oRet )
end

function CGameSlotHold:PlayLogoEffect( i_bDual )
	if ( self.m_oTraceEffectMovie ~= nil ) then
		self.m_oTraceEffectMovie:Kill()
	end
	 
	self.m_oTraceEffectMovie = CTraceEffectMovie:Create( self, 
														"TraceHighlight", 
														"RefinishTitlePartEffect", 
														1.0, 
														CIrpFuncs.mLinear )
	self.m_oTraceEffectMovie:SetX( self._logo:GetX() )
	self.m_oTraceEffectMovie:SetY( self._logo:GetY() )
	
	if ( i_bDual ~= nil
			and i_bDual ) then
		if ( self.m_oTraceEffectMovie2 ~= nil ) then
			self.m_oTraceEffectMovie2:Kill()
		end

		self.m_oTraceEffectMovie2 = CTraceEffectMovie:Create( self, 
									"TraceHighlight", 
									"RefinishTitlePartEffect", 
									1.0, 
									CIrpFuncs.mReverseLinear )

		self.m_oTraceEffectMovie2:SetX( self._logo:GetX() )
		self.m_oTraceEffectMovie2:SetY( self._logo:GetY() )
	end
end

function CGameSlotHold:Activate()

	-- make sure we get updates
	self:SetOnUpdateEnabled( true );
	
    self.complete = false

	function self._logo:OnLeftMouseDown()
		self:GetParent():PlayLogoEffect( false )
	end

	
	-- attach to the refinishing object
	self.buildObject = CBuildObject:Create( self )
	
	self.m_oMeter = CControlMeter:Attach( self.meter )
	
	function self.m_oMeter:OnChange( i_fVal )
		local iRating = 0

		for i = 0, 2, 1 do
			if ( i_fVal > self:GetParent():GetRatingMin( i ) ) then
				iRating = i 
			end
		end


		self:GetParent()._meterFace:SetPlaying( false )
		self:GetParent()._meterFace:SetFrameFromLabel( "Rating" .. tostring( iRating + 1 ) )
	end
	
	self.m_iTurnOnIndex = 1
	
	-- attach to the choice wheel objects
	self.choices = {}
	table.insert( self.choices, CRollHoldChoice:Create( self, self.roll1, 1, 0 ) )
	table.insert( self.choices, CRollHoldChoice:Create( self, self.roll2, 2, 0 ) )
	table.insert( self.choices, CRollHoldChoice:Create( self, self.roll3, 3, 0 ) )

	
	-- set up the buttons
	StandardMouseButton( self.playButton )
	self.playButton.parent = self
	function self.playButton:Pressed()
		log("playbutton pressed")
		if ( self.parent:IsRolling() == false ) then
			PlaySound( "FRU_replay" )
			self.parent:Reset()
		end
	end
	
	StandardMouseButton( self.keepButton )
	self.keepButton.parent = self
	function self.keepButton:Pressed()
		if ( self.parent:IsRolling() == false
				and ( self.parent.playing == true 
					or self.parent.complete ) ) then
			PlaySound( "FRU_keepChoices" )
			self.parent:EndGame()
		end
	end
	
	-- set up the hold buttons
	for i=1,3,1 do
		local holdButton = self[ "hold" .. i ]
		
		StandardRadioButton( holdButton )
		holdButton.parent = self
		-- override this one function to make the buttons act more like checkboxes than radiobuttons
		function holdButton:OnLeftMouseUp( )
			-- only respond to clicks while not rolling
			if ( self.parent.rolls < 3
					and self.parent:IsRolling() == false
					and self.parent.playing == true )  then
				PlaySound( "FRU_reelLock" )
				
				self.Checked = not self.Checked
				
				if( self.Checked ) then	
					self:SetFrame( mouseOverChecked )
				else
					self:SetFrame( mouseOverUnChecked )
				end
				
				self.parent:ReactToHoldState()
			end
			
			if ( self.Checked ) then
				self:GetParent()[ "_lock" .. i ]:SetFrame( 1 )
			else
				self:GetParent()[ "_lock" .. i ]:SetFrame( 0 )
			end
			
		end
	end
	
	-- set up the roll button
	StandardMouseButton( self.rollButton )
	self.rollButton.parent = self
	function self.rollButton:Pressed()
		if ( self.parent:IsRolling() == false ) then
			PlaySound( "FRU_reelSpin" )
			self.parent:Roll()
		end
	end
	
	return self
end


function CGameSlotHold:ReactToHoldState()
	if ( (self.hold1.Checked == true) and (self.hold2.Checked == true) and (self.hold3.Checked == true) ) then
		self.rollButton:SetVisible( false );
		self.rollButton:SetInputEnabled( false );
		self.keepButton:SetVisible( true );
		self.keepButton:SetInputEnabled( true );
		self.playButton:SetVisible( true );
		self.playButton:SetInputEnabled( true );
	else
		self.rollButton:SetVisible( true );
		self.rollButton:SetInputEnabled( true );
		self.keepButton:SetVisible( false );
		self.keepButton:SetInputEnabled( false );
		self.playButton:SetVisible( false );
		self.playButton:SetInputEnabled( false );
	end
end

function CGameSlotHold:IsRolling()
	local rolling = false
	
	for i=1,3,1 do
		if ( self.choices[i].rolling == true ) then
			rolling = true
		end
	end
		
	return rolling
end

-----------------------------------------------------------------------------------
-- public functions
-----------------------------------------------------------------------------------

function CGameSlotHold:Init( )

	-- allow key events
	--self:AddAsGlobalHandler()
	
	self.counter = 0
	
	self._logo:SetFrame( self._logo:GetNumFrames() - 1 )
	self._meterFace:SetFrame( self._meterFace:GetNumFrames() - 1 )
	
    self.complete = false
	self.playing = true
	
	-- attach to the refinish object
	log ("obj base: " .. self:GetObjectBase() )
	self.buildObject:SetBase( self:GetObjectBase() )
	
	-- attach to the choice objects and load in any saved choices if they exist
	local base = self:GetChoiceBase()
	log ("choice base: " .. self:GetChoiceBase() )
	for c in pairs( self.choices ) do
		self.choices[c]:SetBase( base );
		self.choices[c]:SetPick( self["m_Pick"..c] );

		-- if we are loading in a previous save, make sure to pass this pick to the refinish object
		if ( self["m_Pick"..c] ~= -1 ) then
			self.buildObject:SetPoint( c, self["m_Pick"..c]+1 )

			self.choices[c]:SetY( self.choices[c].stopY )
		else
			self.buildObject:SetPoint( c, 0 )

			-- init off center
			self.choices[c]:SetY( self.choices[c].stopY - self.choices[c].height )
		end
	end
	
	-- initialize the hold button states
	for i=1,3,1 do
		local holdButton = self[ "hold" .. i ]
		holdButton:SetFrame( 0 )
		self[ "_lock" .. i ]:SetFrame( 0 )
		holdButton.Checked = false;
	end
end

function CGameSlotHold:Reset()
	-- prepare to play again
	for c in pairs( self.choices ) do
		self["m_Pick"..c] = -1;
		self.choices[c]:SetPick( self["m_Pick"..c] );
		
		self.buildObject:SetPoint( c, 0 )
	end
		
	-- initialize the hold button states
	for i=1,3,1 do
		local holdButton = self[ "hold" .. i ]
		holdButton:SetFrame( 0 )
		self[ "_lock" .. i ]:SetFrame( 0 )
		holdButton.Checked = false;
	end
	
	-- reset the number of rolls available
	self.rolls = 3
	
	-- initiate play
    self.complete = false
	self.playing = true
end

function CGameSlotHold:RollResult( index, result )
	self["m_Pick"..index] = result;
	self.buildObject:SetPoint( index, result+1 )
	
	if ( index == 3 ) then
		-- update the rating
		self:UpdateRating()
	end
	
	if ( self.rolls <= 0 ) then 
	    self.complete = true
	end
end

function CGameSlotHold:UpdateRating()
	-- assume all are rolling at first
	local aVals = { -1, -1, - 1 }
	
	for i = 1, 3, 1 do
		local holdButton = self[ "hold" .. i ]
		if ( self.choices[ i ].rolling == false
				or holdButton.Checked == true ) then
			aVals[ i ] = self[ "m_Pick" .. i ]
		end
	end
	
	local fRating = self:RateChoice( aVals[ 1 ], aVals[ 2 ], aVals[ 3 ] )

	if ( fRating > self:GetRatingMin( 2 ) ) then
		self:PlayLogoEffect( true )
	else
		self:PlayLogoEffect( false )
	end

	-- try to get a value that makes the needle move a bit	
	for i = 1, 20, 1 do
		if ( math.abs( self.m_oMeter:GetVal() - fRating ) < 0.1 ) then
			fRating = self:RateChoice( aVals[ 1 ], aVals[ 2 ], aVals[ 3 ] )
		end
	end
	
	log( "RATING (" .. aVals[ 1 ] .. "," .. aVals[ 2 ] .. "," .. aVals[ 3 ] .. ") :" .. fRating )
	
	self:SetMeterVal( fRating )
end

function CGameSlotHold:SetMeterVal( i_fVal )
	-- give buffer for min/max extents
	local fExtentBuffer = 0.1
	
	local fBoundVal = fExtentBuffer + i_fVal * ( 1.0 - fExtentBuffer * 2 )

	local fDelta = math.abs( fBoundVal - self.m_oMeter:GetUiPos() )
	log( i_fVal .. ".." .. fBoundVal )
	if ( fDelta < 0.1 
			or fDelta > 0.7 ) then
		self.m_oMeter:SetVal( fBoundVal, true, CIrpFuncs.mEaseOutQuad )
	else
		self.m_oMeter:SetVal( fBoundVal, true, CIrpFuncs.mElasticEaseOut, 5 )
	end
end

function CGameSlotHold:OnLeftMouseDown()
	--self:SetMeterVal( math.random( 0, 100 ) / 100, true )
end


function CGameSlotHold:Update()
	self:UpdateIrps( 1 / 60 )
end

function CGameSlotHold:Roll()
	
	if ( (self.playing == false) or (self:IsRolling() == true) ) then
		return
	end
	
	if ( self._logo:GetFrame() ~= self._logo:GetNumFrames() - 1 ) then
		--self._logo:SetPlaying( true )
		--self._meterFace:SetPlaying( true )
	end

	-- execute the rolls
	for i=1,3,1 do
	
		-- roll the choice if not held
		local holdButton = self[ "hold" .. i ]
		if ( holdButton.Checked == false ) then
			self.choices[i]:Roll( i-1 )
		else
			self.choices[i]:FakeRoll( i-1 )
		end
	end
	
	-- decrement the number of rolls
	self.rolls = self.rolls - 1
	
	if ( self.rolls <= 0 ) then 
		self.playing = false
	end
	
end

function CGameSlotHold:EndGame()
	if ( self.playing 
			or self.complete ) then
		-- stop the game
		self.playing = false
		self.complete = false
		
		-- tell the C class that we are done
		self:GameComplete()
	end
end

-- call this in C++ after a reload to rebind functions
function CGameSlotHold:Bind( i_oObj )
	BindObj( CGameSlotHold, i_oObj )
	return i_oObj
end



-------------------------------------------





-----------------------------------------------------------------------------------
-- CControlMeter
-----------------------------------------------------------------------------------



-- reserve constructor
CControlMeter = 
{

}

-- val should be float between min and max (defaults from 0.0 to 1.0)
function CControlMeter:Attach( i_oMovie, iOpt_fVal, iOpt_fMin, iOpt_fMax )
	
	oRet = i_oMovie
	
	CIrpMan:Attach( oRet )

	-- bind into our class
	CControlMeter:Bind( oRet )
	
	-- initialization
	oRet:Init( iOpt_fVal, iOpt_fMin, iOpt_fMax )
	
	return oRet
end

-----------------------------------------------------------------------------------
-- public functions
-----------------------------------------------------------------------------------

function CControlMeter:Init( iOpt_fVal, iOpt_fMin, iOpt_fMax, iOpt_bUseColor )

	self.m_fVal = 0.0
	self.m_fMin = 0.0
	self.m_fMax = 1.0
	
	if ( iOpt_bUseColor ~= nil ) then
		self.m_bUseColor = iOpt_bUseColor
	else
		self.m_bUseColor = false
	end		

	self.m_oAnimIrp = nil
	self.m_fAnimSpeed = 0.02
	self.m_mAnimFunc = CIrpFuncs.mEaseOutQuad
	
	
	if ( self._top ~= nil and self._mask ~= nil ) then
		self._mask:ToggleMaskBase( true )
		self._top:ToggleMaskIn( true )
		self._top:ToggleMaskClear( true )
	end

	if ( iOpt_fVal ~= nil ) then
		self.m_fVal = iOpt_fVal
	end

	if ( iOpt_fMin ~= nil ) then
		self.m_fMin = iOpt_fMin
	end

	if ( iOpt_fMax ~= nil ) then
		self.m_fMax = iOpt_fMax
	end

	self:SetVal( self.m_fVal )
	
	self:SetOnUpdateEnabled( true )
end

function CControlMeter:FrameToVal( i_iFrame )
	local fPct = math.max( 0, math.min( 1, ( self.m_fVal - self.m_fMin ) / ( self.m_fMax - self.m_fMin ) ) )
	return self.m_fMin + ( i_iFrame / self:GetNumFrames() ) * ( self.m_fMax - self.m_fMin )
end

function CControlMeter:OnUpdate()
	self:UpdateIrps( 1 / 60 )
	
	if ( self:GetIrpCount() == 0 
			and self.m_fVal >= 0.9
			and	self.m_oAnimIrp == nil ) then
			
		local fWiggleVal = math.random( 900, 1000 ) / 1000
			
		-- do wiggle effect
		self.m_oAnimIrp = self:CreateIrp( self:GetFrame(), iTargetFrame, math.abs( self:GetFrame() - iTargetFrame ) * self.m_fAnimSpeed, CIrpFuncs.mLinear )
		self.m_oAnimIrp.m_oHookContext = self

		local fPct = math.max( 0, math.min( 1, ( fWiggleVal - self.m_fMin ) / ( self.m_fMax - self.m_fMin ) ) )

		local iTargetFrame = math.floor( fPct * ( self:GetNumFrames() - 1 ) )
		
		self.m_oAnimIrp.mOnChange = function( i_oIrp, i_oContext )
			i_oContext:SetFrame( i_oIrp:GetVal() )
			i_oContext:OnChange( i_oContext:FrameToVal( i_oIrp:GetVal() ) )
		end
		
		self.m_oAnimIrp.mOnComplete = function( i_oIrp, i_oContext )
			if ( i_oContext.m_oAnimIrp == i_oIrp ) then
				i_oContext.m_oAnimIrp = nil
			end
		end
	end
end

function CControlMeter:OnChange( i_fNewVal )
	-- override me
end

function CControlMeter:GetUiPos()
	return self.m_fMin + ( self:GetFrame() / self:GetNumFrames() * ( self.m_fMax - self.m_fMin ) )
end

function CControlMeter:SetMax( i_fMax )
	self.m_fMax = i_fMax
	
	-- refresh
	self:SetVal( self.m_fVal )
end

function CControlMeter:GetVal()
	return self.m_fVal
end

function CControlMeter:SetMin( i_fMin )
	self.m_fMin = i_fMin
	
	-- refresh
	self:SetVal( self.m_fVal )
end

function CControlMeter:SetVal( i_fVal, iOpt_bPlayAnim, iOpt_mAnimFunc, iOpt_fSpeedMod )

	self.m_fVal = i_fVal
	local fPct = math.max( 0, math.min( 1, ( self.m_fVal - self.m_fMin ) / ( self.m_fMax - self.m_fMin ) ) )

	if ( self.m_oAnimIrp ~= nil ) then
		self:KillIrp( self.m_oAnimIrp )
	end
	
	if ( iOpt_mAnimFunc == nil ) then
		iOpt_mAnimFunc = self.m_mAnimFunc
	end
	
	if ( iOpt_fSpeedMod == nil ) then
		iOpt_fSpeedMod = 1.0
	end
	
	local iTargetFrame = math.floor( fPct * ( self:GetNumFrames() - 1 ) )

	if ( iOpt_bPlayAnim ) then
		self.m_oAnimIrp = self:CreateIrp( self:GetFrame(), 
											iTargetFrame, 
											math.abs( self:GetFrame() - iTargetFrame ) * self.m_fAnimSpeed * iOpt_fSpeedMod, 
											iOpt_mAnimFunc )
		self.m_oAnimIrp.m_oHookContext = self

		self.m_oAnimIrp.mOnChange = function( i_oIrp, i_oContext )
			i_oContext:SetFrame( math.max( 0, math.min( i_oIrp:GetVal(), i_oContext:GetNumFrames() - 1 ) ) )
			i_oContext:OnChange( i_oContext:FrameToVal( i_oIrp:GetVal() ) )
		end
	else
		self:SetFrame( iTargetFrame )
	end
	
	-- $ jbilas -- color is just a hack, this should be done in art!
	if ( self.m_bUseColor ) then
		self:SetColor( 0xff00 )
	end
end

-- call this in C++ after a reload to rebind functions
function CControlMeter:Bind( i_oObj )
	BindObj( CControlMeter, i_oObj )
	return i_oObj
end



















CGameSlotHold:Create( )
