import class gameuiVehicleShopGameController extends inkGameController
{
	private editable var m_homePage : inkWidgetRef;
	private editable var m_homePageMainText : inkTextRef;
	private editable var m_rightSidePanel : inkWidgetRef;
	private editable var m_headerImage : inkWidgetRef;
	private editable var m_offersCanvas : inkWidgetRef;
	private editable var m_detailsCanvas : inkWidgetRef;
	private editable var m_brandsListWidget : inkCompoundRef;
	private editable var m_offersGridWidget : inkCompoundRef;
	private editable var m_headerText : inkTextRef;
	private editable var m_scrollControllerWidget : inkWidgetRef;
	private editable var m_playerBalanceText : inkTextRef;
	private var m_playerBalanceAnimator : weak< MoneyLabelController >;
	private var m_callback : VehicleShopPlayerBalanceCallback;
	private var m_inventoryListener : InventoryScriptListener;
	private var m_uiScriptableSystem : weak< UIScriptableSystem >;
	private var m_brandButtons : array< weak< VehicleBrandFilterLogicController > >;
	private var m_offerButtons : array< weak< VehicleOfferLogicController > >;
	private var m_detailsController : weak< VehicleDetailsLogicController >;
	private var m_currentBrandController : weak< VehicleBrandFilterLogicController >;
	private var m_discount : Float;
	private const var c_discountFactTDBID : TweakDBID;
	default c_discountFactTDBID = T"RTDB.VehicleDiscountSettings.discountFact";
	private const var c_discountValuesTDBID : TweakDBID;
	default c_discountValuesTDBID = T"RTDB.VehicleDiscountSettings.discountValues";

	public import function GetCarBrands( brands : ref< array< CName > > );
	public import function GetCarOffersByBrand( brand : CName, offers : ref< array< weak< VehicleOffer_Record > > > );

	protected event OnInitialize()
	{
		var player : PlayerPuppet;
		var brands : array< CName >;
		player = ( ( PlayerPuppet )( GetPlayerControlledObject() ) );
		m_uiScriptableSystem = UIScriptableSystem.GetInstance( GetGame() );
		m_currentBrandController = NULL;
		m_detailsController = ( ( VehicleDetailsLogicController )( inkWidgetRef.GetController( m_detailsCanvas ) ) );
		inkWidgetRef.RegisterToCallback( m_headerImage, 'OnRelease', this, 'OnHeaderClick' );
		m_playerBalanceAnimator = ( ( MoneyLabelController )( inkTextRef.GetController( m_playerBalanceText ) ) );
		m_callback = new VehicleShopPlayerBalanceCallback;
		m_callback.m_owner = this;
		m_inventoryListener = GameInstance.GetTransactionSystem( player.GetGame() ).RegisterInventoryListener( player, m_callback );
		UpdatePlayerBalance();
		UpdateDiscount();
		GetCarBrands( brands );
		SortBrands( brands );
		SetUpBrands( brands );
		OpenHomeScreen();
	}

	protected event OnUninitialize()
	{
		var player : PlayerPuppet;
		player = ( ( PlayerPuppet )( GetPlayerControlledObject() ) );
		GameInstance.GetTransactionSystem( player.GetGame() ).UnregisterInventoryListener( player, m_inventoryListener );
		m_inventoryListener = NULL;
		inkWidgetRef.UnregisterFromCallback( m_headerImage, 'OnRelease', this, 'OnHeaderClick' );
		ClearBrands();
		ClearOffers();
	}

	public function UpdatePlayerBalance()
	{
		var playerMoney : Int32;
		playerMoney = GetPlayerMoney();
		if( playerMoney != StringToInt( inkTextRef.GetText( m_playerBalanceText ) ) )
		{
			m_playerBalanceAnimator.SetMoney( playerMoney, 0.1, 0.25 );
		}
	}

	private function SetUpHomeScreen()
	{
		if( GetFact( GetGame(), 'vehicle_metaquest_discounts_active' ) > 0 )
		{
			inkTextRef.SetText( m_homePageMainText, GetLocalizedText( "LocKey#93536" ) );
		}
		else
		{
			inkTextRef.SetText( m_homePageMainText, GetLocalizedText( "LocKey#87458" ) );
		}
	}

	private function SetUpBrands( brands : ref< array< CName > > )
	{
		var widget : weak< inkWidget >;
		var controller : weak< VehicleBrandFilterLogicController >;
		var brandState : EVehicleBrandState;
		var i : Int32;
		ClearBrands();
		for( i = 0; i < brands.Size(); i += 1 )
		{
			brandState = EVehicleBrandState.Default;
			if( DoesBrandHaveNewOffers( brands[ i ] ) )
			{
				brandState = EVehicleBrandState.New;
			}
			widget = SpawnFromLocal( inkCompoundRef.Get( m_brandsListWidget ), 'brandFilter' );
			controller = ( ( VehicleBrandFilterLogicController )( widget.GetController() ) );
			controller.RegisterToCallback( 'OnRelease', this, 'OnBrandClick' );
			controller.RegisterToCallback( 'OnHoverOver', this, 'OnBrandHoverOver' );
			controller.RegisterToCallback( 'OnHoverOut', this, 'OnBrandHoverOut' );
			controller.SetUp( brands[ i ], brandState );
			m_brandButtons.PushBack( controller );
		}
	}

	private function SetUpOffers( brandController : weak< VehicleBrandFilterLogicController > )
	{
		var offers : array< weak< VehicleOffer_Record > >;
		var widget : weak< inkWidget >;
		var controller : weak< VehicleOfferLogicController >;
		var offerState : EVehicleOfferState;
		var brand : CName;
		var i : Int32;
		brand = brandController.GetBrand();
		inkWidgetRef.SetVisible( m_detailsCanvas, false );
		inkWidgetRef.SetVisible( m_offersCanvas, true );
		ClearOffers();
		GetCarOffersByBrand( brand, offers );
		SortOffersByState( offers );
		for( i = 0; i < offers.Size(); i += 1 )
		{
			offerState = EVehicleOfferState.Default;
			if( CheckFact( offers[ i ].OwnershipFact() ) )
			{
				offerState = EVehicleOfferState.Owned;
			}
			else if( !( CheckFact( offers[ i ].AvailabilityFact() ) ) )
			{
				offerState = EVehicleOfferState.Locked;
			}
			else if( CheckVehicleNew( offers[ i ] ) )
			{
				offerState = EVehicleOfferState.New;
				brandController.AddNewOffer( offers[ i ].AvailabilityFact() );
			}
			widget = SpawnFromLocal( inkCompoundRef.Get( m_offersGridWidget ), 'offer' );
			controller = ( ( VehicleOfferLogicController )( widget.GetController() ) );
			controller.RegisterToCallback( 'OnRelease', this, 'OnOfferClick' );
			controller.RegisterToCallback( 'OnHoverOver', this, 'OnOfferHoverOver' );
			controller.RegisterToCallback( 'OnHoverOut', this, 'OnOfferHoverOut' );
			controller.SetUp( offers[ i ], offerState, m_discount );
			m_offerButtons.PushBack( controller );
		}
	}

	private function ClearBrands()
	{
		var controller : weak< VehicleBrandFilterLogicController >;
		var i : Int32;
		for( i = 0; i < m_brandButtons.Size(); i += 1 )
		{
			controller = m_brandButtons[ i ];
			controller.UnregisterFromCallback( 'OnRelease', this, 'OnBrandClick' );
			controller.UnregisterFromCallback( 'OnHoverOver', this, 'OnBrandHoverOver' );
			controller.UnregisterFromCallback( 'OnHoverOut', this, 'OnBrandHoverOut' );
		}
		m_brandButtons.Clear();
		inkCompoundRef.RemoveAllChildren( m_brandsListWidget );
	}

	private function ClearOffers()
	{
		var controller : weak< VehicleOfferLogicController >;
		var i : Int32;
		for( i = 0; i < m_offerButtons.Size(); i += 1 )
		{
			controller = m_offerButtons[ i ];
			controller.UnregisterFromCallback( 'OnRelease', this, 'OnOfferClick' );
			controller.UnregisterFromCallback( 'OnHoverOver', this, 'OnOfferHoverOver' );
			controller.UnregisterFromCallback( 'OnHoverOut', this, 'OnOfferHoverOut' );
		}
		m_offerButtons.Clear();
		inkCompoundRef.RemoveAllChildren( m_offersGridWidget );
	}

	private function DoesBrandHaveNewOffers( brand : CName ) : Bool
	{
		var offers : array< weak< VehicleOffer_Record > >;
		var i : Int32;
		GetCarOffersByBrand( brand, offers );
		for( i = 0; i < offers.Size(); i += 1 )
		{
			if( CheckVehicleNew( offers[ i ] ) )
			{
				return true;
			}
		}
		return false;
	}

	private function ShowBrandOffers( brandController : weak< VehicleBrandFilterLogicController > )
	{
		if( ( m_currentBrandController == brandController ) && !( inkWidgetRef.IsVisible( m_detailsCanvas ) ) )
		{
			return;
		}
		inkWidgetRef.SetVisible( m_homePage, false );
		inkWidgetRef.SetVisible( m_rightSidePanel, true );
		if( m_currentBrandController != brandController )
		{
			m_currentBrandController.UpdateState( ( ( m_currentBrandController.HasNewOffers() ) ? ( EVehicleBrandState.New ) : ( EVehicleBrandState.Default ) ) );
			brandController.UpdateState( EVehicleBrandState.Selected );
			m_currentBrandController = brandController;
			inkTextRef.SetText( m_headerText, GetLocalizedText( VehicleShopUtils.BrandToLocKey( brandController.GetBrand() ) ) );
		}
		( ( inkScrollController )( inkWidgetRef.GetController( m_scrollControllerWidget ) ) ).SetScrollPosition( 0.0 );
		SetUpOffers( m_currentBrandController );
		brandController.RefreshState();
	}

	private function OpenHomeScreen()
	{
		if( m_currentBrandController != NULL )
		{
			m_currentBrandController.UpdateState( ( ( m_currentBrandController.HasNewOffers() ) ? ( EVehicleBrandState.New ) : ( EVehicleBrandState.Default ) ) );
			m_currentBrandController = NULL;
		}
		SetUpHomeScreen();
		inkWidgetRef.SetVisible( m_homePage, true );
		inkWidgetRef.SetVisible( m_rightSidePanel, false );
	}

	private function ShowOfferDetails( offerController : ref< weak< VehicleOfferLogicController > > )
	{
		inkWidgetRef.SetVisible( m_detailsCanvas, true );
		inkWidgetRef.SetVisible( m_offersCanvas, false );
		m_detailsController.SetUp( offerController.GetOfferRecord(), offerController.GetState(), GetPlayerMoney(), m_discount );
	}

	protected event OnHeaderClick( e : inkPointerEvent )
	{
		OpenHomeScreen();
	}

	protected event OnBrandClick( e : inkPointerEvent )
	{
		var actionName : inkActionName;
		var brandController : weak< VehicleBrandFilterLogicController >;
		brandController = ( ( VehicleBrandFilterLogicController )( e.GetCurrentTarget().GetController() ) );
		actionName = e.GetActionName();
		if( actionName.IsAction( 'click' ) )
		{
			ShowBrandOffers( brandController );
		}
	}

	protected event OnBrandHoverOver( e : inkPointerEvent )
	{
		var brandController : weak< VehicleBrandFilterLogicController >;
		brandController = ( ( VehicleBrandFilterLogicController )( e.GetCurrentTarget().GetController() ) );
		brandController.SetHoverState( true );
	}

	protected event OnBrandHoverOut( e : inkPointerEvent )
	{
		var brandController : weak< VehicleBrandFilterLogicController >;
		brandController = ( ( VehicleBrandFilterLogicController )( e.GetCurrentTarget().GetController() ) );
		brandController.SetHoverState( false );
	}

	protected event OnOfferClick( e : inkPointerEvent )
	{
		var actionName : inkActionName;
		var offerController : weak< VehicleOfferLogicController >;
		offerController = ( ( VehicleOfferLogicController )( e.GetCurrentTarget().GetController() ) );
		actionName = e.GetActionName();
		if( actionName.IsAction( 'click' ) )
		{
			ShowOfferDetails( offerController );
		}
	}

	protected event OnOfferHoverOver( e : inkPointerEvent )
	{
		var offerController : weak< VehicleOfferLogicController >;
		offerController = ( ( VehicleOfferLogicController )( e.GetCurrentTarget().GetController() ) );
		offerController.SetHoverState( true );
	}

	protected event OnOfferHoverOut( e : inkPointerEvent )
	{
		var offerController : weak< VehicleOfferLogicController >;
		var availabilityFact : CName;
		offerController = ( ( VehicleOfferLogicController )( e.GetCurrentTarget().GetController() ) );
		availabilityFact = offerController.GetOfferRecord().AvailabilityFact();
		if( offerController.GetState() == EVehicleOfferState.New )
		{
			MarkNewCarViewed( availabilityFact );
			m_currentBrandController.RemoveNewOffer( availabilityFact );
		}
		offerController.SetHoverState( false );
	}

	protected event OnVehicleShopBackEventEvent( evt : VehicleShopBackEvent )
	{
		if( inkWidgetRef.IsVisible( m_detailsCanvas ) )
		{
			SetUpOffers( m_currentBrandController );
		}
	}

	protected event OnVehicleShopPurchaseEventEvent( evt : VehicleShopPurchaseEvent )
	{
		var playerMoney, basePrice, price : Int32;
		var discounted : Bool;
		playerMoney = GetPlayerMoney();
		discounted = evt.m_offerRecord.DiscountApplicable();
		basePrice = evt.m_offerRecord.Price().OverrideValue();
		price = ( ( discounted ) ? ( GetDiscountedPrice( basePrice ) ) : ( basePrice ) );
		if( ( ( playerMoney >= price ) && CheckFact( evt.m_offerRecord.AvailabilityFact() ) ) && !( CheckFact( evt.m_offerRecord.OwnershipFact() ) ) )
		{
			if( RemovePlayerMoney( price ) )
			{
				ResetDiscount();
				SetFact( evt.m_offerRecord.OwnershipFact(), 1 );
				m_detailsController.UpdateState( EVehicleOfferState.Owned, playerMoney );
				ShowNotification();
			}
		}
	}

	private function SortBrands( sortedBrands : ref< array< CName > > )
	{
		var tempVar : CName;
		var i, j : Int32;
		for( i = 0; i < ( sortedBrands.Size() - 1 ); i += 1 )
		{
			for( j = i + 1; j < sortedBrands.Size(); j += 1 )
			{
				if( GetBrandWeight( sortedBrands[ i ] ) < GetBrandWeight( sortedBrands[ j ] ) )
				{
					tempVar = sortedBrands[ j ];
					sortedBrands[ j ] = sortedBrands[ i ];
					sortedBrands[ i ] = tempVar;
				}
			}
		}
	}

	private function SortOffersByState( sortedOffers : ref< array< weak< VehicleOffer_Record > > > )
	{
		var tempVar : weak< VehicleOffer_Record >;
		var i, j : Int32;
		for( i = 0; i < ( sortedOffers.Size() - 1 ); i += 1 )
		{
			for( j = i + 1; j < sortedOffers.Size(); j += 1 )
			{
				if( GetOfferWeight( sortedOffers[ i ] ) < GetOfferWeight( sortedOffers[ j ] ) )
				{
					tempVar = sortedOffers[ j ];
					sortedOffers[ j ] = sortedOffers[ i ];
					sortedOffers[ i ] = tempVar;
				}
			}
		}
	}

	private function GetBrandWeight( brand : CName ) : Int32
	{
		switch( brand )
		{
			case 'Archer':
				return 12;
			case 'Chevillion':
				return 11;
			case 'Quadra':
				return 10;
			case 'Mizutani':
				return 9;
			case 'Makigai':
				return 8;
			case 'Thorton':
				return 7;
			case 'Villefort':
				return 6;
			case 'Rayfield':
				return 5;
			case 'Mahir':
				return 4;
			case 'Herrera':
				return 3;
			case 'Motorcycles':
				return 1;
			case 'Other':
				return 0;
			default:
				return 2;
		}
	}

	private function GetOfferWeight( offer : weak< VehicleOffer_Record > ) : Int32
	{
		if( CheckVehicleNew( offer ) )
		{
			return 3;
		}
		else if( CheckFact( offer.OwnershipFact() ) )
		{
			return 1;
		}
		else if( !( CheckFact( offer.AvailabilityFact() ) ) )
		{
			return 0;
		}
		else
		{
			return 2;
		}
	}

	private function GetPlayerMoney() : Int32
	{
		var gi : GameInstance;
		var player : PlayerPuppet;
		var transactionSystem : TransactionSystem;
		gi = GetGame();
		player = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( gi ).GetLocalPlayerControlledGameObject() ) );
		transactionSystem = GameInstance.GetTransactionSystem( gi );
		return transactionSystem.GetItemQuantity( player, MarketSystem.Money() );
	}

	private function RemovePlayerMoney( amount : Int32 ) : Bool
	{
		var gi : GameInstance;
		var player : PlayerPuppet;
		var transactionSystem : TransactionSystem;
		gi = GetGame();
		player = ( ( PlayerPuppet )( GameInstance.GetPlayerSystem( gi ).GetLocalPlayerControlledGameObject() ) );
		transactionSystem = GameInstance.GetTransactionSystem( gi );
		return transactionSystem.RemoveItem( player, MarketSystem.Money(), amount );
	}

	private function UpdateDiscount()
	{
		var factValue : Int32;
		var discounts : array< Float >;
		factValue = GetFact( GetGame(), TweakDBInterface.GetCNameDefault( c_discountFactTDBID ) );
		discounts = TweakDBInterface.GetFloatArrayDefault( c_discountValuesTDBID );
		factValue = Clamp( factValue, 0, discounts.Size() - 1 );
		m_discount = discounts[ factValue ];
	}

	private function ResetDiscount()
	{
		var factValue : Int32;
		factValue = GetFact( GetGame(), TweakDBInterface.GetCNameDefault( c_discountFactTDBID ) );
		factValue -= 25;
		if( factValue < 0 )
		{
			factValue = 0;
		}
		SetFactValue( GetGame(), TweakDBInterface.GetCNameDefault( c_discountFactTDBID ), factValue );
		UpdateDiscount();
	}

	private function GetDiscountedPrice( price : Int32 ) : Int32
	{
		return VehicleShopUtils.GetDiscountedPrice( price, m_discount );
	}

	public function CheckFact( factName : CName ) : Bool
	{
		return GetFact( GetGame(), factName ) > 0;
	}

	public function SetFact( factName : CName, factCount : Int32 ) : Bool
	{
		return SetFactValue( GetGame(), factName, factCount );
	}

	public function CheckVehicleNew( vehicleRecord : weak< VehicleOffer_Record > ) : Bool
	{
		return CheckFact( vehicleRecord.AvailabilityFact() ) && m_uiScriptableSystem.IsAvailableCarNew( vehicleRecord.AvailabilityFact() );
	}

	private function MarkNewCarViewed( carFact : CName )
	{
		var request : UIScriptableSystemAddAvailableCar;
		request = new UIScriptableSystemAddAvailableCar;
		request.carFact = carFact;
		UIScriptableSystem.GetInstance( GetGame() ).QueueRequest( request );
	}

	protected function GetGame() : GameInstance
	{
		var gameInstance : GameInstance;
		var gameObject : GameObject;
		gameObject = ( ( GameObject )( GetOwnerEntity() ) );
		if( gameObject )
		{
			gameInstance = gameObject.GetGame();
		}
		return gameInstance;
	}

	private function ShowNotification()
	{
		PlayLibraryAnimation( 'notification_anim' );
	}

}

class VehicleShopUtils
{

	public static function BrandToTexturePartString( carBrand : CName ) : String
	{
		switch( carBrand )
		{
			case 'Motorcycles':
				return "bikes";
			case 'Other':
				return "others";
			case 'Chevillion':
				return "chevillon";
			default:
				return StrLower( NameToString( carBrand ) );
		}
	}

	public static function BrandToLocKey( carBrand : CName ) : String
	{
		switch( carBrand )
		{
			case 'Archer':
				return "LocKey#86989";
			case 'Chevillion':
				return "LocKey#86992";
			case 'Quadra':
				return "LocKey#86996";
			case 'Mizutani':
				return "LocKey#86994";
			case 'Makigai':
				return "LocKey#86993";
			case 'Thorton':
				return "LocKey#86990";
			case 'Villefort':
				return "LocKey#86998";
			case 'Rayfield':
				return "LocKey#86997";
			case 'Mahir':
				return "LocKey#94093";
			case 'Herrera':
				return "LocKey#94094";
			case 'Motorcycles':
				return "LocKey#86991";
			default:
				return "LocKey#86995";
		}
	}

	public static function GetDiscountedPrice( price : Int32, discount : Float ) : Int32
	{
		return price - ( ( Int32 )( ( ( ( Float )( price ) ) * discount ) ) );
	}

}

class VehicleShopPlayerBalanceCallback extends InventoryScriptCallback
{
	var m_owner : weak< gameuiVehicleShopGameController >;

	public override function OnItemQuantityChanged( item : ItemID, diff : Int32, total : Uint32, flaggedAsSilent : Bool )
	{
		if( item == MarketSystem.Money() )
		{
			m_owner.UpdatePlayerBalance();
		}
	}

}

class VehicleShopBackEvent extends Event
{
}

class VehicleShopPurchaseEvent extends Event
{
	var m_offerRecord : weak< VehicleOffer_Record >;
}

