void YaraDetector::CallbackSettings::addDetected(YaraRule &rule)
{
	storedDetected.push_back(rule);
}

/**
 * @file src/yaracpp/yara_detector.cpp
 * @brief Interpret of YARA rules.
 */

#include <yara.h>
#include <yara/compiler.h>
#include <yara/types.h>

#include "retdec/yaracpp/yara_detector.h"

namespace retdec {
namespace yaracpp {

namespace {

/**
 * Interface for YARA scanning interface. Uses template specialization
 * to decide whether to scan file or memory buffer.
 */
template <typename T>
struct Scanner {};

/**
 * Specialization for scanning files.
 */
template <>
struct Scanner<std::string>
{
	static bool scan(
			YR_RULES* rules,
			YR_CALLBACK_FUNC callback,
			YaraDetector::CallbackSettings& settings,
			const std::string& pathToFile)
	{
		return yr_rules_scan_file(
				rules,
				pathToFile.c_str(),
				0,
				callback,
				&settings, 0
		) == ERROR_SUCCESS;
	}
};

/**
 * Specialization for scanning memory buffers.
 */
template <>
struct Scanner<std::vector<std::uint8_t>>
{
	static bool scan(
			YR_RULES* rules,
			YR_CALLBACK_FUNC callback,
			YaraDetector::CallbackSettings& settings,
			const std::vector<std::uint8_t>& buffer)
	{
		return yr_rules_scan_mem(
				rules,
				const_cast<uint8_t*>(buffer.data()),
				buffer.size(),
				0,
				callback,
				&settings, 0
		) == ERROR_SUCCESS;
	}
};

/**
 * Interface for Scanner. Provides template type deduction and
 * always passes correct type into Scanner template.
 */
template <typename T>
bool scan(
		YR_RULES* rules,
		YR_CALLBACK_FUNC callback,
		YaraDetector::CallbackSettings& settings,
		T&& value)
{
	return Scanner<std::decay_t<T>>::scan(
			rules,
			callback,
			settings,
			std::forward<T>(value)
	);
}

} // anonymous namespace

/**
 * Constructor
 */
YaraDetector::YaraDetector()
{
	stateIsValid = ((yr_initialize() == ERROR_SUCCESS)
			&& (yr_compiler_create(&compiler) == ERROR_SUCCESS));
	std::uint32_t max_match_data = 65536;
	yr_set_configuration(YR_CONFIG_MAX_MATCH_DATA, &max_match_data);
}

/**
 * Destructor
 */
YaraDetector::~YaraDetector()
{
	for (auto* file : files)
	{
		if (file)
			fclose(file);
	}

	files.clear();
	detectedRules.clear();
	undetectedRules.clear();

	if (compiler)
	{
		yr_compiler_destroy(compiler);
	}

	if (textFilesRules)
		yr_rules_destroy(textFilesRules);

	for (auto* rules : precompiledRules)
	{
		if (rules)
			yr_rules_destroy(rules);
	}

	yr_finalize();
}

/**
 * Constructor of settings class
 * @param cStoreAll If this parameter is set to @c true, all rules will be
 *    stored (not only detected rules)
 * @param cDetected Into this variable detected rules will be stored
 * @param cUndetected Into this variable undetected rules will be stored
 */
YaraDetector::CallbackSettings::CallbackSettings(
		bool cStoreAll,
		std::vector<YaraRule> &cDetected,
		std::vector<YaraRule> &cUndetected)
		: storeAll(cStoreAll)
		, storedDetected(cDetected)
		, storedUndetected(cUndetected)
{

}

/**
 * Add detected rule
 * @param rule Rule to store
 */

/**
 * Add undetected rule
 * @param rule Rule to store
 */
void YaraDetector::CallbackSettings::addUndetected(YaraRule &rule)
{
	storedUndetected.push_back(rule);
}

/**
 * Check if storing of all rules (not only detected) is set
 * @return @c true if storing of all rules is set
 */
bool YaraDetector::CallbackSettings::storeAllRules() const
{
	return storeAll;
}

/**
 * Callback function for scanning of input file
 * @param context YARA context
 * @param message Type of message from libyara
 * @param messageData Content of message
 * @param userData @c Pointer for save information about detected rules
 * @return Instruction for the next scan
 *
 * Read libyara documentation for more detailed information about
 * callback function
 */
int YaraDetector::yaraCallback(
		YR_SCAN_CONTEXT* context,
		int message,
		void *messageData,
		void *userData)
{
	if(message == CALLBACK_MSG_IMPORT_MODULE
			|| message == CALLBACK_MSG_MODULE_IMPORTED)
	{
		return CALLBACK_CONTINUE;
	}
	else if(message == CALLBACK_MSG_SCAN_FINISHED)
	{
		return CALLBACK_ABORT;
	}
	else if(message != CALLBACK_MSG_RULE_MATCHING
			&& message != CALLBACK_MSG_RULE_NOT_MATCHING)
	{
		return CALLBACK_ERROR;
	}

	auto *settings = static_cast<CallbackSettings*>(userData);
	if(!settings)
	{
		return CALLBACK_ERROR;
	}
	else if(!settings->storeAllRules() && message == CALLBACK_MSG_RULE_NOT_MATCHING)
	{
		return CALLBACK_CONTINUE;
	}

	auto *actRule = static_cast<YR_RULE*>(messageData);
	if(!actRule)
	{
		return CALLBACK_ERROR;
	}

	YaraRule actual;
	actual.setName(actRule->identifier);
	YR_META *meta;
	yr_rule_metas_foreach(actRule, meta)
	{
		if(meta)
		{
			YaraMeta yaralMeta;
			yaralMeta.setId(meta->identifier);
			if(meta->type == META_TYPE_STRING)
			{
				yaralMeta.setType(YaraMeta::Type::String);
				yaralMeta.setStringValue(meta->string);
			}
			else
			{
				yaralMeta.setType(YaraMeta::Type::Int);
				yaralMeta.setIntValue(meta->integer);
			}
			actual.addMeta(yaralMeta);
		}
	}

	if(message == CALLBACK_MSG_RULE_MATCHING)
	{
		YR_STRING *string;
		yr_rule_strings_foreach(actRule, string)
		{
			if(string)
			{
				YR_MATCH *match;
				yr_string_matches_foreach(context, string, match)
				{
					if(match)
					{
						YaraMatch yaralMatch;
						yaralMatch.setOffset(match->base + match->offset);
						yaralMatch.setData(match->data, match->data_length);
						actual.addMatch(yaralMatch);
					}
				}
			}
		}
		settings->addDetected(actual);
	}
	else
	{
		settings->addUndetected(actual);
	}

	return CALLBACK_CONTINUE;
}

/**
 * Add text rules to compiler
 * @param string YARA rules to add
 */
bool YaraDetector::addRules(const char *string)
{
	const auto result = yr_compiler_add_string(compiler, string, nullptr);

	needsRecompilation = (result == 0);
	return needsRecompilation;
}

/**
 * Add external file with text rules
 * @param pathToFile Path to rule file
 * @param nameSpace Namespace to use for the given rule file. If the file is
 *                  already compiled, this has no effect. If it is a text file,
 *                  this allows to have multiple rules with the same ID across
 *                  multiple rule files.
 */
bool YaraDetector::addRuleFile(
		const std::string &pathToFile,
		const std::string &nameSpace)
{
	// AT first, try to load the files as precompiled file
	YR_RULES* rules = nullptr;
	if (yr_rules_load(pathToFile.c_str(), &rules) == ERROR_SUCCESS)
	{
		precompiledRules.push_back(rules);
	}
	// If we didn't succeeded consider it as text file
	else
	{
		auto file = fopen(pathToFile.c_str(), "r");
		if (!file)
			return false;

		const char* ns = nameSpace.empty() ? nullptr : nameSpace.c_str();
		if (yr_compiler_add_file(compiler, file, ns, nullptr) != 0)
		{
			fclose(file);
			return false;
		}

		files.push_back(file);
		needsRecompilation = true;
	}

	return true;
}

/**
 * Getter for state of instance
 * @return @c true if all is OK, @c false otherwise
 */
bool YaraDetector::isInValidState() const
{
	return stateIsValid;
}

/**
 * Analyze input file
 * @param pathToInputFile Path to input file
 * @param storeAllRules If this parameter is set to @c true,
 *                      store all rules (not only detected)
 * @return @c true if analysis completed without any error, otherwise @c false.
 */
bool YaraDetector::analyze(
		const std::string &pathToInputFile,
		bool storeAllRules)
{
	return analyzeWithScan(pathToInputFile, storeAllRules);
}

/**
 * Analyze input bytes
 * @param bytes Vector of input bytes
 * @param storeAllRules If this parameter is set to @c true,
 *                      store all rules (not only detected)
 * @return @c true if analysis completed without any error, otherwise @c false.
 */
bool YaraDetector::analyze(std::vector<std::uint8_t> &bytes, bool storeAllRules)
{
	return analyzeWithScan(bytes, storeAllRules);
}

/**
 * Get detected rules
 * @return Detected rules
 */
const std::vector<YaraRule>& YaraDetector::getDetectedRules() const
{
	return detectedRules;
}

/**
 * Get undetected rules
 * @return Undetected rules
 */
const std::vector<YaraRule>& YaraDetector::getUndetectedRules() const
{
	return undetectedRules;
}

/**
 * Analyze input sequence
 * @param value Value to analyze
 * @param storeAllRules If this parameter is set to @c true,
 *                      store all rules (not only detected)
 * @return @c true if analysis completed without any error, otherwise @c false.
 */
template <typename T>
bool YaraDetector::analyzeWithScan(T&& value, bool storeAllRules)
{
	auto settings = CallbackSettings(
			storeAllRules,
			detectedRules,
			undetectedRules
	);

	auto rules = getCompiledRules();
	if (!(rules))
		return false;

	if (!scan(rules, yaraCallback, settings, std::forward<T>(value)))
		return false;

	for (auto* rules : precompiledRules)
	{
		if (!scan(rules, yaraCallback, settings, std::forward<T>(value)))
			return false;
	}

	return true;
}

/**
 * Returns the compiled rules from text files.
 * @return Compiled rules.
 */
YR_RULES* YaraDetector::getCompiledRules()
{
	// File is text file and needs to be compiled first
	// All text files are compiled into single YR_RULES structure and
	// we shouldn't compile it twice if it's not needed
	// analyze() called for the first time or the file was added since the
	// last analyze() call
	if (needsRecompilation)
	{
		YR_RULES* rules = nullptr;
		if (yr_compiler_get_rules(compiler, &rules) != ERROR_SUCCESS)
			return nullptr;

		if (textFilesRules)
			yr_rules_destroy(textFilesRules);

		textFilesRules = rules;
		needsRecompilation = false;
	}

	return textFilesRules;
}

} // namespace yaracpp
} // namespace retdec


CHudElement::CHudElement( const char *pElementName )
{
	m_bActive = false;
	m_iHiddenBits = 0;
	m_pElementName = pElementName;
	SetNeedsRemove( false );
	m_bIsParentedToClientDLLRootPanel = false;

	// Make this for all hud elements, but when its a bit safer
#if defined( TF_CLIENT_DLL ) || defined( DOD_DLL )
	RegisterForRenderGroup( "global" );
#endif
}

// hud.cpp
// implementation of CHud class
#include "cbase.h"
#include "hud_macros.h"
#include "history_resource.h"
#include "iinput.h"
#include "clientmode.h"
#include "in_buttons.h"
#include <vgui_controls/Controls.h>
#include <vgui/ISurface.h>
#include <KeyValues.h>
#include "itextmessage.h"
#include "mempool.h"
#include <KeyValues.h>
#include "filesystem.h"
#include <vgui_controls/AnimationController.h>
#include <vgui/ISurface.h>
#include "hud_lcd.h"

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

static 	CClassMemoryPool< CHudTexture >	 g_HudTextureMemoryPool( 128 );

// Purpose: Parses the weapon txt files to get the sprites needed.
struct HudTextureFileRef
{
	HudTextureFileRef ( const char *cszFileKey, const char *cszHudTexturePrefix )
	{
		Q_strncpy( m_cszFileKey, cszFileKey, kcszFileKeyLength );
		Q_strncpy( m_cszHudTexturePrefix, cszHudTexturePrefix, kcszHudTexturePrefix );
		m_uiPrefixLength = Q_strlen( cszHudTexturePrefix );
		m_fileKeySymbol = KeyValuesSystem()->GetSymbolForString( m_cszFileKey );
		Assert( m_fileKeySymbol != INVALID_KEY_SYMBOL );
	}

	enum { kcszFileKeyLength = 64, };
	enum { kcszHudTexturePrefix = 16, };

	char m_cszFileKey[kcszFileKeyLength];
	char m_cszHudTexturePrefix[kcszHudTexturePrefix];
	unsigned int m_uiPrefixLength;
	HKeySymbol m_fileKeySymbol;
};

void LoadHudTextures( CUtlDict< CHudTexture *, int >& list, const char *szFilenameWithoutExtension, const unsigned char *pICEKey )
{
	KeyValues *pTemp, *pTextureSection;

	KeyValues *pKeyValuesData = ReadEncryptedKVFile( filesystem, szFilenameWithoutExtension, pICEKey );
	if ( pKeyValuesData )
	{
		CUtlVector<HudTextureFileRef> hudTextureFileRefs;

		// By default, add a default entry mapping "file" to no prefix. This will allow earlier-version files
		// to work with no modification.
		hudTextureFileRefs.AddToTail( HudTextureFileRef( "file", "" ) );

		KeyValues *pTextureFileRefs = pKeyValuesData->FindKey( "TextureFileRefs" );
		if ( pTextureFileRefs )
		{
			pTemp = pTextureFileRefs->GetFirstSubKey();
			while ( pTemp )
			{
				hudTextureFileRefs.AddToTail( HudTextureFileRef( pTemp->GetName(), pTemp->GetString( "prefix", "" ) ) );
				pTemp = pTemp->GetNextKey();
			}
		}

		// Read our individual HUD texture data blocks.
		pTextureSection = pKeyValuesData->FindKey( "TextureData" );
		if ( pTextureSection  )
		{
			// Read the sprite data
			pTemp = pTextureSection->GetFirstSubKey();
			while ( pTemp )
			{
				if ( pTemp->GetString( "font", NULL ) )
				{
					CHudTexture *tex = new CHudTexture();

					// Key Name is the sprite name
					Q_strncpy( tex->szShortName, pTemp->GetName(), sizeof( tex->szShortName ) );

					// it's a font-based icon
					tex->bRenderUsingFont = true;
					tex->cCharacterInFont = *(pTemp->GetString("character", ""));
					Q_strncpy( tex->szTextureFile, pTemp->GetString( "font" ), sizeof( tex->szTextureFile ) );

					list.Insert( tex->szShortName, tex );
				}
				else
				{
					int iTexLeft	= pTemp->GetInt( "x", 0 ),
						iTexTop		= pTemp->GetInt( "y", 0 ),
						iTexRight	= pTemp->GetInt( "width", 0 )	+ iTexLeft,
						iTexBottom	= pTemp->GetInt( "height", 0 )	+ iTexTop;

					for ( int i = 0; i < hudTextureFileRefs.Size(); i++ )
					{
						const char *cszFilename = pTemp->GetString( hudTextureFileRefs[i].m_fileKeySymbol, NULL );
						if ( cszFilename )
						{
							CHudTexture *tex = new CHudTexture();

							tex->bRenderUsingFont = false;
							tex->rc.left	= iTexLeft;
							tex->rc.top		= iTexTop;
							tex->rc.right	= iTexRight;
							tex->rc.bottom	= iTexBottom;

							Q_strncpy( tex->szShortName, hudTextureFileRefs[i].m_cszHudTexturePrefix, sizeof( tex->szShortName ) );
							Q_strncpy( tex->szShortName + hudTextureFileRefs[i].m_uiPrefixLength, pTemp->GetName(), sizeof( tex->szShortName ) - hudTextureFileRefs[i].m_uiPrefixLength );
							Q_strncpy( tex->szTextureFile, cszFilename, sizeof( tex->szTextureFile ) );

							list.Insert( tex->szShortName, tex );
						}
					}
				}

				pTemp = pTemp->GetNextKey();
			}
		}
	}

	// Failed for some reason. Delete the Key data and abort.
	pKeyValuesData->deleteThis();
}

void FreeHudTextureList( CUtlDict< CHudTexture *, int >& list )
{
	int c = list.Count();
	for ( int i = 0; i < c; i++ )
	{
		CHudTexture *tex = list[ i ];
		delete tex;
	}
	list.RemoveAll();
}

// Globally-used fonts
vgui::HFont g_hFontTrebuchet24 = vgui::INVALID_FONT;

// Hud Element Visibility handling
typedef struct hudelement_hidden_s
{
	char	*sElementName;
	int		iHiddenBits;	// Bits in which this hud element is hidden
} hudelement_hidden_t;

ConVar hidehud( "hidehud", "0", FCVAR_CHEAT );

CHudTexture::CHudTexture()
{
	Q_memset( szShortName, 0, sizeof( szShortName ) );
	Q_memset( szTextureFile, 0, sizeof( szTextureFile ) );
	Q_memset( texCoords, 0, sizeof( texCoords ) );
	Q_memset( &rc, 0, sizeof( rc ) );
	textureId = -1;
	bRenderUsingFont = false;
	bPrecached = false;
	cCharacterInFont = 0;
	hFont = ( vgui::HFont )NULL;
}

CHudTexture& CHudTexture::operator =( const CHudTexture& src )
{
	if ( this == &src )
		return *this;

	Q_strncpy( szShortName, src.szShortName, sizeof( szShortName ) );
	Q_strncpy( szTextureFile, src.szTextureFile, sizeof( szTextureFile ) );
	Q_memcpy( texCoords, src.texCoords, sizeof( texCoords ) );

	if ( src.textureId == -1 )
	{
		// Didn't have a texture ID set
		textureId = -1;
	}
	else
	{
		// Make a new texture ID that uses the same texture
		textureId = vgui::surface()->CreateNewTextureID();
		vgui::surface()->DrawSetTextureFile( textureId, src.szTextureFile, false, false );
	}

	rc = src.rc;
	bRenderUsingFont = src.bRenderUsingFont;
	cCharacterInFont = src.cCharacterInFont;
	hFont = src.hFont;

	return *this;
}

CHudTexture::~CHudTexture()
{
	if ( vgui::surface() && textureId != -1 )
	{
		vgui::surface()->DestroyTextureID( textureId );
		textureId = -1;
	}
}

//  CHudElement
//	All hud elements are derived from this class.
// Purpose: Registers the hud element in a global list, in CHud

// Purpose: Remove this hud element from the global list in CHUD
CHudElement::~CHudElement()
{
	if ( m_bNeedsRemove )
	{
		gHUD.RemoveHudElement( this );
	}
}

void CHudElement::SetActive( bool bActive )
{
	m_bActive = bActive;
}

void CHudElement::SetNeedsRemove( bool needsremove )
{
	m_bNeedsRemove = needsremove;
}

void CHudElement::SetHiddenBits( int iBits )
{
	m_iHiddenBits = iBits;
}

bool CHudElement::ShouldDraw( void )
{
	bool bShouldDraw = ( !gHUD.IsHidden( m_iHiddenBits ) );

	if ( bShouldDraw )
	{
		// for each render group
		int iNumGroups = m_HudRenderGroups.Count();
		for ( int iGroupIndex = 0; iGroupIndex < iNumGroups; iGroupIndex++ )
		{
			if ( gHUD.IsRenderGroupLockedFor( this, m_HudRenderGroups.Element(iGroupIndex ) ) )
				return false;
		}
	}

	return bShouldDraw;
}

// Output : Returns true on success, false on failure.
bool CHudElement::IsParentedToClientDLLRootPanel() const
{
	return m_bIsParentedToClientDLLRootPanel;
}

void CHudElement::SetParentedToClientDLLRootPanel( bool parented )
{
	m_bIsParentedToClientDLLRootPanel = parented;
}

// Purpose: We can register to be affected by multiple hud render groups
void CHudElement::RegisterForRenderGroup( const char *pszGroupName )
{
	int iGroupIndex = gHUD.RegisterForRenderGroup( pszGroupName );

	// add group index to our list of registered groups
	if ( m_HudRenderGroups.Find( iGroupIndex ) == m_HudRenderGroups.InvalidIndex() )
	{
		m_HudRenderGroups.AddToTail( iGroupIndex );
	}
}

void CHudElement::UnregisterForRenderGroup( const char *pszGroupName )
{
	int iGroupIndex = gHUD.RegisterForRenderGroup( pszGroupName );

	m_HudRenderGroups.FindAndRemove( iGroupIndex );
}

// Purpose: We want to obscure other elements in this group
void CHudElement::HideLowerPriorityHudElementsInGroup( const char *pszGroupName )
{
	// look up the render group
	int iGroupIndex = gHUD.LookupRenderGroupIndexByName( pszGroupName );

	// lock the group
	gHUD.LockRenderGroup( iGroupIndex, this );
}

// Purpose: Stop obscuring other elements in this group
void CHudElement::UnhideLowerPriorityHudElementsInGroup( const char *pszGroupName )
{
	// look up the render group
	int iGroupIndex = gHUD.LookupRenderGroupIndexByName( pszGroupName );

	// unlock the group
	gHUD.UnlockRenderGroup( iGroupIndex, this );
}

int	CHudElement::GetRenderGroupPriority( void )
{
	return 0;
}

CHud gHUD;  // global HUD object

DECLARE_MESSAGE(gHUD, ResetHUD);

#ifdef CSTRIKE_DLL
DECLARE_MESSAGE(gHUD, SendAudio);
#endif

CHud::CHud()
{
	SetDefLessFunc( m_RenderGroups );

	m_flScreenShotTime = -1;
}

// Purpose: This is called every time the DLL is loaded
void CHud::Init( void )
{
	HOOK_HUD_MESSAGE( gHUD, ResetHUD );

#ifdef CSTRIKE_DLL
	HOOK_HUD_MESSAGE( gHUD, SendAudio );
#endif

	InitFonts();

	// Create all the Hud elements
	CHudElementHelper::CreateAllElements();

	gLCD.Init();

	// Initialize all created elements
	for ( int i = 0; i < m_HudList.Size(); i++ )
	{
		m_HudList[i]->Init();
	}

	m_bHudTexturesLoaded = false;

	KeyValues *kv = new KeyValues( "layout" );
	if ( kv )
	{
		if ( kv->LoadFromFile( filesystem, "scripts/HudLayout.res" ) )
		{
			int numelements = m_HudList.Size();

			for ( int i = 0; i < numelements; i++ )
			{
				CHudElement *element = m_HudList[i];

				vgui::Panel *pPanel = dynamic_cast<vgui::Panel*>(element);
				if ( !pPanel )
				{
					Msg( "Non-vgui hud element %s\n", m_HudList[i]->GetName() );
					continue;
				}

				KeyValues *key = kv->FindKey( pPanel->GetName(), false );
				if ( !key )
				{
					Msg( "Hud element '%s' doesn't have an entry '%s' in scripts/HudLayout.res\n", m_HudList[i]->GetName(), pPanel->GetName() );
				}

				// Note:  When a panel is parented to the module root, it's "parent" is returned as NULL.
				if ( !element->IsParentedToClientDLLRootPanel() &&
					 !pPanel->GetParent() )
				{
					DevMsg( "Hud element '%s'/'%s' doesn't have a parent\n", m_HudList[i]->GetName(), pPanel->GetName() );
				}
			}
		}

		kv->deleteThis();
	}

	if ( m_bHudTexturesLoaded )
		return;

	m_bHudTexturesLoaded = true;
	CUtlDict< CHudTexture *, int >	textureList;

	// check to see if we have sprites for this res; if not, step down
	LoadHudTextures( textureList, "scripts/hud_textures", NULL );
	LoadHudTextures( textureList, "scripts/mod_textures", NULL );

	int c = textureList.Count();
	for ( int index = 0; index < c; index++ )
	{
		CHudTexture* tex = textureList[ index ];
		AddSearchableHudIconToList( *tex );
	}

	FreeHudTextureList( textureList );
}

// Purpose: Init Hud global colors
void CHud::InitColors( vgui::IScheme *scheme )
{
	m_clrNormal = scheme->GetColor( "Normal", Color( 255, 208, 64 ,255 ) );
	m_clrCaution = scheme->GetColor( "Caution", Color( 255, 48, 0, 255 ) );
	m_clrYellowish = scheme->GetColor( "Yellowish", Color( 255, 160, 0, 255 ) );
}

// Initializes fonts
void CHud::InitFonts()
{
	vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
	vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( scheme );
	g_hFontTrebuchet24 = pScheme->GetFont("CenterPrintText", true);
}

void CHud::Shutdown( void )
{
	gLCD.Shutdown();

	// Deleting hudlist items can result in them being removed from the same hudlist (m_bNeedsRemove).
	//	So go through and kill the last item until the array is empty.
	while ( m_HudList.Size() > 0 )
	{
		delete m_HudList.Tail();
	}

	m_HudList.Purge();
	m_bHudTexturesLoaded = false;
}

// Purpose: LevelInit's called whenever a new level is starting
void CHud::LevelInit( void )
{
	// Tell all the registered hud elements to LevelInit
	for ( int i = 0; i < m_HudList.Size(); i++ )
	{
		m_HudList[i]->LevelInit();
	}

	// Unhide all render groups
	int iCount = m_RenderGroups.Count();
	for ( int i = 0; i < iCount; i++ )
	{
		CHudRenderGroup *group = m_RenderGroups[ i ];
		group->bHidden = false;
		group->m_pLockingElements.Purge();
	}
}

// Purpose: LevelShutdown's called whenever a level is finishing
void CHud::LevelShutdown( void )
{
	// Tell all the registered hud elements to LevelShutdown
	for ( int i = 0; i < m_HudList.Size(); i++ )
	{
		m_HudList[i]->LevelShutdown();
	}
}

// Purpose: cleans up memory allocated for m_rg* arrays
CHud::~CHud()
{
	int c = m_Icons.Count();
	for ( int i = c - 1; i >= 0; i-- )
	{
		CHudTexture *tex = m_Icons[ i ];
		g_HudTextureMemoryPool.Free( tex );
	}
	m_Icons.Purge();

	c = m_RenderGroups.Count();
	for ( int i = c - 1; i >= 0; i-- )
	{
		CHudRenderGroup *group = m_RenderGroups[ i ];
		m_RenderGroups.RemoveAt(i);
		delete group;
	}
}

void CHudTexture::Precache( void )
{
	// costly function, used selectively on specific hud elements to get font pages built out at load time
	if ( IsX360() && bRenderUsingFont && !bPrecached && hFont != vgui::INVALID_FONT )
	{
		wchar_t wideChars[2];
		wideChars[0] = (wchar_t)cCharacterInFont;
		wideChars[1] = 0;
		vgui::surface()->PrecacheFontCharacters( hFont, wideChars );
		bPrecached = true;
	}
}

void CHudTexture::DrawSelf( int x, int y, const Color& clr ) const
{
	DrawSelf( x, y, Width(), Height(), clr );
}

void CHudTexture::DrawSelf( int x, int y, int w, int h, const Color& clr ) const
{
	if ( bRenderUsingFont )
	{
		vgui::surface()->DrawSetTextFont( hFont );
		vgui::surface()->DrawSetTextColor( clr );
		vgui::surface()->DrawSetTextPos( x, y );
		vgui::surface()->DrawUnicodeChar( cCharacterInFont );
	}
	else
	{
		if ( textureId == -1 )
			return;

		vgui::surface()->DrawSetTexture( textureId );
		vgui::surface()->DrawSetColor( clr );
		vgui::surface()->DrawTexturedSubRect( x, y, x + w, y + h,
			texCoords[ 0 ], texCoords[ 1 ], texCoords[ 2 ], texCoords[ 3 ] );
	}
}

#ifdef GE_DLL
void CHudTexture::DrawSelfRotated( int x, int y, float theta, Color clr /*= Color(255,255,255,255)*/ ) const
{
	DrawSelfRotated( x, y, Width(), Height(), theta, clr );
}

void CHudTexture::DrawSelfRotated( int x, int y, int w, int h, float theta, Color clr /*= Color(255,255,255,255)*/ ) const
{
	// This is only for textures
	if ( bRenderUsingFont || theta == 0 )
	{
		DrawSelf( x, y, w, h, clr );
		return;
	}

	if ( textureId == -1 )
		return;

	float half_w = w/2.0f;
	float half_h = h/2.0f;

	VMatrix pMat;
	pMat.Init( -half_w, -half_h, 0, 0,
				half_w, -half_h, 0, 0,
				half_w,  half_h, 0, 0,
			   -half_w,	 half_h, 0, 0 );

	VMatrix rotMat;
	rotMat.Init( cos(theta), sin(theta), 0, 0,
				-sin(theta), cos(theta), 0, 0,
				 0,			 0,			 0, 0,
				 0,			 0,			 0, 0 );

	VMatrix rMat;
	MatrixMultiply( pMat, rotMat, rMat );

	int x1 = x + half_w;
	int y1 = y + half_h;

	vgui::Vertex_t points[4] =
	{
		vgui::Vertex_t( Vector2D(rMat[0][0]+x1, rMat[0][1]+y1), Vector2D(texCoords[0], texCoords[1]) ),
		vgui::Vertex_t( Vector2D(rMat[1][0]+x1, rMat[1][1]+y1), Vector2D(texCoords[2], texCoords[1]) ),
		vgui::Vertex_t( Vector2D(rMat[2][0]+x1, rMat[2][1]+y1), Vector2D(texCoords[2], texCoords[3]) ),
		vgui::Vertex_t( Vector2D(rMat[3][0]+x1, rMat[3][1]+y1), Vector2D(texCoords[0], texCoords[3]) ),
	};

	vgui::surface()->DrawSetTexture( textureId );
	vgui::surface()->DrawSetColor( clr );
	vgui::surface()->DrawTexturedPolygon( 4, points );
}

void CHudTexture::DrawSelfScaled(int x, int y, float scale, Color clr /* Color(255,255,255,255) */ ) const
{
	DrawSelfScaled( x, y, Width(), Height(), scale, clr );
}

void CHudTexture::DrawSelfScaled( int x, int y, int w, int h, float scale, Color clr /* Color(255,255,255,255) */ ) const
{
	if ( bRenderUsingFont )
	{
		vgui::surface()->DrawSetTextFont( hFont );
		vgui::surface()->DrawSetTextColor( clr );
		vgui::surface()->DrawSetTextPos( x, y );
		vgui::surface()->DrawUnicodeChar( cCharacterInFont );
	}
	else
	{
		if ( textureId == -1 )
			return;

		float half_w = (w / 2.0f) * scale;
		float half_h = (h / 2.0f) * scale;

		int x1 = x + half_w;
		int y1 = y + half_h;

		VMatrix pMat;
		pMat.Init( -half_w, -half_h, 0, 0,
					half_w, -half_h, 0, 0,
					half_w,  half_h, 0, 0,
				   -half_w,	 half_h, 0, 0 );

		vgui::Vertex_t points[4] =
		{
			vgui::Vertex_t( Vector2D(pMat[0][0]+x1, pMat[0][1]+y1), Vector2D(texCoords[0], texCoords[1]) ),
			vgui::Vertex_t( Vector2D(pMat[1][0]+x1, pMat[1][1]+y1), Vector2D(texCoords[2], texCoords[1]) ),
			vgui::Vertex_t( Vector2D(pMat[2][0]+x1, pMat[2][1]+y1), Vector2D(texCoords[2], texCoords[3]) ),
			vgui::Vertex_t( Vector2D(pMat[3][0]+x1, pMat[3][1]+y1), Vector2D(texCoords[0], texCoords[3]) ),
		};

		vgui::surface()->DrawSetTexture( textureId );
		vgui::surface()->DrawSetColor( clr );
		vgui::surface()->DrawTexturedPolygon( 4, points );
	}
}

void CHudTexture::DrawScew_Left( int tex2, int width, int height, float percent, int offset, int radius ) const
{
	//	1. The image will be centered vertically about the midline of the screen
	//	2. The X coordinate will be the midpoint.x - offset - width
	//	3. The other half of the image will be defined in tex2 to save calculations
	//	4. The edges of the image will be the pivot points for intersections that don't hit the far side

	// Make sure the percent cut is between 0 and 1
	percent = clamp( percent, 0.0f, 1.0f );
	vgui::surface()->DrawSetTexture( textureId );
	vgui::surface()->DrawSetColor( Color(255,255,255,255) );

	// This defines the placement of the U,V on the image
	float scaledV1, scaledV2, scaledU;
	// These next two values define the left x,y position and the right x,y position that the bisecting
	// line cross the image at in screen space. x3 is used when we clamp to the bounds to keep a crisp line
	int x1, y1, x2, y2, x3;
	int midX, midY, origX;
	int midH = height / 2;

	vgui::surface()->GetScreenSize( midX, midY );
	midX /= 2; midY /= 2;
	// Determine the proper origin X position for a perfect circle based on our image
	origX = midX - offset + midH;

	// If we aren't doing ANY scaling then skip unnecessary calcs
	if ( percent == 1.0f )
	{
		// Only show the containing texture
		x1 = midX - offset - width;
		y1 = midY - midH;
		vgui::surface()->DrawTexturedRect( x1, y1, x1 + width, y1 + height );
		return;
	}
	else if ( percent == 0.0f )
	{
		// Only show the texture thats supposed to be behind
		x1 = midX - offset - width;
		y1 = midY - midH;
		if ( vgui::surface()->IsTextureIDValid( tex2 ) )
		{
			vgui::surface()->DrawSetTexture( tex2 );
			vgui::surface()->DrawTexturedRect( x1, y1, x1 + width, y1 + height );
		}
		return;
	}

	// Find our angle coefficient
	float ang;
	if ( percent >= 0.5f )
		percent = RemapValClamped( percent, 0.5, 1.0, 0.0, 1.0 );
	else
		percent = RemapValClamped( percent, 0.0, 0.5, -1.0, 0.0 );

	ang = tan( percent );

	// First let's calculate our intersection points of the scew line to the image in world space
	x1 = midX - offset - width;
	y1 = clamp( midY - ang * (radius), midY-midH, midY+midH );

	x2 = midX - offset;
	y2 = clamp( midY - ang * (radius-width), midY-midH, midY+midH );

	if ( y1 == (midY - midH) )
		x3 = x2 - (y2 - (midY - midH)) * tan( M_PI/2.0f - percent );
	else if ( y1 == (midY + midH) )
		x3 = x2 - (y2 - (midY + midH)) * tan( M_PI/2.0f - percent );
	else
		x3 = x1;

	scaledU = 1.0f - (float)(width - (x3 - x1)) / (float)width;

	// Now calculate our v1, v2 coords by taking the ratio of the difference in the intersection
	// Y points and the full image location
	scaledV1 = 1.0f - (float)(height - (y1 - (midY - midH))) / (float)height;
	scaledV2 = 1.0f - (float)(height - (y2 - (midY - midH))) / (float)height;

	if ( y1 == (midY + midH) )
	{
		// If we are clamped to the bottom of our image, we need to draw a triangle to avoid stretching UV
		vgui::Vertex_t points[3] =
		{
			vgui::Vertex_t( Vector2D(x3, y1), Vector2D(scaledU, scaledV1) ),
			vgui::Vertex_t( Vector2D(x2, y2), Vector2D(1, scaledV2) ),
			vgui::Vertex_t( Vector2D(x2, midY + midH), Vector2D(1,1) ),
		};
		// Draw it on the screen
		vgui::surface()->DrawTexturedPolygon( 3, points );
	}
	else
	{
		// Now populate our polygon with points
		vgui::Vertex_t points[5] =
		{
			vgui::Vertex_t( Vector2D(x1, y1), Vector2D(0, scaledV1) ),
			vgui::Vertex_t( Vector2D(x3, y1), Vector2D(scaledU, scaledV1) ),
			vgui::Vertex_t( Vector2D(x2, y2), Vector2D(1, scaledV2) ),
			vgui::Vertex_t( Vector2D(x2, midY + midH), Vector2D(1,1) ),
			vgui::Vertex_t( Vector2D(x1, midY + midH), Vector2D(0,1) ),
		};
		// Draw it on the screen
		vgui::surface()->DrawTexturedPolygon( 5, points );
	}

/*	// Debugging line
#ifdef _DEBUG
	vgui::surface()->DrawSetColor( 0, 255, 0, 255 );
	vgui::surface()->DrawLine( x1, y1, x3, y1 );
	vgui::surface()->DrawSetColor( 255, 0, 0, 255 );
	vgui::surface()->DrawLine( x3, y1, x2, y2 );
	vgui::surface()->DrawSetColor( 0, 0, 255, 255 );
	vgui::surface()->DrawOutlinedRect( x1, min(y1,y2), x2, midY + midH );
#endif
*/
	if ( vgui::surface()->IsTextureIDValid( tex2 ) )
	{
		// Now draw the inverse image
		vgui::Vertex_t invPoints[5] =
		{
			vgui::Vertex_t( Vector2D(x1, midY - midH), Vector2D(0, 0) ),
			vgui::Vertex_t( Vector2D(x2, midY - midH), Vector2D(1, 0) ),
			vgui::Vertex_t( Vector2D(x2, y2), Vector2D(1, scaledV2) ),
			vgui::Vertex_t( Vector2D(x3, y1), Vector2D(scaledU, scaledV1) ),
			vgui::Vertex_t( Vector2D(x1, y1), Vector2D(0, scaledV1) ),
		};
		// Draw it on the screen
		vgui::surface()->DrawSetTexture( tex2 );
		vgui::surface()->DrawSetColor( 255, 255, 255, 255 );
		vgui::surface()->DrawTexturedPolygon( 5, invPoints );
	}
}

void CHudTexture::DrawScew_Right( int tex2, int width, int height, float percent, int offset, int radius ) const
{
	//	1. The image will be centered vertically about the midline of the screen
	//	2. The X coordinate will be the midpoint.x + offset
	//	3. The other half of the image will be defined in tex2 to save calculations
	//	4. The edges of the image will be the pivot points for intersections that don't hit the far side

	// Make sure the percent cut is between 0 and 1
	percent = clamp( percent, 0.0f, 1.0f );
	vgui::surface()->DrawSetTexture( textureId );
	vgui::surface()->DrawSetColor( Color(255,255,255,255) );

	// This defines the placement of the U,V on the image
	float scaledV1, scaledV2, scaledU;
	// These next two values define the left x,y position and the right x,y position that the bisecting
	// line cross the image at in screen space. x3 is used when we clamp to the bounds to keep a crisp line
	int x1, y1, x2, y2, x3;
	int midX, midY;
	int midH = height / 2;

	vgui::surface()->GetScreenSize( midX, midY );
	midX /= 2; midY /= 2;

	// If we aren't doing ANY scaling then skip unnecessary calcs
	if ( percent == 1.0f )
	{
		// Only show the containing texture
		x1 = midX + offset;
		y1 = midY - midH;
		vgui::surface()->DrawTexturedRect( x1, y1, x1 + width, y1 + height );
		return;
	}
	else if ( percent == 0.0f )
	{
		// Only show the texture thats supposed to be behind
		x1 = midX + offset;
		y1 = midY - midH;
		if ( vgui::surface()->IsTextureIDValid( tex2 ) )
		{
			vgui::surface()->DrawSetTexture( tex2 );
			vgui::surface()->DrawTexturedRect( x1, y1, x1 + width, y1 + height );
		}
		return;
	}

	// Find our angle coefficient
	float ang;
	if ( percent >= 0.5f )
		percent = RemapValClamped( percent, 0.5, 1.0, 0.0, 1.0 );
	else
		percent = RemapValClamped( percent, 0.0, 0.5, -1.0, 0.0 );

	ang = tan( percent );

	// First let's calculate our intersection points of the scew line to the image in world space
	// Note: X1 is closest to middle now! (opposite of LeftScew)
	x1 = midX + offset;
	y1 = clamp( midY - ang * (radius-width), midY-midH, midY+midH );

	x2 = midX + offset + width;
	y2 = clamp( midY - ang * radius, midY-midH, midY+midH );

	if ( y2 == (midY - midH) )
		x3 = x1 + (y1 - (midY - midH)) * tan( M_PI/2 - percent );
	else if ( y2 == (midY + midH) )
		x3 = x1 + (y1 - (midY + midH)) * tan( M_PI/2 - percent );
	else
		x3 = x2;

	scaledU = 1.0f - (float)(x2 - x3) / (float)width;

	// Now calculate our v1, v2 coords by taking the ratio of the difference in the intersection
	// Y points and the full image location
	scaledV1 = 1.0f - (float)(height - (y1 - (midY - midH))) / (float)height;
	scaledV2 = 1.0f - (float)(height - (y2 - (midY - midH))) / (float)height;

	if ( y2 == (midY + midH) )
	{
		// Since we are at the bottom of the image we can ignore X2's coords
		// otherwise we get tearing in our UV mapping
		vgui::Vertex_t points[3] =
		{
			vgui::Vertex_t( Vector2D(x1, y1), Vector2D(0, scaledV1) ),
			vgui::Vertex_t( Vector2D(x3, y2), Vector2D(scaledU, scaledV2) ),
			vgui::Vertex_t( Vector2D(x1, midY + midH), Vector2D(0,1) ),
		};
		// Draw it on the screen
		vgui::surface()->DrawTexturedPolygon( 3, points );
	}
	else
	{
		// Now populate our polygon with points
		vgui::Vertex_t points[5] =
		{
			vgui::Vertex_t( Vector2D(x1, y1), Vector2D(0, scaledV1) ),
			vgui::Vertex_t( Vector2D(x3, y2), Vector2D(scaledU, scaledV2) ),
			vgui::Vertex_t( Vector2D(x2, y2), Vector2D(1, scaledV2) ),
			vgui::Vertex_t( Vector2D(x2, midY + midH), Vector2D(1,1) ),
			vgui::Vertex_t( Vector2D(x1, midY + midH), Vector2D(0,1) ),
		};
		// Draw it on the screen
		vgui::surface()->DrawTexturedPolygon( 5, points );
	}

/*	// Debugging line
#ifdef _DEBUG
	vgui::surface()->DrawSetColor( 0, 255, 0, 255 );
	vgui::surface()->DrawLine( x2, y2, x3, y2 );
	vgui::surface()->DrawSetColor( 255, 0, 0, 255 );
	vgui::surface()->DrawLine( x3, y2, x1, y1 );
	vgui::surface()->DrawSetColor( 0, 0, 255, 255 );
	vgui::surface()->DrawOutlinedRect( x1, min(y1,y2), x2, midY + midH );
#endif
*/
	if ( vgui::surface()->IsTextureIDValid( tex2 ) )
	{
		// Now draw the inverse image
		vgui::Vertex_t invPoints[5] =
		{
			vgui::Vertex_t( Vector2D(x1, midY - midH), Vector2D(0, 0) ),
			vgui::Vertex_t( Vector2D(x2, midY - midH), Vector2D(1, 0) ),
			vgui::Vertex_t( Vector2D(x2, y2), Vector2D(1, scaledV2) ),
			vgui::Vertex_t( Vector2D(x3, y2), Vector2D(scaledU, scaledV2) ),
			vgui::Vertex_t( Vector2D(x1, y1), Vector2D(0, scaledV1) ),
		};
		// Draw it on the screen
		vgui::surface()->DrawSetTexture( tex2 );
		vgui::surface()->DrawSetColor( 255, 255, 255, 255 );
		vgui::surface()->DrawTexturedPolygon( 5, invPoints );
	}
}
#endif

void CHudTexture::DrawSelfCropped( int x, int y, int cropx, int cropy, int cropw, int croph, int finalWidth, int finalHeight, Color clr ) const
{
	if ( bRenderUsingFont )
	{
		// work out how much we've been cropped
		int height = vgui::surface()->GetFontTall( hFont );
		float frac = (height - croph) / (float)height;
		y -= cropy;

		vgui::surface()->DrawSetTextFont( hFont );
		vgui::surface()->DrawSetTextColor( clr );
		vgui::surface()->DrawSetTextPos( x, y );

		vgui::CharRenderInfo info;
		if ( vgui::surface()->DrawGetUnicodeCharRenderInfo( cCharacterInFont, info ) )
		{
			if ( cropy )
			{
				info.verts[0].m_Position.y = Lerp( frac, info.verts[0].m_Position.y, info.verts[1].m_Position.y );
				info.verts[0].m_TexCoord.y = Lerp( frac, info.verts[0].m_TexCoord.y, info.verts[1].m_TexCoord.y );
			}
			else if ( croph != height )
			{
				info.verts[1].m_Position.y = Lerp( 1.0f - frac, info.verts[0].m_Position.y, info.verts[1].m_Position.y );
				info.verts[1].m_TexCoord.y = Lerp( 1.0f - frac, info.verts[0].m_TexCoord.y, info.verts[1].m_TexCoord.y );
			}
			vgui::surface()->DrawRenderCharFromInfo(info);
		}
	}
	else
	{
		if ( textureId == -1 )
			return;

		float fw = (float)Width();
		float fh = (float)Height();

		float twidth	= texCoords[ 2 ] - texCoords[ 0 ];
		float theight	= texCoords[ 3 ] - texCoords[ 1 ];

		// Interpolate coords
		float tCoords[ 4 ];
		tCoords[ 0 ] = texCoords[ 0 ] + ( (float)cropx / fw ) * twidth;
		tCoords[ 1 ] = texCoords[ 1 ] + ( (float)cropy / fh ) * theight;
		tCoords[ 2 ] = texCoords[ 0 ] + ( (float)(cropx + cropw ) / fw ) * twidth;
		tCoords[ 3 ] = texCoords[ 1 ] + ( (float)(cropy + croph ) / fh ) * theight;

		vgui::surface()->DrawSetTexture( textureId );
		vgui::surface()->DrawSetColor( clr );
		vgui::surface()->DrawTexturedSubRect(
			x, y,
			x + finalWidth, y + finalHeight,
			tCoords[ 0 ], tCoords[ 1 ],
			tCoords[ 2 ], tCoords[ 3 ] );
	}
}

void CHudTexture::DrawSelfCropped( int x, int y, int cropx, int cropy, int cropw, int croph, Color clr ) const
{
	DrawSelfCropped( x, y, cropx, cropy, cropw, croph, cropw, croph, clr );
}

// Purpose: returns width of texture with scale factor applied.  (If rendered
//			using font, scale factor is ignored.)
int CHudTexture::EffectiveWidth( float flScale ) const
{
	if ( !bRenderUsingFont )
	{
		return (int) ( Width() * flScale );
	}
	else
	{
		return vgui::surface()->GetCharacterWidth( hFont, cCharacterInFont );
	}
}

// Purpose: returns height of texture with scale factor applied.  (If rendered
//			using font, scale factor is ignored.)
int CHudTexture::EffectiveHeight( float flScale ) const
{
	if ( !bRenderUsingFont )
	{
		return (int) ( Height() * flScale );
	}
	else
	{
		return vgui::surface()->GetFontAscent( hFont, cCharacterInFont );
	}
}

// Purpose: Gets texture handles for the hud icon
void CHud::SetupNewHudTexture( CHudTexture *t )
{
	if ( t->bRenderUsingFont )
	{
		vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
		t->hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( t->szTextureFile, true );
		t->rc.top = 0;
		t->rc.left = 0;
		t->rc.right = vgui::surface()->GetCharacterWidth( t->hFont, t->cCharacterInFont );
		t->rc.bottom = vgui::surface()->GetFontTall( t->hFont );
	}
	else
	{
		// Set up texture id and texture coordinates
		t->textureId = vgui::surface()->CreateNewTextureID();
		vgui::surface()->DrawSetTextureFile( t->textureId, t->szTextureFile, false, false );

		int wide, tall;
		vgui::surface()->DrawGetTextureSize( t->textureId, wide, tall );

		t->texCoords[ 0 ] = (float)(t->rc.left + 0.5f) / (float)wide;
		t->texCoords[ 1 ] = (float)(t->rc.top + 0.5f) / (float)tall;
		t->texCoords[ 2 ] = (float)(t->rc.right - 0.5f) / (float)wide;
		t->texCoords[ 3 ] = (float)(t->rc.bottom - 0.5f) / (float)tall;
	}
}

CHudTexture *CHud::AddUnsearchableHudIconToList( CHudTexture& texture )
{
	// These names are composed based on the texture file name
	char composedName[ 512 ];

	if ( texture.bRenderUsingFont )
	{
		Q_snprintf( composedName, sizeof( composedName ), "%s_c%i",
			texture.szTextureFile, texture.cCharacterInFont );
	}
	else
	{
		Q_snprintf( composedName, sizeof( composedName ), "%s_%i_%i_%i_%i",
			texture.szTextureFile, texture.rc.left, texture.rc.top, texture.rc.right, texture.rc.bottom );
	}

	CHudTexture *icon = GetIcon( composedName );
	if ( icon )
	{
		return icon;
	}

	CHudTexture *newTexture = ( CHudTexture * )g_HudTextureMemoryPool.Alloc();
	*newTexture = texture;

	SetupNewHudTexture( newTexture );

	int idx = m_Icons.Insert( composedName, newTexture );
	return m_Icons[ idx ];
}

CHudTexture *CHud::AddSearchableHudIconToList( CHudTexture& texture )
{
	CHudTexture *icon = GetIcon( texture.szShortName );
	if ( icon )
	{
		return icon;
	}

	CHudTexture *newTexture = ( CHudTexture * )g_HudTextureMemoryPool.Alloc();
	*newTexture = texture;

	SetupNewHudTexture( newTexture );

	int idx = m_Icons.Insert( texture.szShortName, newTexture );
	return m_Icons[ idx ];
}

// Purpose: returns a pointer to an icon in the list
CHudTexture *CHud::GetIcon( const char *szIcon )
{
	int i = m_Icons.Find( szIcon );
	if ( i == m_Icons.InvalidIndex() )
		return NULL;

	return m_Icons[ i ];
}

void CHud::RefreshHudTextures()
{
	if ( !m_bHudTexturesLoaded )
	{
		Assert( 0 );
		return;
	}

	CUtlDict< CHudTexture *, int >	textureList;

	// check to see if we have sprites for this res; if not, step down
	LoadHudTextures( textureList, "scripts/hud_textures", NULL );
	LoadHudTextures( textureList, "scripts/mod_textures", NULL );

	// fix up all the texture icons first
	int c = textureList.Count();
	for ( int index = 0; index < c; index++ )
	{
		CHudTexture *tex = textureList[ index ];
		Assert( tex );

		CHudTexture *icon = GetIcon( tex->szShortName );
		if ( !icon )
			continue;

		// Update file
		Q_strncpy( icon->szTextureFile, tex->szTextureFile, sizeof( icon->szTextureFile ) );

		if ( !icon->bRenderUsingFont )
		{
			// Update subrect
			icon->rc = tex->rc;

			// Keep existing texture id, but now update texture file and texture coordinates
			vgui::surface()->DrawSetTextureFile( icon->textureId, icon->szTextureFile, false, false );

			// Get new texture dimensions in case it changed
			int wide, tall;
			vgui::surface()->DrawGetTextureSize( icon->textureId, wide, tall );

			// Assign coords
			icon->texCoords[ 0 ] = (float)(icon->rc.left + 0.5f) / (float)wide;
			icon->texCoords[ 1 ] = (float)(icon->rc.top + 0.5f) / (float)tall;
			icon->texCoords[ 2 ] = (float)(icon->rc.right - 0.5f) / (float)wide;
			icon->texCoords[ 3 ] = (float)(icon->rc.bottom - 0.5f) / (float)tall;
		}
	}

	FreeHudTextureList( textureList );

	// fixup all the font icons
	vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
	for (int i = m_Icons.First(); m_Icons.IsValidIndex(i); i = m_Icons.Next(i))
	{
		CHudTexture *icon = m_Icons[i];
		if ( !icon )
			continue;

		// Update file
		if ( icon->bRenderUsingFont )
		{
			icon->hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( icon->szTextureFile, true );
			icon->rc.top = 0;
			icon->rc.left = 0;
			icon->rc.right = vgui::surface()->GetCharacterWidth( icon->hFont, icon->cCharacterInFont );
			icon->rc.bottom = vgui::surface()->GetFontTall( icon->hFont );
		}
	}
}

void CHud::OnRestore()
{
	ResetHUD();
}

void CHud::VidInit( void )
{
	for ( int i = 0; i < m_HudList.Size(); i++ )
	{
		m_HudList[i]->VidInit();
	}

	ResetHUD();
}

CHudElement *CHud::FindElement( const char *pName )
{
	for ( int i = 0; i < m_HudList.Size(); i++ )
	{
		if ( V_stricmp( m_HudList[i]->GetName(), pName ) == 0 )
			return m_HudList[i];
	}

	DevWarning(1, "Could not find Hud Element: %s\n", pName );
	Assert(0);
	return NULL;
}

// Purpose: Adds a member to the HUD
void CHud::AddHudElement( CHudElement *pHudElement )
{
	// Add the hud element to the end of the array
	m_HudList.AddToTail( pHudElement );

	pHudElement->SetNeedsRemove( true );
}

// Purpose: Remove an element from the HUD
void CHud::RemoveHudElement( CHudElement *pHudElement )
{
	m_HudList.FindAndRemove( pHudElement );
}

// Purpose: Returns current mouse sensitivity setting
float CHud::GetSensitivity( void )
{
#ifndef _X360
	return m_flMouseSensitivity;
#else
	return 1.0f;
#endif
}

float CHud::GetFOVSensitivityAdjust()
{
	return m_flFOVSensitivityAdjust;
}
// Purpose: Return true if the passed in sections of the HUD shouldn't be drawn
bool CHud::IsHidden( int iHudFlags )
{
	// Not in game?
	if ( !engine->IsInGame() )
		return true;

	// No local player yet?
	C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
	if ( !pPlayer )
		return true;

	// Get current hidden flags
	int iHideHud = pPlayer->m_Local.m_iHideHUD;
	if ( hidehud.GetInt() )
	{
		iHideHud = hidehud.GetInt();
	}

	// Everything hidden?
	if ( iHideHud & HIDEHUD_ALL )
		return true;

	// Local player dead?
	if ( ( iHudFlags & HIDEHUD_PLAYERDEAD ) && ( pPlayer->GetHealth() <= 0 && !pPlayer->IsAlive() ) )
		return true;

	// Need the HEV suit ( HL2 )
	if ( ( iHudFlags & HIDEHUD_NEEDSUIT ) && ( !pPlayer->IsSuitEquipped() ) )
		return true;

	// Hide all HUD elements during screenshot if the user's set hud_freezecamhide ( TF2 )
#if defined( TF_CLIENT_DLL )
	extern bool IsTakingAFreezecamScreenshot();
	extern ConVar hud_freezecamhide;

	if ( IsTakingAFreezecamScreenshot() && hud_freezecamhide.GetBool() )
		return true;
#endif

	return ( ( iHudFlags & iHideHud ) != 0);
}

// Purpose: Allows HUD to modify input data
void CHud::ProcessInput( bool bActive )
{
	if ( bActive )
	{
		m_iKeyBits = input->GetButtonBits( 0 );

		// Weaponbits need to be sent down as a UserMsg now.
		gHUD.Think();
	}
}

int CHud::LookupRenderGroupIndexByName( const char *pszGroupName )
{
	return m_RenderGroupNames.Find( pszGroupName );
}

// Purpose: A hud element wants to lock this render group so other panels in the
// group do not draw
bool CHud::LockRenderGroup( int iGroupIndex, CHudElement *pLocker /* = NULL */ )
{
	if ( !DoesRenderGroupExist(iGroupIndex) )
		return false;

	int i = m_RenderGroups.Find( iGroupIndex );

	Assert( m_RenderGroups.IsValidIndex(i) );

	CHudRenderGroup *group = m_RenderGroups.Element(i);

	Assert( group );

	if ( group )
	{
		// NULL pLocker means some higher power is globally hiding this group
		if ( pLocker == NULL )
		{
			group->bHidden = true;
		}
		else
		{
			bool bFound = false;
			// See if we have it locked already
			int iNumLockers = group->m_pLockingElements.Count();
			for ( int i=0;i<iNumLockers;i++ )
			{
				if ( pLocker == group->m_pLockingElements.Element(i) )
				{
					bFound = true;
					break;
				}
			}

			// otherwise lock us
			if ( !bFound )
				group->m_pLockingElements.Insert( pLocker );
		}

		return true;
	}

	return false;
}

// Purpose: A hud element wants to release the lock on this render group
bool CHud::UnlockRenderGroup( int iGroupIndex, CHudElement *pLocker /* = NULL */ )
{
	if ( !DoesRenderGroupExist(iGroupIndex) )
		return false;

	int i = m_RenderGroups.Find( iGroupIndex );

	Assert( m_RenderGroups.IsValidIndex(i) );

	CHudRenderGroup *group = m_RenderGroups.Element(i);

	if ( group )
	{
		// NULL pLocker means some higher power is globally hiding this group
		if ( group->bHidden && pLocker == NULL )
		{
			group->bHidden = false;
			return true;
		}

		int iNumLockers = group->m_pLockingElements.Count();
		for ( int i=0;i<iNumLockers;i++ )
		{
			if ( pLocker == group->m_pLockingElements.Element(i) )
			{
				group->m_pLockingElements.RemoveAt( i );
				return true;
			}
		}
	}

	return false;
}

// Purpose: See if we should draw based on a hud render group
//			Return true if this group is locked, hud elem will be hidden
bool CHud::IsRenderGroupLockedFor( CHudElement *pHudElement, int iGroupIndex )
{
	if ( !DoesRenderGroupExist(iGroupIndex) )
		return false;

	int i = m_RenderGroups.Find( iGroupIndex );

	Assert( m_RenderGroups.IsValidIndex(i) );

	CHudRenderGroup *group = m_RenderGroups.Element(i);

	if ( !group )
		return false;

	if ( group->bHidden )
		return true;

	if ( group->m_pLockingElements.Count() == 0 )
		return false;

	if ( !pHudElement )
		return true;

	CHudElement *pLocker = group->m_pLockingElements.ElementAtHead();

	return ( pLocker != pHudElement && pLocker->GetRenderGroupPriority() > pHudElement->GetRenderGroupPriority() );
}

// Purpose: CHudElements can ask for the index of hud element render groups
//			returns a group index
int CHud::RegisterForRenderGroup( const char *pszGroupName )
{
	int iGroupNameIndex = m_RenderGroupNames.Find( pszGroupName );

	if ( iGroupNameIndex != m_RenderGroupNames.InvalidIndex() )
	{
		return iGroupNameIndex;
	}

	// otherwise add the group
	return AddHudRenderGroup( pszGroupName );
}

// Purpose: Create a new hud render group
//			returns a group index
int CHud::AddHudRenderGroup( const char *pszGroupName )
{
	// we tried to register for a group but didn't find it, add a new one

	int iGroupNameIndex = m_RenderGroupNames.AddToTail( pszGroupName );

	CHudRenderGroup *group = new CHudRenderGroup();
	return m_RenderGroups.Insert( iGroupNameIndex, group );
}

bool CHud::DoesRenderGroupExist( int iGroupIndex )
{
	return ( m_RenderGroups.Find( iGroupIndex ) != m_RenderGroups.InvalidIndex() );
}

// Purpose: Allows HUD to Think and modify input data
// Output : int - 1 if there were changes, 0 otherwise
void CHud::UpdateHud( bool bActive )
{
	// clear the weapon bits.
	gHUD.m_iKeyBits &= (~(IN_WEAPON1|IN_WEAPON2));

	g_pClientMode->Update();

	gLCD.Update();
}

// Purpose: Force a Hud UI anim to play
CON_COMMAND_F( testhudanim, "Test a hud element animation.\n\tArguments: <anim name>\n", FCVAR_CHEAT )
{
	if ( args.ArgC() != 2 )
	{
		Msg("Usage:\n   testhudanim <anim name>\n");
		return;
	}

	g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( args[1] );
}


bool isBitSet(UINT8 bits[], UINT32 bit)
	{
		return ((bits[bit/8] >> (bit%8)) & 1) != 0;
	}

#include "Input/BsInput.h"
#include "Private/Linux/BsLinuxInput.h"
#include "Input/BsMouse.h"
#include "Input/BsKeyboard.h"
#include "Input/BsGamepad.h"
#include <fcntl.h>
#include <linux/input.h>

namespace bs
{
	/** Information about events reported from a specific input event device. */
	struct EventInfo
	{
		Vector<INT32> buttons;
		Vector<INT32> relAxes;
		Vector<INT32> absAxes;
		Vector<INT32> hats;
	};

	/** Checks is the bit at the specified location in a byte array is set. */

	/** Returns information about an input event device attached to he provided file handle. */
	bool getEventInfo(int fileHandle, EventInfo& eventInfo)
	{
		UINT8 eventBits[1 + EV_MAX/8];
		bs_zero_out(eventBits);

		if (ioctl(fileHandle, EVIOCGBIT(0, sizeof(eventBits)), eventBits) == -1)
			return false;

		for (UINT32 i = 0; i < EV_MAX; i++)
		{
			if(isBitSet(eventBits, i))
			{
				if(i == EV_ABS)
				{
					UINT8 absAxisBits[1 + ABS_MAX/8];
					bs_zero_out(absAxisBits);

					if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(absAxisBits)), absAxisBits) == -1)
					{
						BS_LOG(Error, Platform, "Could not read device absolute axis features.");
						continue;
					}

					for (UINT32 j = 0; j < ABS_MAX; j++)
					{
						if(isBitSet(absAxisBits, j))
						{
							if(j >= ABS_HAT0X && j <= ABS_HAT3Y)
								eventInfo.hats.push_back(j);
							else
								eventInfo.absAxes.push_back(j);
						}
					}
				}
				else if(i == EV_REL)
				{
					UINT8 relAxisBits[1 + REL_MAX/8];
					bs_zero_out(relAxisBits);

					if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(relAxisBits)), relAxisBits) == -1)
					{
						BS_LOG(Error, Platform, "Could not read device relative axis features.");
						continue;
					}

					for (UINT32 j = 0; j < REL_MAX; j++)
					{
						if(isBitSet(relAxisBits, j))
							eventInfo.relAxes.push_back(j);
					}
				}
				else if(i == EV_KEY)
				{
					UINT8 keyBits[1 + KEY_MAX/8];
					bs_zero_out(keyBits);

					if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(keyBits)), keyBits) == -1)
					{
						BS_LOG(Error, Platform, "Could not read device key features.");
						continue;
					}

					for (UINT32 j = 0; j < KEY_MAX; j++)
					{
						if(isBitSet(keyBits, j))
							eventInfo.buttons.push_back(j);
					}
				}
			}
		}

		return true;
	}

	/** Converts a Linux button code to Banshee ButtonCode. */
	ButtonCode gamepadMapCommonButton(INT32 code)
	{
		// Note: Assuming XBox controller layout here
		switch (code)
		{
		case BTN_TRIGGER_HAPPY1:
			return BC_GAMEPAD_DPAD_LEFT;
		case BTN_TRIGGER_HAPPY2:
			return BC_GAMEPAD_DPAD_RIGHT;
		case BTN_TRIGGER_HAPPY3:
			return BC_GAMEPAD_DPAD_UP;
		case BTN_TRIGGER_HAPPY4:
			return BC_GAMEPAD_DPAD_DOWN;
		case BTN_START:
			return BC_GAMEPAD_START;
		case BTN_SELECT:
			return BC_GAMEPAD_BACK;
		case BTN_THUMBL:
			return BC_GAMEPAD_LS;
		case BTN_THUMBR:
			return BC_GAMEPAD_RS;
		case BTN_TL:
			return BC_GAMEPAD_LB;
		case BTN_TR:
			return BC_GAMEPAD_RB;
		case BTN_A:
			return BC_GAMEPAD_A;
		case BTN_B:
			return BC_GAMEPAD_B;
		case BTN_X:
			return BC_GAMEPAD_X;
		case BTN_Y:
			return BC_GAMEPAD_Y;
		}

		return BC_UNASSIGNED;
	}

	/**
	 * Maps an absolute axis as reported by the Linux system, to a Banshee axis. This will be one of the InputAxis enum
	 * members, or -1 if it cannot be mapped.
	 */
	INT32 gamepadMapCommonAxis(INT32 axis)
	{
		switch(axis)
		{
		case ABS_X: return (INT32)InputAxis::LeftStickX;
		case ABS_Y: return (INT32)InputAxis::LeftStickY;
		case ABS_RX: return (INT32)InputAxis::RightStickX;
		case ABS_RY: return (INT32)InputAxis::RightStickY;
		case ABS_Z: return (INT32)InputAxis::LeftTrigger;
		case ABS_RZ: return (INT32)InputAxis::RightTrigger;
		}

		return -1;
	}

	/**
	 * Returns true if the input event attached to the specified file handle is a gamepad,
	 * and populates the gamepad info structure. Returns false otherwise.
	 */
	bool parseGamepadInfo(int fileHandle, int eventHandlerIdx, GamepadInfo& info)
	{
		EventInfo eventInfo;
		if(!getEventInfo(fileHandle, eventInfo))
			return false;

		bool isGamepad = false;

		// Check for gamepad buttons
		UINT32 unknownButtonIdx = 0;
		for(auto& entry : eventInfo.buttons)
		{
			if((entry >= BTN_JOYSTICK && entry < BTN_GAMEPAD)
				|| (entry >= BTN_GAMEPAD && entry < BTN_DIGI)
				|| (entry >= BTN_WHEEL && entry < KEY_OK))
			{
				ButtonCode bc = gamepadMapCommonButton(entry);
				if(bc == BC_UNASSIGNED)
				{
					// Map to unnamed buttons
					if(unknownButtonIdx < 20)
					{
						bc = (ButtonCode)((INT32)BC_GAMEPAD_BTN1 + unknownButtonIdx);
						info.buttonMap[entry] = bc;

						unknownButtonIdx++;
					}
				}
				else
					info.buttonMap[entry] = bc;

				isGamepad = true;
			}
		}

		if(isGamepad)
		{
			info.eventHandlerIdx = eventHandlerIdx;

			// Get device name
			char name[128];
			if (ioctl(fileHandle, EVIOCGNAME(sizeof(name)), name) != -1)
				info.name = String(name);
			else
				BS_LOG(Error, Platform, "Could not read device name.");

			// Get axis ranges
			UINT32 unknownAxisIdx = 0;
			for(auto& entry : eventInfo.absAxes)
			{
				AxisInfo& axisInfo = info.axisMap[entry];
				axisInfo.min = Gamepad::MIN_AXIS;
				axisInfo.max = Gamepad::MAX_AXIS;

				input_absinfo absinfo;
				if (ioctl(fileHandle, EVIOCGABS(entry), &absinfo) == -1)
				{
					BS_LOG(Error, Platform, "Could not read absolute axis device features.");
					continue;
				}

				axisInfo.min = absinfo.minimum;
				axisInfo.max = absinfo.maximum;

				axisInfo.axisIdx = gamepadMapCommonAxis(entry);
				if(axisInfo.axisIdx == -1)
				{
					axisInfo.axisIdx = (INT32)InputAxis::Count + unknownAxisIdx;
					unknownAxisIdx++;
				}
			}
		}

		return isGamepad;
	}

	void Input::initRawInput()
	{
		mPlatformData = bs_new<InputPrivateData>();

		// Scan for valid gamepad devices
		for(int i = 0; i < 64; ++i )
		{
			String eventPath = "/dev/input/event" + toString(i);
			int file = open(eventPath.c_str(), O_RDONLY |O_NONBLOCK);
			if(file == -1)
			{
				// Note: We're ignoring failures due to permissions. The assumption is that gamepads won't have special
				// permissions. If this assumption proves wrong, then using udev might be required to read gamepad input.
				continue;
			}

			GamepadInfo info;
			if(parseGamepadInfo(file, i, info))
			{
				info.id = (UINT32)mPlatformData->gamepadInfos.size();
				mPlatformData->gamepadInfos.push_back(info);
			}

			close(file);
		}

		mKeyboard = bs_new<Keyboard>("Keyboard", this);
		mMouse = bs_new<Mouse>("Mouse", this);

		UINT32 numGamepads = getDeviceCount(InputDevice::Gamepad);
		for (UINT32 i = 0; i < numGamepads; i++)
			mGamepads.push_back(bs_new<Gamepad>(mPlatformData->gamepadInfos[i].name, mPlatformData->gamepadInfos[i], this));
	}

	void Input::cleanUpRawInput()
	{
		if (mMouse != nullptr)
			bs_delete(mMouse);

		if (mKeyboard != nullptr)
			bs_delete(mKeyboard);

		for (auto& gamepad : mGamepads)
			bs_delete(gamepad);

		bs_delete(mPlatformData);
	}

	UINT32 Input::getDeviceCount(InputDevice device) const
	{
		switch(device)
		{
		case InputDevice::Keyboard: return 1;
		case InputDevice::Mouse: return 1;
		case InputDevice::Gamepad: return (UINT32)mPlatformData->gamepadInfos.size();
		case InputDevice::Count: return 0;
		}

		return 0;
	}
}


switch(axis)
		{
		case ABS_X: return (INT32)InputAxis::LeftStickX;
		case ABS_Y: return (INT32)InputAxis::LeftStickY;
		case ABS_RX: return (INT32)InputAxis::RightStickX;
		case ABS_RY: return (INT32)InputAxis::RightStickY;
		case ABS_Z: return (INT32)InputAxis::LeftTrigger;
		case ABS_RZ: return (INT32)InputAxis::RightTrigger;
		}

#include "Input/BsInput.h"
#include "Private/Linux/BsLinuxInput.h"
#include "Input/BsMouse.h"
#include "Input/BsKeyboard.h"
#include "Input/BsGamepad.h"
#include <fcntl.h>
#include <linux/input.h>

namespace bs
{
	/** Information about events reported from a specific input event device. */
	struct EventInfo
	{
		Vector<INT32> buttons;
		Vector<INT32> relAxes;
		Vector<INT32> absAxes;
		Vector<INT32> hats;
	};

	/** Checks is the bit at the specified location in a byte array is set. */
	bool isBitSet(UINT8 bits[], UINT32 bit)
	{
		return ((bits[bit/8] >> (bit%8)) & 1) != 0;
	}

	/** Returns information about an input event device attached to he provided file handle. */
	bool getEventInfo(int fileHandle, EventInfo& eventInfo)
	{
		UINT8 eventBits[1 + EV_MAX/8];
		bs_zero_out(eventBits);

		if (ioctl(fileHandle, EVIOCGBIT(0, sizeof(eventBits)), eventBits) == -1)
			return false;

		for (UINT32 i = 0; i < EV_MAX; i++)
		{
			if(isBitSet(eventBits, i))
			{
				if(i == EV_ABS)
				{
					UINT8 absAxisBits[1 + ABS_MAX/8];
					bs_zero_out(absAxisBits);

					if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(absAxisBits)), absAxisBits) == -1)
					{
						BS_LOG(Error, Platform, "Could not read device absolute axis features.");
						continue;
					}

					for (UINT32 j = 0; j < ABS_MAX; j++)
					{
						if(isBitSet(absAxisBits, j))
						{
							if(j >= ABS_HAT0X && j <= ABS_HAT3Y)
								eventInfo.hats.push_back(j);
							else
								eventInfo.absAxes.push_back(j);
						}
					}
				}
				else if(i == EV_REL)
				{
					UINT8 relAxisBits[1 + REL_MAX/8];
					bs_zero_out(relAxisBits);

					if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(relAxisBits)), relAxisBits) == -1)
					{
						BS_LOG(Error, Platform, "Could not read device relative axis features.");
						continue;
					}

					for (UINT32 j = 0; j < REL_MAX; j++)
					{
						if(isBitSet(relAxisBits, j))
							eventInfo.relAxes.push_back(j);
					}
				}
				else if(i == EV_KEY)
				{
					UINT8 keyBits[1 + KEY_MAX/8];
					bs_zero_out(keyBits);

					if (ioctl(fileHandle, EVIOCGBIT(i, sizeof(keyBits)), keyBits) == -1)
					{
						BS_LOG(Error, Platform, "Could not read device key features.");
						continue;
					}

					for (UINT32 j = 0; j < KEY_MAX; j++)
					{
						if(isBitSet(keyBits, j))
							eventInfo.buttons.push_back(j);
					}
				}
			}
		}

		return true;
	}

	/** Converts a Linux button code to Banshee ButtonCode. */
	ButtonCode gamepadMapCommonButton(INT32 code)
	{
		// Note: Assuming XBox controller layout here
		switch (code)
		{
		case BTN_TRIGGER_HAPPY1:
			return BC_GAMEPAD_DPAD_LEFT;
		case BTN_TRIGGER_HAPPY2:
			return BC_GAMEPAD_DPAD_RIGHT;
		case BTN_TRIGGER_HAPPY3:
			return BC_GAMEPAD_DPAD_UP;
		case BTN_TRIGGER_HAPPY4:
			return BC_GAMEPAD_DPAD_DOWN;
		case BTN_START:
			return BC_GAMEPAD_START;
		case BTN_SELECT:
			return BC_GAMEPAD_BACK;
		case BTN_THUMBL:
			return BC_GAMEPAD_LS;
		case BTN_THUMBR:
			return BC_GAMEPAD_RS;
		case BTN_TL:
			return BC_GAMEPAD_LB;
		case BTN_TR:
			return BC_GAMEPAD_RB;
		case BTN_A:
			return BC_GAMEPAD_A;
		case BTN_B:
			return BC_GAMEPAD_B;
		case BTN_X:
			return BC_GAMEPAD_X;
		case BTN_Y:
			return BC_GAMEPAD_Y;
		}

		return BC_UNASSIGNED;
	}

	/**
	 * Maps an absolute axis as reported by the Linux system, to a Banshee axis. This will be one of the InputAxis enum
	 * members, or -1 if it cannot be mapped.
	 */
	INT32 gamepadMapCommonAxis(INT32 axis)
	{

		return -1;
	}

	/**
	 * Returns true if the input event attached to the specified file handle is a gamepad,
	 * and populates the gamepad info structure. Returns false otherwise.
	 */
	bool parseGamepadInfo(int fileHandle, int eventHandlerIdx, GamepadInfo& info)
	{
		EventInfo eventInfo;
		if(!getEventInfo(fileHandle, eventInfo))
			return false;

		bool isGamepad = false;

		// Check for gamepad buttons
		UINT32 unknownButtonIdx = 0;
		for(auto& entry : eventInfo.buttons)
		{
			if((entry >= BTN_JOYSTICK && entry < BTN_GAMEPAD)
				|| (entry >= BTN_GAMEPAD && entry < BTN_DIGI)
				|| (entry >= BTN_WHEEL && entry < KEY_OK))
			{
				ButtonCode bc = gamepadMapCommonButton(entry);
				if(bc == BC_UNASSIGNED)
				{
					// Map to unnamed buttons
					if(unknownButtonIdx < 20)
					{
						bc = (ButtonCode)((INT32)BC_GAMEPAD_BTN1 + unknownButtonIdx);
						info.buttonMap[entry] = bc;

						unknownButtonIdx++;
					}
				}
				else
					info.buttonMap[entry] = bc;

				isGamepad = true;
			}
		}

		if(isGamepad)
		{
			info.eventHandlerIdx = eventHandlerIdx;

			// Get device name
			char name[128];
			if (ioctl(fileHandle, EVIOCGNAME(sizeof(name)), name) != -1)
				info.name = String(name);
			else
				BS_LOG(Error, Platform, "Could not read device name.");

			// Get axis ranges
			UINT32 unknownAxisIdx = 0;
			for(auto& entry : eventInfo.absAxes)
			{
				AxisInfo& axisInfo = info.axisMap[entry];
				axisInfo.min = Gamepad::MIN_AXIS;
				axisInfo.max = Gamepad::MAX_AXIS;

				input_absinfo absinfo;
				if (ioctl(fileHandle, EVIOCGABS(entry), &absinfo) == -1)
				{
					BS_LOG(Error, Platform, "Could not read absolute axis device features.");
					continue;
				}

				axisInfo.min = absinfo.minimum;
				axisInfo.max = absinfo.maximum;

				axisInfo.axisIdx = gamepadMapCommonAxis(entry);
				if(axisInfo.axisIdx == -1)
				{
					axisInfo.axisIdx = (INT32)InputAxis::Count + unknownAxisIdx;
					unknownAxisIdx++;
				}
			}
		}

		return isGamepad;
	}

	void Input::initRawInput()
	{
		mPlatformData = bs_new<InputPrivateData>();

		// Scan for valid gamepad devices
		for(int i = 0; i < 64; ++i )
		{
			String eventPath = "/dev/input/event" + toString(i);
			int file = open(eventPath.c_str(), O_RDONLY |O_NONBLOCK);
			if(file == -1)
			{
				// Note: We're ignoring failures due to permissions. The assumption is that gamepads won't have special
				// permissions. If this assumption proves wrong, then using udev might be required to read gamepad input.
				continue;
			}

			GamepadInfo info;
			if(parseGamepadInfo(file, i, info))
			{
				info.id = (UINT32)mPlatformData->gamepadInfos.size();
				mPlatformData->gamepadInfos.push_back(info);
			}

			close(file);
		}

		mKeyboard = bs_new<Keyboard>("Keyboard", this);
		mMouse = bs_new<Mouse>("Mouse", this);

		UINT32 numGamepads = getDeviceCount(InputDevice::Gamepad);
		for (UINT32 i = 0; i < numGamepads; i++)
			mGamepads.push_back(bs_new<Gamepad>(mPlatformData->gamepadInfos[i].name, mPlatformData->gamepadInfos[i], this));
	}

	void Input::cleanUpRawInput()
	{
		if (mMouse != nullptr)
			bs_delete(mMouse);

		if (mKeyboard != nullptr)
			bs_delete(mKeyboard);

		for (auto& gamepad : mGamepads)
			bs_delete(gamepad);

		bs_delete(mPlatformData);
	}

	UINT32 Input::getDeviceCount(InputDevice device) const
	{
		switch(device)
		{
		case InputDevice::Keyboard: return 1;
		case InputDevice::Mouse: return 1;
		case InputDevice::Gamepad: return (UINT32)mPlatformData->gamepadInfos.size();
		case InputDevice::Count: return 0;
		}

		return 0;
	}
}


void UlfAxisym::InitializeSolutionStep(ProcessInfo& CurrentProcessInfo)
{
    KRATOS_TRY
    //save original Area
    mA0 = GeometryUtils::CalculateVolume2D(GetGeometry());
    //save the original radius
    r0=0.33333333333*(GetGeometry()[0].X()+GetGeometry()[1].X()+GetGeometry()[2].X());

    KRATOS_CATCH("");
}

/*
==============================================================================
KratosULFApplication
A library based on:
Kratos
A General Purpose Software for Multi-Physics Finite Element Analysis
Version 1.0 (Released on march 05, 2007).

example@nnthink.com
- CIMNE (International Center for Numerical Methods in Engineering),
Gran Capita' s/n, 08034 Barcelona, Spain

==============================================================================
*/

//   Project Name:        Kratos
//   Last modified by:    $Author: rrossi $

//#define GRADPN_FORM
//#define STOKES

// System includes

// External includes

// Project includes
#include "includes/define.h"
#include "custom_elements/ulf_axisym.h"
#include "utilities/math_utils.h"
#include "ULF_application.h"
#include "utilities/geometry_utilities.h"

namespace Kratos
{

UlfAxisym::UlfAxisym(IndexType NewId, GeometryType::Pointer pGeometry)
    : Element(NewId, pGeometry)
{
    //DO NOT ADD DOFS HERE!!!
}

UlfAxisym::UlfAxisym(IndexType NewId, GeometryType::Pointer pGeometry,  PropertiesType::Pointer pProperties)
    : Element(NewId, pGeometry, pProperties)
{
}

Element::Pointer UlfAxisym::Create(IndexType NewId, NodesArrayType const& ThisNodes,  PropertiesType::Pointer pProperties) const
{
    KRATOS_TRY

    return Element::Pointer(new UlfAxisym(NewId, GetGeometry().Create(ThisNodes), pProperties));
    KRATOS_CATCH("");
}

UlfAxisym::~UlfAxisym()
{
}

void UlfAxisym::CalculateLocalSystem(MatrixType& rLeftHandSideMatrix, VectorType& rRightHandSideVector, ProcessInfo& rCurrentProcessInfo)
{
    KRATOS_TRY

    BoundedMatrix<double,4,6> msB;
    BoundedMatrix<double,4,4> ms_constitutive_matrix;
    BoundedMatrix<double,4,6> ms_temp;
    BoundedMatrix<double,3,2> msDN_Dx;
    array_1d<double,3> msN; //dimension = number of nodes

    const double& density = 0.333333333*(GetGeometry()[0].FastGetSolutionStepValue(DENSITY)+
                                         GetGeometry()[1].FastGetSolutionStepValue(DENSITY) +
                                         GetGeometry()[2].FastGetSolutionStepValue(DENSITY));
    double K = 0.333333333*(GetGeometry()[0].FastGetSolutionStepValue(BULK_MODULUS)+
                            GetGeometry()[1].FastGetSolutionStepValue(BULK_MODULUS) +
                            GetGeometry()[2].FastGetSolutionStepValue(BULK_MODULUS));
    K *= density;

    int is_int = GetGeometry()[0].FastGetSolutionStepValue(IS_INTERFACE);
    is_int += GetGeometry()[1].FastGetSolutionStepValue(IS_INTERFACE);
    is_int += GetGeometry()[2].FastGetSolutionStepValue(IS_INTERFACE);

  int is_wall = GetGeometry()[0].FastGetSolutionStepValue(IS_STRUCTURE);
    is_wall += GetGeometry()[1].FastGetSolutionStepValue(IS_STRUCTURE);
    is_wall += GetGeometry()[2].FastGetSolutionStepValue(IS_STRUCTURE);

    unsigned int dim = 2;
    unsigned int number_of_nodes = 3;

    if(rLeftHandSideMatrix.size1() != 6)
        rLeftHandSideMatrix.resize(6,6,false);

    if(rRightHandSideVector.size() != 6)
        rRightHandSideVector.resize(6,false);

    //calculate current area
    double current_area;
    GeometryUtils::CalculateGeometryData(GetGeometry(), msDN_Dx, msN, current_area);
    double r=0.33333333333*(GetGeometry()[0].X()+GetGeometry()[1].X()+GetGeometry()[2].X());

    if (is_int!=0 && is_wall!=0)
	{

	current_area*=0.0000000001;
	mA0*=0.0000000001;
	}

    //writing the body force
    const array_1d<double,3>& body_force = 0.333333333*(GetGeometry()[0].FastGetSolutionStepValue(BODY_FORCE)+
                                           GetGeometry()[1].FastGetSolutionStepValue(BODY_FORCE) +
                                           GetGeometry()[2].FastGetSolutionStepValue(BODY_FORCE));

    for(unsigned int i = 0; i<number_of_nodes; i++)
    {

	rRightHandSideVector[i*2] = r*body_force[0]* density * mA0 * 0.3333333333333;
        rRightHandSideVector[i*2+1] = r*body_force[1] * density * mA0 * 0.3333333333333;
    }

    //VISCOUS CONTRIBUTION TO THE STIFFNESS MATRIX
    //filling matrix B
    for (unsigned int i=0; i<number_of_nodes; i++)
    {
        unsigned int index = dim*i;
        msB(0,index+0)=msDN_Dx(i,0);   msB(0,index+1)= 0.0;
        msB(1,index+0)=0.0;            msB(1,index+1)= msDN_Dx(i,1);
	//the third raw are the "new" axisymmetric entries corresponding to the hoop strains.. so we directly proceed to the fourth row.. third raw is filled separetely
        msB(3,index+0)= msDN_Dx(i,1);  msB(3,index+1)= msDN_Dx(i,0);
    }
    //and now we fill the 3rd raw of the axisymmetric Strain-Displ matrix B: N/r (N/x_center in our case)
    double norm_r = r*r;
    norm_r=sqrt(norm_r);
    //to avoid the singularity at the central axis (where r=0), one of the tricks is to assume that there epsilon_theta=epsilon_r, or msB(2,i)=msB(0,i)

    if (norm_r>0.0000000001)
	{
	msB(2,0)=msN[0]/r;  msB(2,1)=0.0;   	msB(2,2)=msN[1]/r; msB(2,3)=0.0;   	msB(2,4)=msN[2]/r; msB(2,5)=0.0;
	}
    else
	{
	msB(2,0)=msB(0,0);  msB(2,1)=msB(0,1); msB(2,2)=msB(0,2); msB(2,3)=msB(0,3);   msB(2,4)=msB(0,4); msB(2,5)=msB(0,5);
	}

    //constitutive tensor
    /*
    ms_constitutive_matrix(0,0) = K;
    ms_constitutive_matrix(0,1) = K ;
    ms_constitutive_matrix(0,2) = 0.0;
    ms_constitutive_matrix(1,0) = K;
    ms_constitutive_matrix(1,1) = K;
    ms_constitutive_matrix(1,2) = 0.0;
    ms_constitutive_matrix(2,0) = 0.0;
    ms_constitutive_matrix(2,1) = 0.0;
    ms_constitutive_matrix(2,2) = 0.0;
    */
    //TO CHECK!!! IS THIS CONST MATRIX CORRECT??

    ms_constitutive_matrix(0,0) = K;
    ms_constitutive_matrix(0,1) = K;
    ms_constitutive_matrix(0,2) = K;
    ms_constitutive_matrix(0,3) = 0;

    ms_constitutive_matrix(1,0) = K;
    ms_constitutive_matrix(1,1) = K;
    ms_constitutive_matrix(1,2) = K;
    ms_constitutive_matrix(1,3) = 0;

    ms_constitutive_matrix(2,0) = K;
    ms_constitutive_matrix(2,1) = K;
    ms_constitutive_matrix(2,2) = K;
    ms_constitutive_matrix(2,3) = 0;

    ms_constitutive_matrix(3,0) = 0.0;
    ms_constitutive_matrix(3,1) = 0.0;
    ms_constitutive_matrix(3,2) = 0.0;
    ms_constitutive_matrix(3,3) = 0.0;

    //calculating viscous contributions
    ms_temp = prod( ms_constitutive_matrix , msB);
    noalias(rLeftHandSideMatrix) = prod( trans(msB) , ms_temp);
    rLeftHandSideMatrix *= -current_area;
    rLeftHandSideMatrix *=r;

    //get the old pressure
    double p0old = GetGeometry()[0].FastGetSolutionStepValue(PRESSURE,1);
    double p1old = GetGeometry()[1].FastGetSolutionStepValue(PRESSURE,1);
    double p2old = GetGeometry()[2].FastGetSolutionStepValue(PRESSURE,1);

    //adding pressure gradient
    double pavg = p0old + p1old + p2old; //calculate old pressure over the element
    pavg *= 0.33333333333333333333333 * current_area;

    //add to pavg the increment of pressure inside the time step
    double dp = K * (current_area - mA0);
    pavg += dp;
    pavg*=r;

    rRightHandSideVector[0] += (msDN_Dx(0,0)+0.33333/r)*pavg;
    rRightHandSideVector[1] += msDN_Dx(0,1)*pavg;
    rRightHandSideVector[2] += (msDN_Dx(1,0)+0.33333/r)*pavg;
    rRightHandSideVector[3] += msDN_Dx(1,1)*pavg;
    rRightHandSideVector[4] += (msDN_Dx(2,0)+0.33333/r)*pavg;
    rRightHandSideVector[5] += msDN_Dx(2,1)*pavg;

    KRATOS_CATCH("")
}

void UlfAxisym::CalculateRightHandSide(VectorType& rRightHandSideVector, ProcessInfo& rCurrentProcessInfo)
{
KRATOS_THROW_ERROR(std::logic_error,"function not implemeneted for this element","");

}

void UlfAxisym::CalculateMassMatrix(MatrixType& rMassMatrix, ProcessInfo& rCurrentProcessInfo)
{
    KRATOS_TRY
    int is_int = GetGeometry()[0].FastGetSolutionStepValue(IS_INTERFACE);
    is_int += GetGeometry()[1].FastGetSolutionStepValue(IS_INTERFACE);
    is_int += GetGeometry()[2].FastGetSolutionStepValue(IS_INTERFACE);

  int is_wall = GetGeometry()[0].FastGetSolutionStepValue(IS_STRUCTURE);
    is_wall += GetGeometry()[1].FastGetSolutionStepValue(IS_STRUCTURE);
    is_wall += GetGeometry()[2].FastGetSolutionStepValue(IS_STRUCTURE);

    const double& density = 0.333333333*(GetGeometry()[0].FastGetSolutionStepValue(DENSITY)+
                                         GetGeometry()[1].FastGetSolutionStepValue(DENSITY) +
                                         GetGeometry()[2].FastGetSolutionStepValue(DENSITY));
    //lumped
    unsigned int dimension = GetGeometry().WorkingSpaceDimension();
    unsigned int NumberOfNodes = GetGeometry().size();
    unsigned int MatSize = dimension * NumberOfNodes;

    if(rMassMatrix.size1() != MatSize)
        rMassMatrix.resize(MatSize,MatSize,false);

    noalias(rMassMatrix) = ZeroMatrix(MatSize,MatSize);

    if (is_int!=0 && is_wall!=0)
	{
	mA0*=0.0000000001;
	}

    double nodal_mass = mA0 * density * 0.333333333333333333;

    for(unsigned int i=0; i<NumberOfNodes; i++)
    {
        for(unsigned int j=0; j<dimension; j++)
        {
            unsigned int index = i*dimension + j;
            rMassMatrix(index,index) = nodal_mass;
        }
    }
    //axisymetric formulation
    double r=0.33333333333*(GetGeometry()[0].X()+GetGeometry()[1].X()+GetGeometry()[2].X());

    rMassMatrix*=r;
    KRATOS_CATCH("")
}

void UlfAxisym::CalculateDampingMatrix(MatrixType& rDampMatrix, ProcessInfo& rCurrentProcessInfo)
{
    KRATOS_TRY

    int is_int = GetGeometry()[0].FastGetSolutionStepValue(IS_INTERFACE);
    is_int += GetGeometry()[1].FastGetSolutionStepValue(IS_INTERFACE);
    is_int += GetGeometry()[2].FastGetSolutionStepValue(IS_INTERFACE);

  int is_wall = GetGeometry()[0].FastGetSolutionStepValue(IS_STRUCTURE);
    is_wall += GetGeometry()[1].FastGetSolutionStepValue(IS_STRUCTURE);
    is_wall += GetGeometry()[2].FastGetSolutionStepValue(IS_STRUCTURE);

    BoundedMatrix<double,4,6> msB;
    BoundedMatrix<double,4,4> ms_constitutive_matrix;
    BoundedMatrix<double,4,6> ms_temp;
    BoundedMatrix<double,3,2> msDN_Dx;
    array_1d<double,3> msN; //dimension = number of nodes

    unsigned int number_of_nodes = GetGeometry().size();
    unsigned int dim = GetGeometry().WorkingSpaceDimension();

    if(rDampMatrix.size1() != 6)
        rDampMatrix.resize(6,6,false);

    //getting data for the given geometry
    double Area;
    GeometryUtils::CalculateGeometryData(GetGeometry(), msDN_Dx, msN, Area);

    if (is_int!=0 && is_wall!=0)
	{

	Area*=0.0000000001;
	}

    //getting properties
    const double& nu = 0.333333333*(GetGeometry()[0].FastGetSolutionStepValue(VISCOSITY)+
                                    GetGeometry()[1].FastGetSolutionStepValue(VISCOSITY) +
                                    GetGeometry()[2].FastGetSolutionStepValue(VISCOSITY));

    const double& density = 0.333333333*(GetGeometry()[0].FastGetSolutionStepValue(DENSITY)+
                                         GetGeometry()[1].FastGetSolutionStepValue(DENSITY) +
                                         GetGeometry()[2].FastGetSolutionStepValue(DENSITY));
    //VISCOUS CONTRIBUTION TO THE STIFFNESS MATRIX
    //filling matrix B
    for (unsigned int i=0; i<number_of_nodes; i++)
    {
        unsigned int index = dim*i;
        msB(0,index+0)=msDN_Dx(i,0);
        msB(0,index+1)= 0.0;
        msB(1,index+0)=0.0;
        msB(1,index+1)= msDN_Dx(i,1);
	//the thirrd raw are the "new" axisymmetric entries corresponding to the hoop strains.. so we directly proceed to the fourth row.. third raw is filled separetely
        msB(3,index+0)= msDN_Dx(i,1);
        msB(3,index+1)= msDN_Dx(i,0);
    }
    //and now we fill the 3rd raw of the axisymmetric Strain-Displ matrix B: N/r (N/x_center in our case)
    double r=0.33333333333*(GetGeometry()[0].X()+GetGeometry()[1].X()+GetGeometry()[2].X());
    double norm_r = r*r;
    norm_r=sqrt(norm_r);
    //to avoid the singularity at the central axis (where r=0), one of the tricks is to assume that there epsilon_theta=epsilon_r, or msB(2,i)=msB(0,i)

    if (norm_r>0.0000000001)
	{
	msB(2,0)=msN[0]/r;  msB(2,1)=0.0;   msB(2,2)=msN[1]/r; msB(2,3)=0.0;   msB(2,4)=msN[2]/r; msB(2,5)=0.0;
	}
    else
	{
	msB(2,0)=msB(0,0);  msB(2,1)=msB(0,1); msB(2,2)=msB(0,2); msB(2,3)=msB(0,3);  msB(2,4)=msB(0,4); msB(2,5)=msB(0,5);
	}

    //constitutive tensor
    const double& a = nu*density;
    ms_constitutive_matrix(0,0) = (4.0/3.0)*a; 	 ms_constitutive_matrix(0,1) = (-2.0/3.0)*a; 	ms_constitutive_matrix(0,2) = (-2.0/3.0)*a; ms_constitutive_matrix(0,3) = 0.0;
    ms_constitutive_matrix(1,0) = (-2.0/3.0)*a;  ms_constitutive_matrix(1,1) = (4.0/3.0)*a;  	ms_constitutive_matrix(1,2) = (-2.0/3.0)*a; ms_constitutive_matrix(1,3) = 0.0;
    ms_constitutive_matrix(2,0) = (-2.0/3.0)*a;  ms_constitutive_matrix(2,1) = (-2.0/3.0)*a;    ms_constitutive_matrix(2,2) = (4.0/3.0)*a;  ms_constitutive_matrix(2,3) = 0.0;
    ms_constitutive_matrix(3,0) = 0.0;     	 ms_constitutive_matrix(3,1) = 0.0;		ms_constitutive_matrix(3,2) = 0.0;	    ms_constitutive_matrix(3,3) = a;

    //calculating viscous contributions
    ms_temp = prod( ms_constitutive_matrix , msB);
    noalias(rDampMatrix) = prod( trans(msB) , ms_temp);

    rDampMatrix *= Area;
    rDampMatrix *=r;

    KRATOS_CATCH("")
}

// this subroutine calculates the nodal contributions for the explicit steps of the
// fractional step procedure

void UlfAxisym::EquationIdVector(EquationIdVectorType& rResult, ProcessInfo& CurrentProcessInfo)
{
    unsigned int number_of_nodes = GetGeometry().PointsNumber();
    unsigned int dim = 2;

    if(rResult.size() != number_of_nodes*dim)
        rResult.resize(number_of_nodes*dim,false);

    for (unsigned int i=0; i<number_of_nodes; i++)
    {
        rResult[i*dim] = GetGeometry()[i].GetDof(DISPLACEMENT_X).EquationId();
        rResult[i*dim+1] = GetGeometry()[i].GetDof(DISPLACEMENT_Y).EquationId();
    }

}

void UlfAxisym::GetDofList(DofsVectorType& ElementalDofList,ProcessInfo& CurrentProcessInfo)
{
    unsigned int number_of_nodes = GetGeometry().PointsNumber();
    unsigned int dim = 2;

    if(ElementalDofList.size() != number_of_nodes*dim)
        ElementalDofList.resize(number_of_nodes*dim);

    for (unsigned int i=0; i<number_of_nodes; i++)
    {
        ElementalDofList[i*dim] = GetGeometry()[i].pGetDof(DISPLACEMENT_X);
        ElementalDofList[i*dim+1] = GetGeometry()[i].pGetDof(DISPLACEMENT_Y);
    }
}

void UlfAxisym::GetValuesVector(Vector& values, int Step)
{
    const unsigned int number_of_nodes = GetGeometry().size();
    const unsigned int dim = GetGeometry().WorkingSpaceDimension();
    unsigned int MatSize = number_of_nodes*dim;
    if(values.size() != MatSize)	values.resize(MatSize,false);
    for (unsigned int i=0; i<number_of_nodes; i++)
    {
        unsigned int index = i*dim;
        const array_1d<double,3>& disp = GetGeometry()[i].FastGetSolutionStepValue(DISPLACEMENT,Step);
        values[index] = disp[0];
        values[index + 1] = disp[1];
    }
}

void UlfAxisym::GetFirstDerivativesVector(Vector& values, int Step)
{
    const unsigned int number_of_nodes = GetGeometry().size();
    const unsigned int dim = GetGeometry().WorkingSpaceDimension();
    unsigned int MatSize = number_of_nodes*dim;
    if(values.size() != MatSize)   values.resize(MatSize,false);
    for (unsigned int i=0; i<number_of_nodes; i++)
    {
        unsigned int index = i*dim;
        const array_1d<double,3>& vel = GetGeometry()[i].FastGetSolutionStepValue(VELOCITY,Step);
        values[index] = vel[0];
        values[index + 1] = vel[1];
    }
}
void UlfAxisym::GetSecondDerivativesVector(Vector& values, int Step)
{
    const unsigned int number_of_nodes = GetGeometry().size();
    const unsigned int dim = GetGeometry().WorkingSpaceDimension();
    unsigned int MatSize = number_of_nodes*dim;
    if(values.size() != MatSize) values.resize(MatSize,false);
    for (unsigned int i=0; i<number_of_nodes; i++)
    {
        unsigned int index = i*dim;
        const array_1d<double,3>& acc = GetGeometry()[i].FastGetSolutionStepValue(ACCELERATION,Step);
        values[index] = acc[0];
        values[index + 1] = acc[1];
    }
}

void  UlfAxisym::Calculate(const Variable<double >& rVariable, double& Output, const ProcessInfo& rCurrentProcessInfo)
{
    if(rVariable == PRESSURE)
    {
	    int is_int = GetGeometry()[0].FastGetSolutionStepValue(IS_INTERFACE);
	    is_int += GetGeometry()[1].FastGetSolutionStepValue(IS_INTERFACE);
	    is_int += GetGeometry()[2].FastGetSolutionStepValue(IS_INTERFACE);

	  int is_wall = GetGeometry()[0].FastGetSolutionStepValue(IS_STRUCTURE);
	    is_wall += GetGeometry()[1].FastGetSolutionStepValue(IS_STRUCTURE);
	    is_wall += GetGeometry()[2].FastGetSolutionStepValue(IS_STRUCTURE);

        array_1d<double,3> msN; //dimension = number of nodes

        const double density = 0.333333333*(GetGeometry()[0].FastGetSolutionStepValue(DENSITY)+
                                            GetGeometry()[1].FastGetSolutionStepValue(DENSITY) +
                                            GetGeometry()[2].FastGetSolutionStepValue(DENSITY));

        double K = 0.333333333*(GetGeometry()[0].FastGetSolutionStepValue(BULK_MODULUS)+
                                GetGeometry()[1].FastGetSolutionStepValue(BULK_MODULUS) +
                                GetGeometry()[2].FastGetSolutionStepValue(BULK_MODULUS));
       K *= density;

      double r=0.33333333333*(GetGeometry()[0].X()+GetGeometry()[1].X()+GetGeometry()[2].X());
      double norm_r = r*r;
      norm_r=sqrt(norm_r);

        double current_area = GeometryUtils::CalculateVolume2D(GetGeometry());

    if (is_int!=0 && is_wall!=0)
	{
	current_area*=0.0000000001;
	mA0*=0.0000000001;

	}

        //save in msN the old pressure
        msN[0] = GetGeometry()[0].FastGetSolutionStepValue(PRESSURE,1);
        msN[1] = GetGeometry()[1].FastGetSolutionStepValue(PRESSURE,1);
        msN[2] = GetGeometry()[2].FastGetSolutionStepValue(PRESSURE,1);
        double diag_term = current_area/6.0;
        double out_term = current_area/12.0;

        //add to pavg the increment of pressure inside the time step
	double dp_area = K * 0.3333333333333333333*(r*current_area - r0*mA0)/r;

        //P = Mconsistent*pold + dp
        		GetGeometry()[0].FastGetSolutionStepValue(PRESSURE) += dp_area;
        		GetGeometry()[1].FastGetSolutionStepValue(PRESSURE) += dp_area;
        		GetGeometry()[2].FastGetSolutionStepValue(PRESSURE) += dp_area;

    }
    else if (rVariable == IS_FLUID)
    {
        GetGeometry()[0].FastGetSolutionStepValue(IS_FLUID) = 1.0 ;
        GetGeometry()[1].FastGetSolutionStepValue(IS_FLUID) = 1.0 ;
        GetGeometry()[2].FastGetSolutionStepValue(IS_FLUID) = 1.0 ;

    }

}

} // Namespace Kratos


void ALeatherBoots::BeginPlay()
{
	Super::BeginPlay();
}

#include "Ethereal.h"
#include "LeatherBoots.h"

#define LOCTEXT_NAMESPACE "EtherealText"

// Sets default values
ALeatherBoots::ALeatherBoots(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// Get Assets, References Obtained Via Right Click in Editor
	static ConstructorHelpers::FObjectFinder<UTexture2D> LargeIconObject(TEXT("Texture2D'/Game/Blueprints/Widgets/UI-Images/Icons_Gear/ArmorIcon_LeatherBoots.ArmorIcon_LeatherBoots'"));
	static ConstructorHelpers::FObjectFinder<UTexture2D> SmallIconObject(TEXT("Texture2D'/Game/Blueprints/Widgets/UI-Images/Icons_Gear/ArmorIcon_LeatherBoots-small.ArmorIcon_LeatherBoots-small'"));
	static ConstructorHelpers::FObjectFinder<USkeletalMesh> ArmorMeshObject(TEXT("SkeletalMesh'/Game/EtherealParty/Morgan/Clothes/LeatherArmor/LeatherBoots/LeatherBoots.LeatherBoots'"));

	// Set Default Objects
	LargeIcon = LargeIconObject.Object;
	SmallIcon = SmallIconObject.Object;

	SK_ArmorMesh = ArmorMeshObject.Object;
	ArmorMesh = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("ArmorMesh"));
	ArmorMesh->SetSkeletalMesh(SK_ArmorMesh);
	ArmorMesh->SetupAttachment(RootComponent);
	UCommonLibrary::SetupSKComponentsWithCollision(ArmorMesh);

	// Set Default Values
	Name = EMasterGearList::GL_LeatherBoots;
	NameText = LOCTEXT("LeatherBootsName", "Leather Boots");
	Type = EMasterGearTypes::GT_Feet;
	TypeText = LOCTEXT("LeatherBootsType", "Feet");
	Description = "Armor of Ethereal Warriors.";
	Price = 5000;
	MPCost = 0;
	ATK = 1;
	DEF = 2;
	SPD = 1;
	HP = 15;
	MP = 5;
}

// Called when the game starts or when spawned


int main(int argc, char* argv[])
{
    // initialize HPX, run hpx_main
    hpx::start(argc, argv);

    // wait for hpx::finalize being called
    HPX_TEST_EQ(hpx::stop(), 0);
    return hpx::util::report_errors();
}

#include <hpx/config.hpp>
#if !defined(HPX_COMPUTE_DEVICE_CODE)
#include <hpx/futures/future.hpp>
#include <hpx/hpx_start.hpp>
#include <hpx/include/actions.hpp>
#include <hpx/include/async.hpp>
#include <hpx/include/runtime.hpp>
#include <hpx/iostream.hpp>
#include <hpx/modules/testing.hpp>

#include <utility>

bool called_test_action = false;
bool called_t = false;

namespace mynamespace {
    void test()
    {
        called_test_action = true;
    }

    HPX_DEFINE_PLAIN_ACTION(test, test_action);

    static auto t = hpx::actions::lambda_to_action([]() { called_t = true; });
}    // namespace mynamespace

HPX_REGISTER_ACTION(mynamespace::test_action, mynamespace_test_action)

// hpx_main, is the actual main called by hpx
int hpx_main()
{
    {
        using func = mynamespace::test_action;
        hpx::async<func>(hpx::find_here()).get();
        HPX_TEST(called_test_action);
    }

    // Same test with lambdas
    // action lambdas inhibit undefined behavior...
#if !defined(HPX_HAVE_SANITIZERS)
    {
        hpx::async(std::move(mynamespace::t), hpx::find_here()).get();
        HPX_TEST(called_t);
    }
#endif

    // End the program
    return hpx::finalize();
}

// Main, initializes HPX