/** 
 *	Anchor library
 *
 *	Helpers for anchor manipulation and map validation
 *
 *	/!\ Don't forget to call the count() function before using the other functions
 *	/!\ to update the count of anchors present on the map
 */

#Const Version		"2013-11-14"
#Const ScriptName	"Anchor.Script.txt"

// ---------------------------------- //
// Globales
// ---------------------------------- //
declare Integer[Integer][Text] G_AnchorCounts;

// ---------------------------------- //
// Functions
// ---------------------------------- //
// ---------------------------------- //
// Private
// ---------------------------------- //

// ---------------------------------- //
/** Validate the map
 *
 *	@return		Always True
 */
Boolean Private_SetAsValid() {
	ValidationStatus = CMapType::ValidationStatus::Validated;
	return True;
}

// ---------------------------------- //
/** Unvalidate the map and set the error message
 *
 *	@param	_Error		The error message
 *
 *	@return		Always False
 */
Boolean Private_SetAsNotValid(Text _Error) {
	ValidationStatus = CMapType::ValidationStatus::NotValidable;
	ValidabilityRequirementsMessage = _Error;
	return False;
}

// ---------------------------------- //
// Public
// ---------------------------------- //

// ---------------------------------- //
/** Return the version number of the script
 *
 *	@return		The version number of the script
 */
Text GetScriptVersion() {
	return Version;
}

// ---------------------------------- //
/** Return the name of the script
 *
 *	@return		The name of the script
 */
Text GetScriptName() {
	return ScriptName;
}

// ---------------------------------- //
/** Get the number of anchor with a specific tag and order
 *
 *	@param	_Tag		The tag to count
 *	@param	_Order		The order to count
 *
 *	@return		The number of anchor found
 */
Integer GetCount(Text _Tag, Integer _Order) {
	if (G_AnchorCounts.existskey(_Tag) && G_AnchorCounts[_Tag].existskey(_Order)) return G_AnchorCounts[_Tag][_Order];
	return 0;
}

// ---------------------------------- //
/** Get the number of anchor with a specific tag
 *
 *	@param	_Tag		The tag to count
 *
 *	@return		The number of anchor found
 */
Integer GetCount(Text _Tag) {
	declare Nb = 0;
	
	if (G_AnchorCounts.existskey(_Tag)) {
		declare AnchorCount = G_AnchorCounts[_Tag];
		foreach (Order => Count in AnchorCount) {
			Nb += Count;
		}
	}
	
	return Nb;
}

// ---------------------------------- //
/** Get the number of anchor with a specific order
 *
 *	@param	_Order		The order to count
 *
 *	@return		The number of anchor found
 */
Integer GetCount(Integer _Order) {
	declare Nb = 0;
	
	foreach (Tag => AnchorCount in G_AnchorCounts) {
		foreach (Order => Count in AnchorCount) {
			if (Order == _Order) Nb += Count;
		}
	}
	
	return Nb;
}

// ---------------------------------- //
/** Check and valid if there's at least X anchor with the corresponding tag and order
 *
 *	@param	_Tag		The tag to check
 *	@param	_Order		The order to check
 *	@param	_X			The number of anchor to search
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasAtLeastXAnchor(Text _Tag, Integer _Order, Integer _X, Text _Error) {  
	if (GetCount(_Tag, _Order) < _X) {
		return Private_SetAsNotValid(_Error);
	}	
	return Private_SetAsValid();
}

// ---------------------------------- //
/** Check and valid if there's at least X anchor with the corresponding tag
 *
 *	@param	_Tag		The tag to check
 *	@param	_X			The number of anchor to search
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasAtLeastXAnchor(Text _Tag, Integer _X, Text _Error) {
	if (GetCount(_Tag) < _X) {
		return Private_SetAsNotValid(_Error);
	}
	return Private_SetAsValid();
}

// ---------------------------------- //
/** Check and valid if there's at least X anchor with the corresponding order
 *
 *	@param	_Order		The order to check
 *	@param	_X			The number of anchor to search
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasAtLeastXAnchor(Integer _Order, Integer _X, Text _Error) {
	if (GetCount(_Order) < _X) {
		return Private_SetAsNotValid(_Error);
	}
	return Private_SetAsValid();
}

// ---------------------------------- //
/** Check and valid if there's at least one anchor with the corresponding tag and order
 *
 *	@param	_Tag		The tag to check
 *	@param	_Order		The order to check
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasAtLeastOneAnchor(Text _Tag, Integer _Order, Text _Error) {  
	return HasAtLeastXAnchor(_Tag, _Order, 1, _Error);
}

// ---------------------------------- //
/** Check and valid if there's at least one anchor with the corresponding tag 
 *
 *	@param	_Tag		The tag to check
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasAtLeastOneAnchor(Text _Tag, Text _Error) {  
	return HasAtLeastXAnchor(_Tag, 1, _Error);
}

// ---------------------------------- //
/** Check and valid if there's at least one anchor with the corresponding order
 *
 *	@param	_Order		The order to check
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasAtLeastOneAnchor(Integer _Order, Text _Error) {  
	return HasAtLeastXAnchor(_Order, 1, _Error);
}

// ---------------------------------- //
/** Check and valid if there's exactly X anchor with the corresponding tag and order
 *
 *	@param	_Tag		The tag to check
 *	@param	_Order		The order to check
 *	@param	_X			The number of anchor to search
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasExactlyXAnchor(Text _Tag, Integer _Order, Integer _X, Text _Error) {
	if (GetCount(_Tag, _Order) != _X) {
		return Private_SetAsNotValid(_Error);
	}
	return Private_SetAsValid();
}

// ---------------------------------- //
/** Check and valid if there's exactly X anchor with the corresponding tag
 *
 *	@param	_Tag		The tag to check
 *	@param	_X			The number of anchor to search
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasExactlyXAnchor(Text _Tag, Integer _X, Text _Error) {
	if (GetCount(_Tag) != _X) {
		return Private_SetAsNotValid(_Error);
	}
	return Private_SetAsValid();
}

// ---------------------------------- //
/** Check and valid if there's exactly X anchor with the corresponding order
 *
 *	@param	_Order		The order to check
 *	@param	_X			The number of anchor to search
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasExactlyXAnchor(Integer _Order, Integer _X, Text _Error) {
	if (GetCount(_Order) != _X) {
		return Private_SetAsNotValid(_Error);
	}
	return Private_SetAsValid();
}

// ---------------------------------- //
/** Check and valid if there's exactly one anchor with the corresponding tag and order
 *
 *	@param	_Tag		The tag to check
 *	@param	_Order		The order to check
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasExactlyOneAnchor(Text _Tag, Integer _Order, Text _Error) {
	return HasExactlyXAnchor(_Tag, _Order, 1, _Error);
}

// ---------------------------------- //
/** Check and valid if there's exactly one anchor with the corresponding tag
 *
 *	@param	_Tag		The tag to check
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasExactlyOneAnchor(Text _Tag, Text _Error) {
	return HasExactlyXAnchor(_Tag, 1, _Error);
}

// ---------------------------------- //
/** Check and valid if there's exactly one anchor with the corresponding order
 *
 *	@param	_Order		The order to check
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasExactlyOneAnchor(Integer _Order, Text _Error) {
	return HasExactlyXAnchor(_Order, 1, _Error);
}

// ---------------------------------- //
/** Check and valid if there's less than X anchor with the corresponding tag and order
 *
 *	@param	_Tag		The tag to check
 *	@param	_Order		The order to check
 *	@param	_X			The number of anchor to search
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasLessThanXAnchor(Text _Tag, Integer _Order, Integer _X, Text _Error) {  
	if (GetCount(_Tag, _Order) >= _X) {
		return Private_SetAsNotValid(_Error);
	}
	return Private_SetAsValid();
}

// ---------------------------------- //
/** Check and valid if there's less than X anchor with the corresponding tag
 *
 *	@param	_Tag		The tag to check
 *	@param	_X			The number of anchor to search
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasLessThanXAnchor(Text _Tag, Integer _X, Text _Error) {  
	if (GetCount(_Tag) >= _X) {
		return Private_SetAsNotValid(_Error);
	}
	return Private_SetAsValid();
}

// ---------------------------------- //
/** Check and valid if there's less than X anchor with the corresponding order
 *
 *	@param	_Order		The order to check
 *	@param	_X			The number of anchor to search
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasLessThanXAnchor(Integer _Order, Integer _X, Text _Error) {  
	if (GetCount(_Order) >= _X) {
		return Private_SetAsNotValid(_Error);
	}
	return Private_SetAsValid();
}

// ---------------------------------- //
/** Check and valid if there isn't any anchor with the corresponding tag and order
 *
 *	@param	_Tag		The tag to check
 *	@param	_Order		The order to check
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasNoAnchor(Text _Tag, Integer _Order, Text _Error) {  
	if (GetCount(_Tag, _Order) > 0) {
		return Private_SetAsNotValid(_Error);
	}
	return Private_SetAsValid();
}

// ---------------------------------- //
/** Check and valid if there isn't any anchor with the corresponding tag
 *
 *	@param	_Tag		The tag to check
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasNoAnchor(Text _Tag, Text _Error) {  
	if (GetCount(_Tag) > 0) {
		return Private_SetAsNotValid(_Error);
	}
	return Private_SetAsValid();
}

// ---------------------------------- //
/** Check and valid if there isn't any anchor with the corresponding order
 *
 *	@param	_Order		The order to check
 *	@param	_Error		The error message to display if the condition is not met
 *
 *	@return		True if the condition is met, False otherwise
 */
Boolean HasNoAnchor(Integer _Order, Text _Error) {  
	if (GetCount(_Order) > 0) {
		return Private_SetAsNotValid(_Error);
	}
	return Private_SetAsValid();
}

// ---------------------------------- //
/// Update the count of the anchors on the map
Void UpdateAnchorCounts() {
	G_AnchorCounts.clear();
	foreach(Data in AnchorData) {
		if (!G_AnchorCounts.existskey(Data.Tag)) G_AnchorCounts[Data.Tag] = Integer[Integer];
		if (!G_AnchorCounts[Data.Tag].existskey(Data.Order)) G_AnchorCounts[Data.Tag][Data.Order] = 0;
		G_AnchorCounts[Data.Tag][Data.Order] += 1;		
	}
}

// ---------------------------------- //
/// UpdateAnchorCounts() overload, Update the count of the anchors on the map
Void Count() {
	UpdateAnchorCounts();
}