class IDisplayData
{

	public virtual function CreateTooltipData( manager : PlayerDevelopmentDataManager ) : BasePerksMenuTooltipData
	{
		return NULL;
	}

}

class BasePerkDisplayData extends IDisplayData
{
	var m_attributeId : TweakDBID;
	var m_name : CName;
	var m_localizedName : String;
	var m_localizedDescription : String;
	var m_iconID : TweakDBID;
	var m_binkRef : ResRef;
	var m_level : Int32;
	var m_maxLevel : Int32;
	var m_locked : Bool;
	var m_proficiency : gamedataProficiencyType;
}

class PerkDisplayData extends BasePerkDisplayData
{
	var m_area : gamedataPerkArea;
	var m_type : gamedataPerkType;

	public override function CreateTooltipData( manager : PlayerDevelopmentDataManager ) : BasePerksMenuTooltipData
	{
		var data : PerkTooltipData;
		data = new PerkTooltipData;
		data.manager = manager;
		data.perkType = m_type;
		data.perkArea = m_area;
		data.proficiency = m_proficiency;
		data.attributeId = m_attributeId;
		data.perkData = this;
		return data;
	}

}

class NewPerkDisplayData extends BasePerkDisplayData
{
	var m_area : gamedataNewPerkSlotType;
	var m_type : gamedataNewPerkType;
	var m_isRipperdoc : Bool;

	public override function CreateTooltipData( manager : PlayerDevelopmentDataManager ) : BasePerksMenuTooltipData
	{
		var data : NewPerkTooltipData;
		data = new NewPerkTooltipData;
		data.manager = manager;
		data.perkType = m_type;
		data.perkArea = m_area;
		data.proficiency = m_proficiency;
		data.attributeId = m_attributeId;
		data.isRipperdoc = m_isRipperdoc;
		data.perkData = this;
		return data;
	}

}

class TraitDisplayData extends BasePerkDisplayData
{
	var m_type : gamedataTraitType;

	public override function CreateTooltipData( manager : PlayerDevelopmentDataManager ) : BasePerksMenuTooltipData
	{
		var data : TraitTooltipData;
		data = new TraitTooltipData;
		data.manager = manager;
		data.traitType = m_type;
		data.proficiency = m_proficiency;
		data.attributeId = m_attributeId;
		return data;
	}

}

class AreaDisplayData extends IDisplayData
{
	var m_attributeId : TweakDBID;
	var m_perks : array< PerkDisplayData >;
	var m_locked : Bool;
	var m_proficency : gamedataProficiencyType;
	var m_area : gamedataPerkArea;
}

class ProficiencyDisplayData extends IDisplayData
{
	var m_attributeId : TweakDBID;
	var m_proficiency : gamedataProficiencyType;
	var m_index : Int32;
	var m_areas : array< AreaDisplayData >;
	var m_passiveBonusesData : array< LevelRewardDisplayData >;
	var m_traitData : TraitDisplayData;
	var m_localizedName : String;
	var m_localizedDescription : String;
	var m_level : Int32;
	var m_maxLevel : Int32;
	var m_expPoints : Int32;
	var m_maxExpPoints : Int32;
	var m_unlockedLevel : Int32;

	public override function CreateTooltipData( manager : PlayerDevelopmentDataManager ) : BasePerksMenuTooltipData
	{
		var data : SkillTooltipData;
		data = new SkillTooltipData;
		data.manager = manager;
		data.proficiencyType = m_proficiency;
		data.attributeRecord = TweakDBInterface.GetAttributeRecord( m_attributeId );
		return data;
	}

}

class AttributeDisplayData extends IDisplayData
{
	var m_attributeId : TweakDBID;
	var m_proficiencies : array< ProficiencyDisplayData >;

	public override function CreateTooltipData( manager : PlayerDevelopmentDataManager ) : BasePerksMenuTooltipData
	{
		var data : AttributeTooltipData;
		data = new AttributeTooltipData;
		data.manager = manager;
		data.attributeId = m_attributeId;
		data.attributeType = manager.GetAttributeEnumFromRecordID( m_attributeId );
		return data;
	}

}

class AttributeData extends IDisplayData
{
	var label : String;
	var icon : String;
	var id : TweakDBID;
	var value : Int32;
	var maxValue : Int32;
	var description : String;
	var availableToUpgrade : Bool;
	var type : gamedataStatType;

	public override function CreateTooltipData( manager : PlayerDevelopmentDataManager ) : BasePerksMenuTooltipData
	{
		var data : AttributeTooltipData;
		data = new AttributeTooltipData;
		data.manager = manager;
		data.attributeId = id;
		data.attributeType = manager.GetAttributeEnumFromRecordID( id );
		return data;
	}

}

class PerkLevelData_Records extends BasePerkLevelData_Records
{
	private var m_arr : array< weak< PerkLevelData_Record > >;

	public function Initialize( perkRecord : ref< weak< Perk_Record > > )
	{
		perkRecord.Levels( m_arr );
	}

	public const override function GetItemAt( index : Int32 ) : weak< PerkLevelData_Record >
	{
		if( m_arr.Size() <= index )
		{
			return NULL;
		}
		return m_arr[ index ];
	}

}

class NewPerkLevelData_Records extends BasePerkLevelData_Records
{
	private var m_perkRecord : weak< NewPerk_Record >;
	private var m_arr : array< weak< NewPerkLevelData_Record > >;

	public function Initialize( perkRecord : ref< weak< NewPerk_Record > > )
	{
		m_perkRecord = perkRecord;
		perkRecord.Levels( m_arr );
	}

	public function GetNewPerkRecord() : weak< NewPerk_Record >
	{
		return m_perkRecord;
	}

	public function Size() : Int32
	{
		return m_arr.Size();
	}

	public const override function GetItemAt( index : Int32 ) : weak< NewPerkLevelData_Record >
	{
		if( m_arr.Size() <= index )
		{
			return NULL;
		}
		return m_arr[ index ];
	}

}

abstract class BasePerkLevelData_Records
{

	public const virtual function GetItemAt( index : Int32 ) : weak< TweakDBRecord >
	{
		return NULL;
	}

}

class PlayerDevUpdateDataEvent extends Event
{
}

class AttributeUpdatedEvent extends Event
{
	var attributeId : TweakDBID;
}

class PlayerDevelopmentDataManager
{
	private var m_player : weak< PlayerPuppet >;
	private var m_playerDevSystem : PlayerDevelopmentSystem;
	private var m_parentGameCtrl : weak< inkGameController >;

	public function Initialize( player : PlayerPuppet, parentGameCtrl : inkGameController )
	{
		m_parentGameCtrl = parentGameCtrl;
		m_player = player;
		m_playerDevSystem = PlayerDevelopmentSystem.GetInstance( player );
	}

	public function GetPlayerDevelopmentSystem() : weak< PlayerDevelopmentSystem >
	{
		return m_playerDevSystem;
	}

	public function GetPlayerDevelopmentData() : weak< PlayerDevelopmentData >
	{
		return PlayerDevelopmentSystem.GetData( m_player );
	}

	public function GetPlayer() : weak< PlayerPuppet >
	{
		return m_player;
	}

	public function GetNewPerkDisplayData( perkType : gamedataNewPerkType, perkArea : gamedataNewPerkSlotType, proficiency : gamedataProficiencyType, attributeId : TweakDBID, isRipperdoc : Bool, optional playerDevelopmentData : weak< PlayerDevelopmentData > ) : NewPerkDisplayData
	{
		return GetNewPerkDisplayData( perkType, perkArea, proficiency, TweakDBInterface.GetAttributeRecord( attributeId ), isRipperdoc, playerDevelopmentData );
	}

	public function GetNewPerkDisplayData( perkType : gamedataNewPerkType, perkArea : gamedataNewPerkSlotType, proficiency : gamedataProficiencyType, attributeRecord : Attribute_Record, isRipperdoc : Bool, optional playerDevelopmentData : weak< PlayerDevelopmentData > ) : NewPerkDisplayData
	{
		var curPerkDisplayData : NewPerkDisplayData;
		var perkRecord : weak< NewPerk_Record >;
		var perkCurrLevel : Int32;
		if( !( playerDevelopmentData ) )
		{
			playerDevelopmentData = PlayerDevelopmentSystem.GetData( m_player );
		}
		perkRecord = RPGManager.GetNewPerkRecord( perkType );
		curPerkDisplayData = new NewPerkDisplayData;
		curPerkDisplayData.m_isRipperdoc = isRipperdoc;
		curPerkDisplayData.m_attributeId = attributeRecord.GetID();
		curPerkDisplayData.m_localizedName = GetLocalizedText( perkRecord.Loc_name_key() );
		if( curPerkDisplayData.m_localizedName == "" )
		{
			curPerkDisplayData.m_localizedName = NameToString( perkRecord.EnumName() );
		}
		curPerkDisplayData.m_localizedDescription = GetLocalizedText( perkRecord.Loc_desc_key() );
		if( curPerkDisplayData.m_localizedDescription == "" )
		{
			curPerkDisplayData.m_localizedDescription = perkRecord.EnumComment();
		}
		curPerkDisplayData.m_binkRef = perkRecord.BinkPath();
		curPerkDisplayData.m_type = perkType;
		curPerkDisplayData.m_iconID = perkRecord.PerkIcon().GetID();
		curPerkDisplayData.m_locked = !( m_playerDevSystem.IsNewPerkUnlocked( m_player, perkType ) );
		perkCurrLevel = m_playerDevSystem.GetPerkLevel( m_player, curPerkDisplayData.m_type );
		curPerkDisplayData.m_level = ( ( perkCurrLevel < 0 ) ? ( 0 ) : ( perkCurrLevel ) );
		curPerkDisplayData.m_maxLevel = m_playerDevSystem.GetPerkMaxLevel( m_player, curPerkDisplayData.m_type );
		curPerkDisplayData.m_proficiency = proficiency;
		curPerkDisplayData.m_area = perkArea;
		return curPerkDisplayData;
	}

	public function GetPerkDisplayData( perkType : gamedataPerkType, perkArea : gamedataPerkArea, proficiency : gamedataProficiencyType, attributeId : TweakDBID, optional playerDevelopmentData : weak< PlayerDevelopmentData > ) : PerkDisplayData
	{
		return GetPerkDisplayData( perkType, perkArea, proficiency, TweakDBInterface.GetAttributeRecord( attributeId ), playerDevelopmentData );
	}

	public function GetPerkDisplayData( perkType : gamedataPerkType, perkArea : gamedataPerkArea, proficiency : gamedataProficiencyType, attributeRecord : Attribute_Record, optional playerDevelopmentData : weak< PlayerDevelopmentData > ) : PerkDisplayData
	{
		var curPerkDisplayData : PerkDisplayData;
		var perkRecord : weak< Perk_Record >;
		var perkCurrLevel : Int32;
		if( !( playerDevelopmentData ) )
		{
			playerDevelopmentData = PlayerDevelopmentSystem.GetData( m_player );
		}
		perkRecord = playerDevelopmentData.GetPerkRecord( perkType );
		curPerkDisplayData = new PerkDisplayData;
		curPerkDisplayData.m_attributeId = attributeRecord.GetID();
		curPerkDisplayData.m_localizedName = GetLocalizedText( perkRecord.Loc_name_key() );
		curPerkDisplayData.m_localizedDescription = GetLocalizedText( perkRecord.Loc_desc_key() );
		curPerkDisplayData.m_binkRef = perkRecord.BinkPath();
		curPerkDisplayData.m_type = perkType;
		curPerkDisplayData.m_iconID = perkRecord.PerkIcon().GetID();
		curPerkDisplayData.m_locked = !( playerDevelopmentData.IsPerkAreaUnlocked( perkArea ) );
		perkCurrLevel = m_playerDevSystem.GetPerkLevel( m_player, curPerkDisplayData.m_type );
		curPerkDisplayData.m_level = ( ( perkCurrLevel < 0 ) ? ( 0 ) : ( perkCurrLevel ) );
		curPerkDisplayData.m_maxLevel = m_playerDevSystem.GetPerkMaxLevel( m_player, curPerkDisplayData.m_type );
		curPerkDisplayData.m_proficiency = proficiency;
		curPerkDisplayData.m_area = perkArea;
		return curPerkDisplayData;
	}

	public function GetTraitDisplayData( traitType : gamedataTraitType, attributeId : TweakDBID, proficiency : gamedataProficiencyType, optional playerDevelopmentData : weak< PlayerDevelopmentData > ) : TraitDisplayData
	{
		return GetTraitDisplayData( RPGManager.GetTraitRecord( traitType ), TweakDBInterface.GetAttributeRecord( attributeId ), proficiency, playerDevelopmentData );
	}

	public function GetTraitDisplayData( traitRecordId : TweakDBID, attributeId : TweakDBID, proficiency : gamedataProficiencyType, optional playerDevelopmentData : weak< PlayerDevelopmentData > ) : TraitDisplayData
	{
		return GetTraitDisplayData( TweakDBInterface.GetTraitRecord( traitRecordId ), TweakDBInterface.GetAttributeRecord( attributeId ), proficiency, playerDevelopmentData );
	}

	public function GetTraitDisplayData( traitRecord : weak< Trait_Record >, attributeRecord : Attribute_Record, proficiency : gamedataProficiencyType, optional playerDevelopmentData : weak< PlayerDevelopmentData > ) : TraitDisplayData
	{
		var traitData : TraitDisplayData;
		var traitType : gamedataTraitType;
		if( !( playerDevelopmentData ) )
		{
			playerDevelopmentData = PlayerDevelopmentSystem.GetData( m_player );
		}
		traitType = traitRecord.Type();
		traitData = new TraitDisplayData;
		traitData.m_attributeId = attributeRecord.GetID();
		traitData.m_localizedName = GetLocalizedText( traitRecord.Loc_name_key() );
		traitData.m_localizedDescription = GetLocalizedText( traitRecord.Loc_desc_key() );
		traitData.m_type = traitType;
		traitData.m_proficiency = proficiency;
		traitData.m_iconID = traitRecord.TraitIcon().GetID();
		traitData.m_locked = !( playerDevelopmentData.IsTraitUnlocked( traitType ) );
		traitData.m_level = playerDevelopmentData.GetTraitLevel( traitType );
		traitData.m_maxLevel = -1;
		return traitData;
	}

	public function GetAreaDisplayData( perkArea : gamedataPerkArea, proficiency : gamedataProficiencyType, attributeId : TweakDBID, optional playerDevelopmentData : weak< PlayerDevelopmentData > ) : AreaDisplayData
	{
		return GetAreaDisplayData( perkArea, proficiency, TweakDBInterface.GetAttributeRecord( attributeId ), playerDevelopmentData );
	}

	public function GetAreaDisplayData( perkArea : gamedataPerkArea, proficiency : gamedataProficiencyType, attributeRecord : Attribute_Record, optional playerDevelopmentData : weak< PlayerDevelopmentData > ) : AreaDisplayData
	{
		var curPerkAreaDisplayData : AreaDisplayData;
		if( !( playerDevelopmentData ) )
		{
			playerDevelopmentData = PlayerDevelopmentSystem.GetData( m_player );
		}
		curPerkAreaDisplayData = new AreaDisplayData;
		curPerkAreaDisplayData.m_attributeId = attributeRecord.GetID();
		curPerkAreaDisplayData.m_locked = !( playerDevelopmentData.IsPerkAreaUnlocked( perkArea ) );
		curPerkAreaDisplayData.m_area = perkArea;
		curPerkAreaDisplayData.m_proficency = proficiency;
		return curPerkAreaDisplayData;
	}

	public function GetProficiencyDisplayData( proficiency : gamedataProficiencyType ) : ProficiencyDisplayData
	{
		var attributeRecord : weak< Attribute_Record >;
		var proficiencyRecord : weak< Proficiency_Record >;
		attributeRecord = GetAttributeRecordFromProficiency( proficiency );
		if( PlayerDevelopmentData.IsProfficiencyObsolete( proficiency ) )
		{
			proficiencyRecord = GetProficiencyRecord( attributeRecord, proficiency );
			return GetProficiencyDisplayData( proficiencyRecord.Type(), attributeRecord );
		}
		else
		{
			return GetProficiencyDisplayData( RPGManager.GetProficiencyRecord( proficiency ), attributeRecord );
		}
	}

	public function GetProficiencyDisplayData( proficiency : gamedataProficiencyType, attributeId : TweakDBID ) : ProficiencyDisplayData
	{
		return GetProficiencyDisplayData( proficiency, TweakDBInterface.GetAttributeRecord( attributeId ) );
	}

	public function GetProficiencyDisplayData( proficiency : gamedataProficiencyType, attributeRecord : Attribute_Record ) : ProficiencyDisplayData
	{
		var proficiencyRecord : Proficiency_Record;
		proficiencyRecord = GetProficiencyRecord( proficiency );
		return GetProficiencyDisplayData( proficiencyRecord, attributeRecord );
	}

	public function GetProficiencyDisplayData( proficiencyRecord : Proficiency_Record, attributeRecord : Attribute_Record ) : ProficiencyDisplayData
	{
		var curProfDisplayData : ProficiencyDisplayData;
		var proficiency : gamedataProficiencyType;
		var attributeData : AttributeData;
		proficiency = proficiencyRecord.Type();
		attributeData = GetAttribute( attributeRecord.GetID() );
		curProfDisplayData = new ProficiencyDisplayData;
		curProfDisplayData.m_attributeId = attributeRecord.GetID();
		curProfDisplayData.m_proficiency = proficiency;
		curProfDisplayData.m_traitData = GetTraitDisplayData( proficiencyRecord.Trait(), attributeRecord, proficiency );
		curProfDisplayData.m_localizedName = GetLocalizedText( proficiencyRecord.Loc_name_key() );
		curProfDisplayData.m_localizedDescription = GetLocalizedText( proficiencyRecord.Loc_desc_key() );
		curProfDisplayData.m_level = m_playerDevSystem.GetProficiencyLevel( m_player, proficiency );
		curProfDisplayData.m_expPoints = m_playerDevSystem.GetCurrentLevelProficiencyExp( m_player, proficiency );
		curProfDisplayData.m_maxExpPoints = curProfDisplayData.m_expPoints + m_playerDevSystem.GetRemainingExpForLevelUp( m_player, proficiency );
		curProfDisplayData.m_maxLevel = m_playerDevSystem.GetProficiencyAbsoluteMaxLevel( m_player, proficiency );
		curProfDisplayData.m_unlockedLevel = attributeData.value;
		curProfDisplayData.m_passiveBonusesData = GetPassiveBonusDisplayData( proficiencyRecord );
		return curProfDisplayData;
	}

	public function GetPassiveBonusDisplayData( proficiencyRecord : Proficiency_Record ) : array< LevelRewardDisplayData >
	{
		var bonusesDisplay : array< LevelRewardDisplayData >;
		var bonusDisplay : LevelRewardDisplayData;
		var bonusData : weak< PassiveProficiencyBonus_Record >;
		var bonusIndex : Int32;
		for( bonusIndex = 1; bonusIndex < proficiencyRecord.GetPassiveBonusesCount(); bonusIndex += 1 )
		{
			bonusDisplay = new LevelRewardDisplayData;
			bonusData = proficiencyRecord.GetPassiveBonusesItem( bonusIndex );
			bonusDisplay.level = bonusIndex + 1;
			bonusDisplay.locPackage = UILocalizationDataPackage.FromPassiveUIDataPackage( bonusData.UiData() );
			bonusDisplay.description = LocKeyToString( bonusData.UiData().Loc_name_key() );
			bonusesDisplay.PushBack( bonusDisplay );
		}
		return bonusesDisplay;
	}

	private function GetAttributeRecordFromProficiency( proficiency : gamedataProficiencyType ) : weak< Attribute_Record >
	{
		var i, j : Int32;
		var playerDevelopmentData : PlayerDevelopmentData;
		var attributes : array< SAttribute >;
		var attributeRecord : Attribute_Record;
		var proficiencies : array< weak< Proficiency_Record > >;
		playerDevelopmentData = PlayerDevelopmentSystem.GetData( m_player );
		attributes = playerDevelopmentData.GetAttributes();
		for( i = 0; i < attributes.Size(); i += 1 )
		{
			attributeRecord = ( ( Attribute_Record )( playerDevelopmentData.GetAttributeRecord( attributes[ i ].attributeName ) ) );
			attributeRecord.Proficiencies( proficiencies );
			for( j = 0; j < proficiencies.Size(); j += 1 )
			{
				if( proficiencies[ j ].Type() == proficiency )
				{
					return attributeRecord;
				}
			}
		}
		return NULL;
	}

	private function GetProficiencyRecord( attributeRecord : weak< Attribute_Record >, proficiency : gamedataProficiencyType ) : weak< Proficiency_Record >
	{
		var i : Int32;
		var proficiencies : array< weak< Proficiency_Record > >;
		attributeRecord.Proficiencies( proficiencies );
		for( i = 0; i < proficiencies.Size(); i += 1 )
		{
			if( proficiencies[ i ].Type() == proficiency )
			{
				return proficiencies[ i ];
			}
		}
		return NULL;
	}

	private function GetProficiencyRecord( proficiency : gamedataProficiencyType ) : weak< Proficiency_Record >
	{
		var i, j : Int32;
		var playerDevelopmentData : PlayerDevelopmentData;
		var attributes : array< SAttribute >;
		var attributeRecord : Attribute_Record;
		var proficiencies : array< weak< Proficiency_Record > >;
		playerDevelopmentData = PlayerDevelopmentSystem.GetData( m_player );
		attributes = playerDevelopmentData.GetAttributes();
		for( i = 0; i < attributes.Size(); i += 1 )
		{
			attributeRecord = ( ( Attribute_Record )( playerDevelopmentData.GetAttributeRecord( attributes[ i ].attributeName ) ) );
			attributeRecord.Proficiencies( proficiencies );
			for( j = 0; j < proficiencies.Size(); j += 1 )
			{
				if( proficiencies[ j ].Type() == proficiency )
				{
					return proficiencies[ j ];
				}
			}
		}
		return NULL;
	}

	public function GetProficiencyWithData( proficiency : gamedataProficiencyType ) : ProficiencyDisplayData
	{
		var attributeRecord : Attribute_Record;
		var areaIdx, perkIdx : Int32;
		var curProficiency : weak< Proficiency_Record >;
		var curProfDisplayData : ProficiencyDisplayData;
		var curPerkArea : weak< PerkArea_Record >;
		var curPerkAreaDisplayData : AreaDisplayData;
		var curPerkDisplayData : PerkDisplayData;
		var playerDevelopmentData : PlayerDevelopmentData;
		playerDevelopmentData = PlayerDevelopmentSystem.GetData( m_player );
		attributeRecord = GetAttributeRecordFromProficiency( proficiency );
		curProficiency = GetProficiencyRecord( attributeRecord, proficiency );
		curProfDisplayData = GetProficiencyDisplayData( curProficiency.Type(), attributeRecord );
		for( areaIdx = 0; areaIdx < curProficiency.GetPerkAreasCount(); areaIdx += 1 )
		{
			curPerkArea = curProficiency.GetPerkAreasItem( areaIdx );
			curPerkAreaDisplayData = GetAreaDisplayData( curPerkArea.Type(), curProficiency.Type(), attributeRecord, playerDevelopmentData );
			curProfDisplayData.m_areas.PushBack( curPerkAreaDisplayData );
			for( perkIdx = 0; perkIdx < curPerkArea.GetPerksCount(); perkIdx += 1 )
			{
				curPerkDisplayData = GetPerkDisplayData( curPerkArea.GetPerksItem( perkIdx ).Type(), curPerkArea.Type(), curProficiency.Type(), attributeRecord, playerDevelopmentData );
				curPerkAreaDisplayData.m_perks.PushBack( curPerkDisplayData );
			}
		}
		return curProfDisplayData;
	}

	public function GetAttributeData( attributeId : TweakDBID ) : AttributeDisplayData
	{
		var attributeRecord : Attribute_Record;
		var attributeDisplayData : AttributeDisplayData;
		var perkrecord : weak< Perk_Record >;
		var profIdx, areaIdx, perkIdx : Int32;
		var curProficiency : weak< Proficiency_Record >;
		var curProfDisplayData : ProficiencyDisplayData;
		var curPerkArea : weak< PerkArea_Record >;
		var curPerkAreaDisplayData : AreaDisplayData;
		var curPerkDisplayData : PerkDisplayData;
		var playerDevelopmentData : PlayerDevelopmentData;
		playerDevelopmentData = PlayerDevelopmentSystem.GetData( m_player );
		attributeRecord = TweakDBInterface.GetAttributeRecord( attributeId );
		attributeDisplayData = new AttributeDisplayData;
		attributeDisplayData.m_attributeId = attributeId;
		for( profIdx = 0; profIdx < attributeRecord.GetProficienciesCount(); profIdx += 1 )
		{
			curProficiency = attributeRecord.GetProficienciesItem( profIdx );
			curProfDisplayData = GetProficiencyDisplayData( curProficiency.Type(), attributeRecord );
			for( areaIdx = 0; areaIdx < curProficiency.GetPerkAreasCount(); areaIdx += 1 )
			{
				curPerkArea = curProficiency.GetPerkAreasItem( areaIdx );
				curPerkAreaDisplayData = GetAreaDisplayData( curPerkArea.Type(), curProficiency.Type(), attributeRecord, playerDevelopmentData );
				for( perkIdx = 0; perkIdx < curPerkArea.GetPerksCount(); perkIdx += 1 )
				{
					perkrecord = curPerkArea.GetPerksItem( perkIdx );
					curPerkDisplayData = GetPerkDisplayData( perkrecord.Type(), curPerkArea.Type(), curProficiency.Type(), attributeRecord, playerDevelopmentData );
					curPerkAreaDisplayData.m_perks.PushBack( curPerkDisplayData );
				}
				curProfDisplayData.m_areas.PushBack( curPerkAreaDisplayData );
			}
			attributeDisplayData.m_proficiencies.PushBack( curProfDisplayData );
		}
		return attributeDisplayData;
	}

	public function GetAttributes() : array< AttributeData >
	{
		var i : Int32;
		var sAttributeData : array< SAttribute >;
		var attributeDataArray : array< AttributeData >;
		var outData : AttributeData;
		var sAttribute : SAttribute;
		sAttributeData = m_playerDevSystem.GetAttributes( m_player );
		for( i = 0; i < sAttributeData.Size(); i += 1 )
		{
			sAttribute = sAttributeData[ i ];
			outData = new AttributeData;
			FillAttributeData( sAttribute, outData );
			attributeDataArray.PushBack( outData );
		}
		return attributeDataArray;
	}

	public function GetAttributeFromType( attributeType : gamedataStatType ) : AttributeData
	{
		var i : Int32;
		var outData : AttributeData;
		var sAttributeData : array< SAttribute >;
		sAttributeData = m_playerDevSystem.GetAttributes( m_player );
		for( i = 0; i < sAttributeData.Size(); i += 1 )
		{
			if( sAttributeData[ i ].attributeName == attributeType )
			{
				outData = new AttributeData;
				FillAttributeData( sAttributeData[ i ], outData );
				break;
			}
		}
		return outData;
	}

	public function GetAttribute( attributeID : TweakDBID ) : AttributeData
	{
		var i : Int32;
		var outData : AttributeData;
		var sAttributeData : array< SAttribute >;
		sAttributeData = m_playerDevSystem.GetAttributes( m_player );
		for( i = 0; i < sAttributeData.Size(); i += 1 )
		{
			if( sAttributeData[ i ].id == attributeID )
			{
				outData = new AttributeData;
				FillAttributeData( sAttributeData[ i ], outData );
				break;
			}
		}
		return outData;
	}

	public function GetAttributeRecordIDFromEnum( attribute : PerkMenuAttribute ) : TweakDBID
	{
		switch( attribute )
		{
			case PerkMenuAttribute.Body:
				return TweakDBInterface.GetStatRecord( T"BaseStats.Strength" ).GetID();
			case PerkMenuAttribute.Reflex:
				return TweakDBInterface.GetStatRecord( T"BaseStats.Reflexes" ).GetID();
			case PerkMenuAttribute.Technical_Ability:
				return TweakDBInterface.GetStatRecord( T"BaseStats.TechnicalAbility" ).GetID();
			case PerkMenuAttribute.Cool:
				return TweakDBInterface.GetStatRecord( T"BaseStats.Cool" ).GetID();
			case PerkMenuAttribute.Intelligence:
				return TweakDBInterface.GetStatRecord( T"BaseStats.Intelligence" ).GetID();
			case PerkMenuAttribute.Espionage:
				return TweakDBInterface.GetStatRecord( T"BaseStats.Espionage" ).GetID();
		}
		return TDBID.None();
	}

	public function GetAttributeEnumFromRecordID( recordID : TweakDBID ) : PerkMenuAttribute
	{
		if( recordID == T"BaseStats.Strength" )
		{
			return PerkMenuAttribute.Body;
		}
		else if( recordID == T"BaseStats.Reflexes" )
		{
			return PerkMenuAttribute.Reflex;
		}
		else if( recordID == T"BaseStats.TechnicalAbility" )
		{
			return PerkMenuAttribute.Technical_Ability;
		}
		else if( recordID == T"BaseStats.Cool" )
		{
			return PerkMenuAttribute.Cool;
		}
		else if( recordID == T"BaseStats.Intelligence" )
		{
			return PerkMenuAttribute.Intelligence;
		}
		else if( recordID == T"BaseStats.Espionage" )
		{
			return PerkMenuAttribute.Espionage;
		}
		return PerkMenuAttribute.Johnny;
	}

	private function FillAttributeData( attribute : SAttribute, out outData : AttributeData )
	{
		if( attribute.attributeName == gamedataStatType.Espionage )
		{
			outData.label = "LocKey#20997";
		}
		else
		{
			outData.label = TweakDBInterface.GetStatRecord( TDBID.Create( "BaseStats." + EnumValueToString( "gamedataStatType", ( ( Int32 )( attribute.attributeName ) ) ) ) ).LocalizedName();
		}
		outData.value = attribute.value;
		outData.maxValue = 20;
		outData.id = attribute.id;
		outData.availableToUpgrade = outData.value < outData.maxValue;
		outData.type = attribute.attributeName;
		outData.description = TweakDBInterface.GetStatRecord( TDBID.Create( "BaseStats." + EnumValueToString( "gamedataStatType", ( ( Int32 )( attribute.attributeName ) ) ) ) ).LocalizedDescription();
	}

	public function GetSpentPerkPoints() : Int32
	{
		var devData : PlayerDevelopmentData;
		devData = PlayerDevelopmentSystem.GetData( m_player );
		return devData.GetSpentPerkPoints();
	}

	public function GetSpentTraitPoints() : Int32
	{
		var devData : PlayerDevelopmentData;
		devData = PlayerDevelopmentSystem.GetData( m_player );
		return devData.GetSpentTraitPoints();
	}

	public function GetTotalRespecCost() : Int32
	{
		var devData : PlayerDevelopmentData;
		devData = PlayerDevelopmentSystem.GetData( m_player );
		return devData.GetTotalRespecCost();
	}

	public function CheckRespecCost() : Bool
	{
		var devData : PlayerDevelopmentData;
		devData = PlayerDevelopmentSystem.GetData( m_player );
		return devData.CheckPlayerRespecCost();
	}

	public function GetPerkPoints() : Int32
	{
		return m_playerDevSystem.GetDevPoints( m_player, gamedataDevelopmentPointType.Primary );
	}

	public function GetSpyPerkPoints() : Int32
	{
		return m_playerDevSystem.GetDevPoints( m_player, gamedataDevelopmentPointType.Espionage );
	}

	public function GetAttributePoints() : Int32
	{
		return m_playerDevSystem.GetDevPoints( m_player, gamedataDevelopmentPointType.Attribute );
	}

	public function GetPerkLevel( type : gamedataPerkType ) : Int32
	{
		return m_playerDevSystem.GetPerkLevel( m_player, type );
	}

	public static function PerkUtilityToString( utility : gamedataPerkUtility ) : String
	{
		switch( utility )
		{
			case gamedataPerkUtility.ActiveUtility:
				return GetLocalizedText( "UI-ScriptExports-Active" );
			case gamedataPerkUtility.PassiveUtility:
				return GetLocalizedText( "UI-Tooltips-Passive" );
			case gamedataPerkUtility.TriggeredUtility:
				return GetLocalizedText( "UI-Tooltips-Trigger" );
			default:
				return "";
		}
	}

	private function UpdateData()
	{
		var evt : PlayerDevUpdateDataEvent;
		evt = new PlayerDevUpdateDataEvent;
		m_parentGameCtrl.QueueEvent( evt );
	}

	private function NotifyAttributeUpdate( attributeId : TweakDBID )
	{
		var evt : AttributeUpdatedEvent;
		evt = new AttributeUpdatedEvent;
		evt.attributeId = attributeId;
		m_parentGameCtrl.QueueEvent( evt );
	}

	public function UpgradePerk( data : PerkDisplayData )
	{
		var request : BuyPerk;
		request = new BuyPerk;
		request.Set( m_player, data.m_type );
		m_playerDevSystem.QueueRequest( request );
		UpdateData();
	}

	public function UpgradeTrait( data : TraitDisplayData )
	{
		var request : IncreaseTraitLevel;
		request = new IncreaseTraitLevel;
		request.Set( m_player, data.m_type );
		m_playerDevSystem.QueueRequest( request );
		UpdateData();
	}

	public function UpgradeAttribute( data : AttributeData )
	{
		var refresh : RefreshPerkAreas;
		UpgradeAttribute( data.type );
		NotifyAttributeUpdate( data.id );
		refresh = new RefreshPerkAreas;
		refresh.owner = m_player;
		m_playerDevSystem.QueueRequest( refresh );
	}

	public function UpgradeAttribute( type : gamedataStatType )
	{
		var request : BuyAttribute;
		request = new BuyAttribute;
		request.Set( m_player, type );
		m_playerDevSystem.QueueRequest( request );
		UpdateData();
	}

	public function IsPerkUpgradeable( data : BasePerkDisplayData, optional showNotification : Bool ) : Bool
	{
		var newPerkData : NewPerkDisplayData;
		var notificationEvent : UIMenuNotificationEvent;
		var notificationType : UIMenuNotificationType;
		var isPerkUpgradeable : Bool;
		var isEspionage, isEspionageMilestone : Bool;
		if( data.IsA( 'NewPerkDisplayData' ) )
		{
			newPerkData = ( ( NewPerkDisplayData )( data ) );
			isEspionage = IsEspionagePerk( newPerkData.m_type );
			isEspionageMilestone = IsEspionageMilestonePerk( newPerkData.m_type );
		}
		else
		{
			isEspionage = false;
			isEspionageMilestone = false;
		}
		isPerkUpgradeable = true;
		if( isEspionage && ( GetSpyPerkPoints() <= ( ( isEspionageMilestone ) ? ( 2 ) : ( 0 ) ) ) )
		{
			notificationType = UIMenuNotificationType.NoPerksPoints;
			isPerkUpgradeable = false;
		}
		else if( !( isEspionage ) && ( GetPerkPoints() <= 0 ) )
		{
			notificationType = UIMenuNotificationType.NoPerksPoints;
			isPerkUpgradeable = false;
		}
		if( data.m_locked )
		{
			notificationType = UIMenuNotificationType.PerksLocked;
			isPerkUpgradeable = false;
		}
		if( ( data.m_maxLevel > -1 ) && ( data.m_level >= data.m_maxLevel ) )
		{
			notificationType = UIMenuNotificationType.MaxLevelPerks;
			isPerkUpgradeable = false;
		}
		if( !( isPerkUpgradeable ) && showNotification )
		{
			notificationEvent = new UIMenuNotificationEvent;
			notificationEvent.m_notificationType = notificationType;
			GameInstance.GetUISystem( m_player.GetGame() ).QueueEvent( notificationEvent );
		}
		return isPerkUpgradeable;
	}

	public function IsNewPerkRefundable( data : NewPerkDisplayData ) : Bool
	{
		var allParentsSold : Bool;
		allParentsSold = PlayerDevelopmentSystem.GetData( m_player ).CheckIfAllnewPerkParentSold( data.m_type );
		return ( !( data.m_locked ) && ( data.m_level > 0 ) ) && allParentsSold;
	}

	public function HasAvailableAttributePoints( optional showNotification : Bool ) : Bool
	{
		var notificationEvent : UIMenuNotificationEvent;
		var points : Bool;
		points = GetAttributePoints() > 0;
		if( !( points ) && showNotification )
		{
			notificationEvent = new UIMenuNotificationEvent;
			notificationEvent.m_notificationType = UIMenuNotificationType.NoAttributePoints;
			GameInstance.GetUISystem( m_player.GetGame() ).QueueEvent( notificationEvent );
		}
		return points;
	}

	public function IsAttributeUpgradeable( attribute : PerkMenuAttribute ) : Bool
	{
		return PlayerDevelopmentSystem.GetData( m_player ).CanAttributeBeBought( PerkAttributeHelper.PerkMenuAttributeToStat( attribute ) );
	}

	public function IsEspionagePerk( perkType : gamedataNewPerkType ) : Bool
	{
		return PlayerDevelopmentSystem.GetData( m_player ).IsNewPerkEspionage( perkType );
	}

	public function IsEspionageMilestonePerk( perkType : gamedataNewPerkType ) : Bool
	{
		return PlayerDevelopmentSystem.GetData( m_player ).IsEspionageMilestonePerk( perkType );
	}

	public function GetAttributeLevel( attribute : PerkMenuAttribute ) : Int32
	{
		var attributeDataType : gamedataAttributeDataType;
		attributeDataType = PerkAttributeHelper.PerkMenuAttributeToAttributeDataType( attribute );
		return PlayerDevelopmentSystem.GetData( m_player ).GetAttributePoints( attributeDataType );
	}

}

struct AttributeBoughtData
{
	var attribute : gamedataStatType;
	var value : Int32;
}

