//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//

#include "cbase.h"
#include "item_ad_panel.h"
#include "econ_item_system.h"
#include "item_model_panel.h"
#include "econ_store.h"
#include "econ_ui.h"
#include "store/store_panel.h"
#include "tf_controls.h"
#include "econ_item_description.h"
#include "vgui/IInput.h"

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CBaseAdPanel::CBaseAdPanel( Panel *parent, const char *panelName )
	: BaseClass( parent, panelName )
{}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CBaseAdPanel::ApplySettings( KeyValues *inResourceData )
{
	BaseClass::ApplySettings( inResourceData );

	m_flPresentTime = inResourceData->GetFloat( "present_time", 10.f );
}

bool CBaseAdPanel::CheckForRequiredSteamComponents( const char* pszSteamRequried, const char* pszOverlayRequired )
{
	// Make sure we've got the appropriate connections to Steam
	if ( !steamapicontext || !steamapicontext->SteamUtils() )
	{
		OpenStoreStatusDialog( NULL, pszSteamRequried, true, false );
		return false;
	}

	if ( !steamapicontext->SteamUtils()->IsOverlayEnabled() )
	{
		OpenStoreStatusDialog( NULL, pszOverlayRequired, true, false );
		return false;
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CItemAdPanel::CItemAdPanel( Panel *parent, const char *panelName, item_definition_index_t itemDefIndex )
	: BaseClass( parent, panelName )
	, m_ItemDefIndex( itemDefIndex )
	, m_bShowMarketButton( true )
{
	SetDialogVariable( "price", "..." );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CItemAdPanel::ApplySchemeSettings( IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	LoadControlSettings( GetItemDef()->GetAdResFile() );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CItemAdPanel::ApplySettings( KeyValues *inResourceData )
{
	BaseClass::ApplySettings( inResourceData );

	m_bShowMarketButton = inResourceData->GetBool( "show_market", true ); // Default to showing market

	if ( !m_bShowMarketButton )
	{
		// Tick every second as we try to get our price from the store
		vgui::ivgui()->AddTickSignal( GetVPanel(), 1000 );
	}

	const CTFItemDefinition* pItemDef = GetItemDef();
	CItemModelPanel* pItemImage = FindControl< CItemModelPanel >( "ItemIcon" );
	if ( pItemImage )
	{
		CEconItemView adItem;
		adItem.Init( pItemDef->GetDefinitionIndex(), AE_UNIQUE, 1, 1 );
		pItemImage->InvalidateLayout( true, true );
		pItemImage->SetItem( &adItem );

		KeyValuesAD modelpanelKV( "modelpanel_kv" );
		KeyValues *itemKV = new KeyValues( "itemmodelpanel" );
		itemKV->SetBool( "inventory_image_type", true );
		itemKV->SetBool( "use_item_rendertarget", false );
		itemKV->SetBool( "allow_rot", false );

		modelpanelKV->AddSubKey( itemKV );
		pItemImage->ApplySettings( modelpanelKV );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CItemAdPanel::PerformLayout()
{
	BaseClass::PerformLayout();

	const CTFItemDefinition* pItemDef = GetItemDef();

	// Get the ad text for the item.  If it's not there, juse use the description text.
	SetDialogVariable( "item_name", g_pVGuiLocalize->Find( pItemDef->GetItemBaseName() ) );
	const char* pszAdtext = pItemDef->GetAdTextToken() ? pItemDef->GetAdTextToken() : pItemDef->GetItemDesc();
	CExScrollingEditablePanel* pScrollableItemText = FindControl< CExScrollingEditablePanel >( "ScrollableItemText", true );
	if ( pszAdtext && pScrollableItemText )
	{
		pScrollableItemText->SetDialogVariable( "item_ad_text", g_pVGuiLocalize->Find( pszAdtext ) );

		Label* pAdLabel = pScrollableItemText->FindControl< Label >( "ItemAdText", true );
		if ( pAdLabel )
		{
			int nWide, nTall;
			pAdLabel->GetContentSize( nWide, nTall );
			pAdLabel->SetTall( nTall );
		}

		pScrollableItemText->InvalidateLayout( true );
	}

	CExButton* pBuyButton = FindControl< CExButton >( "BuyButton", true );
	CExButton* pMarketButton = FindControl< CExButton >( "MarketButton", true );
	if ( pBuyButton && pMarketButton )
	{
		
		pBuyButton->SetVisible( !m_bShowMarketButton );
		pMarketButton->SetVisible( m_bShowMarketButton );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CItemAdPanel::OnTick()
{
	const CTFItemDefinition* pItemDef = GetItemDef();
	bool bStoreIsReady = EconUI()->GetStorePanel() && EconUI()->GetStorePanel()->GetPriceSheet() && EconUI()->GetStorePanel()->GetCart() && steamapicontext && steamapicontext->SteamUser() && pItemDef;
	if ( bStoreIsReady )
	{
		// Get the price of the item
		const ECurrency eCurrency = EconUI()->GetStorePanel()->GetCurrency();
		const econ_store_entry_t *pEntry = EconUI()->GetStorePanel()->GetPriceSheet()->GetEntry( pItemDef->GetDefinitionIndex() );
		if ( pEntry )
		{
			item_price_t unPrice = pEntry->GetCurrentPrice( eCurrency );
			// Set that price into the button
			wchar_t wzLocalizedPrice[ kLocalizedPriceSizeInChararacters ];
			MakeMoneyString( wzLocalizedPrice, ARRAYSIZE( wzLocalizedPrice ), unPrice, eCurrency );
			SetDialogVariable( "price", wzLocalizedPrice );

			// Don't need to tick anymore
			vgui::ivgui()->RemoveTickSignal( GetVPanel() );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
const CTFItemDefinition* CItemAdPanel::GetItemDef() const
{
	return (CTFItemDefinition*)ItemSystem()->GetItemSchema()->GetItemDefinition( m_ItemDefIndex );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CItemAdPanel::OnCommand( const char *command )
{
	if ( FStrEq( "purchase", command ) )
	{
		if ( !CheckForRequiredSteamComponents( "#StoreUpdate_SteamRequired", "#MMenu_OverlayRequired" ) )
			return;

		const CTFItemDefinition* pItemDef = GetItemDef();
		if ( pItemDef )
		{
			if ( EconUI()->GetStorePanel() && EconUI()->GetStorePanel()->GetPriceSheet() && EconUI()->GetStorePanel()->GetCart() && steamapicontext && steamapicontext->SteamUser() )
			{
				// Add a the item to the users cart and checkout
				EconUI()->GetStorePanel()->GetCart()->EmptyCart();
				AddItemToCartHelper( NULL, pItemDef->GetDefinitionIndex(), kCartItem_Purchase );
				EconUI()->GetStorePanel()->InitiateCheckout( true );
			}
		}
	}
	else if ( FStrEq( "market", command ) ) 
	{
		if ( !CheckForRequiredSteamComponents( "#StoreUpdate_SteamRequired", "#MMenu_OverlayRequired" ) )
			return;

		const CTFItemDefinition* pItemDef = GetItemDef();
		if ( pItemDef && steamapicontext && steamapicontext->SteamFriends() )
		{
			const char *pszPrefix = "";
			if ( GetUniverse() == k_EUniverseBeta )
			{
				pszPrefix = "beta.";
			}

			static char pszItemName[256];
			g_pVGuiLocalize->ConvertUnicodeToANSI( g_pVGuiLocalize->Find ( pItemDef->GetItemBaseName() ) , pszItemName, sizeof(pszItemName) );

			char szURL[512];
			V_snprintf( szURL, sizeof(szURL), "http://%ssteamcommunity.com/market/listings/%d/%s", pszPrefix, engine->GetAppID(), pszItemName );
			steamapicontext->SteamFriends()->ActivateGameOverlayToWebPage( szURL );
		}
	}
}



DECLARE_BUILD_FACTORY( CCyclingAdContainerPanel );

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CCyclingAdContainerPanel::CCyclingAdContainerPanel( Panel *parent, const char *panelName )
	: BaseClass( parent, panelName )
	, m_pAdsContainer( NULL )
	, m_pKVItems( NULL )
	, m_nCurrentIndex( 0 )
	, m_nXPos( 0 )
	, m_nTargetIndex( 0 )
	, m_nTransitionStartOffsetX( 0 )
	, m_bTransitionRight( true )
	, m_bSettingsApplied( false )
	, m_bNeedsToCreatePanels( false )
{
	m_pAdsContainer = new EditablePanel( this, "AdsContainer" );
	m_pFadePanel = new EditablePanel( this, "FadeTransition" );
	m_pNextButton = new CExButton( this, "NextButton", ">", this, "next" );
	m_pPrevButton = new CExButton( this, "PrevButton", "<", this, "prev" );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CCyclingAdContainerPanel::~CCyclingAdContainerPanel()
{
	if ( m_pKVItems )
	{
		m_pKVItems->deleteThis();
		m_pKVItems = NULL;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCyclingAdContainerPanel::ApplySchemeSettings( IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	LoadControlSettings( "Resource/UI/econ/CyclingAdContainer.res" );

	m_bSettingsApplied = true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCyclingAdContainerPanel::ApplySettings( KeyValues *inResourceData )
{
	BaseClass::ApplySettings( inResourceData );

	KeyValues* pKVItems = inResourceData->FindKey( "items" );
	if ( pKVItems )
	{
		SetItemKVs( pKVItems );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCyclingAdContainerPanel::CreatePanels()
{
	if ( ItemSystem()->GetItemSchema()->GetVersion() == 0 )
		return;

	m_vecPossibleAds.Purge();

	FOR_EACH_TRUE_SUBKEY( m_pKVItems, pKVItem )
	{
		const char* pszItemName = pKVItem->GetString( "item" );
		const CEconItemDefinition *pDef = ItemSystem()->GetItemSchema()->GetItemDefinitionByName( pszItemName );
		if ( pDef )
		{
			AdData_t& adData = m_vecPossibleAds[ m_vecPossibleAds.AddToTail() ];
			adData.m_pAdPanel = new CItemAdPanel( m_pAdsContainer, "ad", pDef->GetDefinitionIndex() );

			adData.m_pAdPanel->InvalidateLayout( true, true );	// Default settings
			adData.m_pAdPanel->ApplySettings( pKVItem );		
			adData.m_pAdPanel->InvalidateLayout();		
		}
		else
		{
			AssertMsg( 0, "Invalid item def '%s'!", pszItemName );
		}
	}

	m_bNeedsToCreatePanels = false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCyclingAdContainerPanel::PerformLayout()
{
	BaseClass::PerformLayout();

	PresentIndex( 0 );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCyclingAdContainerPanel::OnThink() 
{
	BaseClass::OnThink();

	if ( m_bNeedsToCreatePanels && m_bSettingsApplied )
	{
		CreatePanels();
		PresentIndex( 0 );
	}

	UpdateAdPanelPositions();

	// See if it's time to auto-cycle to the next ad
	if ( m_ShowTimer.HasStarted() && m_ShowTimer.IsElapsed() && m_vecPossibleAds.Count() > 1 )
	{
		m_ShowTimer.Invalidate();
		PresentIndex( m_nTargetIndex + 1 );
	}

	int nMouseX, nMouseY;
	vgui::input()->GetCursorPos( nMouseX, nMouseY );
	bool bControlsVisible = IsWithin( nMouseX, nMouseY ) && m_vecPossibleAds.Count() > 1;
	m_pPrevButton->SetVisible( bControlsVisible );
	m_pNextButton->SetVisible( bControlsVisible );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCyclingAdContainerPanel::SetItemKVs( KeyValues* pKVItems )
{
	if ( pKVItems )
	{
		if ( m_pKVItems )
		{
			m_pKVItems->deleteThis();
			m_pKVItems = NULL;
		}
		m_pKVItems = pKVItems->MakeCopy();
	}

	m_bNeedsToCreatePanels = true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCyclingAdContainerPanel::OnCommand( const char *command )
{
	if ( FStrEq( "next", command ) )
	{
		PresentIndex( m_nTargetIndex + 1 );
	}
	else if ( FStrEq( "prev", command ) )
	{
		PresentIndex( m_nTargetIndex - 1 );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCyclingAdContainerPanel::PresentIndex( int nIndex )
{
	if ( m_vecPossibleAds.IsEmpty() )
		return;

	if ( m_nCurrentIndex == nIndex )
		return;

	// Figure out which way we want to ransition
	m_bTransitionRight = nIndex > m_nCurrentIndex;

	// Wrap if needed
	if ( nIndex >= m_vecPossibleAds.Count() )
	{
		nIndex = 0;
	}
	else if ( nIndex < 0 )
	{
		nIndex = m_vecPossibleAds.Count() - 1;
	}

	m_nTargetIndex = nIndex;

	// If they click more times while transitioning out, just change the target.  If we're
	// into transitioning in to the next panel, then we need to start the whole thing over.
	if ( !IsTransitioningOut() )
	{
		m_nTransitionStartOffsetX = m_nXPos;
		float flTransitionTime = 1.f;
		m_TransitionTimer.Start( flTransitionTime );

		m_ShowTimer.Start( flTransitionTime + m_vecPossibleAds[ m_nCurrentIndex ].m_pAdPanel->GetPresentTime() );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CCyclingAdContainerPanel::UpdateAdPanelPositions()
{
	// Figure out how far along a transition we are
	float flPercent = Clamp( m_TransitionTimer.GetElapsedTime() / m_TransitionTimer.GetCountdownDuration(), 0.f, 1.f );
	flPercent = Gain( flPercent, 0.8f );

	// At a certain point, we're no longer transitioning out the old -- we're transitioning in the new
	const float flTransitionCutOff = m_TransitionTimer.GetCountdownDuration() / 2.f;
	bool bTransitionOut = flPercent < flTransitionCutOff;

	int nStartX = 0;
	int nTargetX = 0;
	float flFadeAmount = 0.f;

	if ( bTransitionOut )
	{
		nStartX = m_nTransitionStartOffsetX;
		nTargetX = m_bTransitionRight ? -100 : 100;
		flFadeAmount = RemapValClamped( flPercent, 0.f, flTransitionCutOff * 0.75f, 0.f, 255.f );
	}
	else
	{
		// Once we've passed the middle, show the target
		m_nCurrentIndex = m_nTargetIndex;
		nStartX = m_bTransitionRight ? 100 : -100;
		nTargetX = 0;
		flFadeAmount = RemapValClamped( flPercent, flTransitionCutOff * 1.25f, 1.f, 255.f, 0.f );
	}

	// Alpha fades up entirely near the middle to cover the swap
	m_pFadePanel->SetAlpha( flFadeAmount );

	m_nXPos = RemapVal( flPercent, 0.f, 1.f, nStartX, nTargetX );
	FOR_EACH_VEC( m_vecPossibleAds, i )
	{
		m_vecPossibleAds[i].m_pAdPanel->SetPos( m_nXPos, m_vecPossibleAds[i].m_pAdPanel->GetYPos() );
		m_vecPossibleAds[i].m_pAdPanel->SetVisible( i == m_nCurrentIndex );
	}
}

float CCyclingAdContainerPanel::GetTransitionProgress() const
{
	float flPercent = Clamp( m_TransitionTimer.GetElapsedTime() / m_TransitionTimer.GetCountdownDuration(), 0.f, 1.f );
	return Gain( flPercent, 0.8f );
}

bool CCyclingAdContainerPanel::IsTransitioningOut() const
{
	const float flTransitionCutOff = m_TransitionTimer.GetCountdownDuration() / 2.f;
	return GetTransitionProgress() < flTransitionCutOff;
}