contract BattleBase is Ownable {
	 using SafeMath for uint256;
	 
	/***********************************************************************************/
	/* EVENTS
	/***********************************************************************************/
	
	/**
	* History sequence will be represented by uint256, in other words the max round is 256 (rounds more than this will decide on hp left or draw)
	*  1 is challenger attack
	*  2 is defender attack
	*  3 is challenger attack with critical
	*  4 is defender attack with critical
	*/
	event BattleHistory(
		uint256 historyId,
		uint8 winner, // 0 - challenger; 1 - defender; 2 - draw;
		uint64 battleTime,
		uint256 sequence,
		uint256 blockNumber,
		uint256 tokensGained);
	
	event BattleHistoryChallenger(
		uint256 historyId,
		uint256 cardId,
		uint8 element,
		uint16 level,
		uint32 attack,
		uint32 defense,
		uint32 hp,
		uint32 speed,
		uint32 criticalRate,
		uint256 rank);
		
	event BattleHistoryDefender(
		uint256 historyId,
		uint256 cardId,
		uint8 element,
		uint16 level,
		uint32 attack,
		uint32 defense,
		uint32 hp,
		uint32 speed,
		uint16 criticalRate,
		uint256 rank);
	
	event RejectChallenge(
		uint256 challengerId,
		uint256 defenderId,
		uint256 defenderRank,
		uint8 rejectCode,
		uint256 blockNumber);
		
	event HashUpdated(
		uint256 cardId, 
		uint256 cardHash);
		
	event LevelUp(uint256 cardId);
	
	event CardCreated(address owner, uint256 cardId);
	
	
	/***********************************************************************************/
	/* CONST DATA
	/***********************************************************************************/		
	uint32[] expToNextLevelArr = [0,103,103,207,207,207,414,414,414,414,724,724,724,828,828,931,931,1035,1035,1138,1138,1242,1242,1345,1345,1449,1449,1552,1552,1656,1656,1759,1759,1863,1863,1966,1966,2070,2070,2173,2173,2173,2277,2277,2380,2380,2484,2484,2587,2587,2691,2691,2794,2794,2898,2898,3001,3001,3105,3105,3208,3208,3312,3312,3415,3415,3519,3519,3622,3622,3622,3726,3726,3829,3829,3933,3933,4036,4036,4140,4140,4243,4243,4347,4347,4450,4450,4554,4554,4657,4657,4761,4761,4864,4864,4968,4968,5071,5071,5175];
	
	uint32[] activeWinExp = [10,11,14,19,26,35,46,59,74,91,100,103,108,116,125,135,146,158,171,185,200,215,231,248,265,283,302,321,341,361,382];
	
	
	/***********************************************************************************/
	/* DATA VARIABLES
	/***********************************************************************************/		
	//Card structure that holds all information for battle
	struct Card {
		uint8 element; // 1 - fire; 2 - water; 3 - wood;    8 - light; 9 - dark;
		uint16 level; //"unlimited" level bound to uint16 Max level is 65535
		uint32 attack;
		uint32 defense;
		uint32 hp;
		uint32 speed;
		uint16 criticalRate; //max 8000
		uint32 flexiGems;
		uint256 cardHash;
		uint32 currentExp;
		uint32 expToNextLevel;
		uint64 createdDatetime;

		uint256 rank; //rank is n-1 (need to add 1 for display);

		//uint8 passiveSkill; //TBC
	}
	
	// Mapping from tokenId to Card Struct
	mapping (uint256 => Card) public cards;
	
	uint256[] ranking; //stores the token id according to array position starts from 0 (rank 1)
	
	// Mapping from rank to amount held in that rank (challenge tokens)
	mapping (uint256 => uint256) public rankTokens;
	
	uint8 public currentElement = 0; //start with 0 as +1 will become fire
	
	uint256 public historyId = 0;
	
	/***********************************************************************************/
	/* CONFIGURATIONS
	/***********************************************************************************/
	/// @dev The address of the HogSmashToken
	HogSmashToken public hogsmashToken;
	
	/// @dev The address of the Marketplace
	Marketplace public marketplace;
			
	// Challenge fee changes on ranking difference
	uint256 public challengeFee;

	// Upgrade fee
	uint256 public upgradeFee;
	
	// Avatar fee
	uint256 public avatarFee;
	
	// Referrer fee in % (x10000)
	uint256 public referrerFee;
	
	// Developer Cut in % (x10000)
	uint256 public developerCut;
	
	uint256 internal totalDeveloperCut;

	// Price for each card draw (in wei)
	uint256 public cardDrawPrice;

	// Gems provided for upgrade every level up
	uint8 public upgradeGems; //
	// Gems provided for upgrade every 10 level up
	uint8 public upgradeGemsSpecial;
	// 1 Gem to attack conversion
	uint16 public gemAttackConversion;
	// 1 Gem to defense conversion
	uint16 public gemDefenseConversion;
	// 1 Gem to hp conversion
	uint16 public gemHpConversion;
	// 1 Gem to speed conversion
	uint16 public gemSpeedConversion;
	// 1 Gem to critical rate conversion divided by 100, eg 25 = 0.25
	uint16 public gemCriticalRateConversion;
	
	//% to get a gold card, 0 to 100
	uint8 public goldPercentage;
	
	//% to get a silver card, 0 to 100
	uint8 public silverPercentage;
 	
	//Range of event card number 1-99999999
	uint32 public eventCardRangeMin;
	
	//Range of event card number 1-99999999
	uint32 public eventCardRangeMax;
	
	// Maximum rounds of battle, cannot exceed 128
	uint8 public maxBattleRounds; //
		
	// Record how much tokens are held as rank tokens
	uint256 internal totalRankTokens;
	
	// Flag for start fighting
	bool internal battleStart;
	
	//Flag for starter pack sale
	bool internal starterPackOnSale;
	
	uint256 public starterPackPrice; //price of starter pack
	
	uint16 public starterPackCardLevel; //card level from starter pack
	
	
	/***********************************************************************************/
	/* ADMIN FUNCTIONS FOR SETTING CONFIGS
	/***********************************************************************************/		
	/// @dev Sets the reference to the marketplace.
	/// @param _address - Address of marketplace.
	function setMarketplaceAddress(address _address) external onlyOwner {
		Marketplace candidateContract = Marketplace(_address);

		require(candidateContract.isMarketplace(),"needs to be marketplace");

		// Set the new contract address
		marketplace = candidateContract;
	}
		
	/**
	* @dev set upgrade gems for each level up and each 10 level up
	* @param _upgradeGems upgrade gems for normal levels
	* @param _upgradeGemsSpecial upgrade gems for every n levels
	* @param _gemAttackConversion gem to attack conversion
	* @param _gemDefenseConversion gem to defense conversion
	* @param _gemHpConversion gem to hp conversion
	* @param _gemSpeedConversion gem to speed conversion
	* @param _gemCriticalRateConversion gem to critical rate conversion
	* @param _goldPercentage percentage to get gold card
	* @param _silverPercentage percentage to get silver card
	* @param _eventCardRangeMin event card hash range start (inclusive)
	* @param _eventCardRangeMax event card hash range end (inclusive)	
	* @param _newMaxBattleRounds maximum battle rounds
	*/
	function setSettingValues(  uint8 _upgradeGems,
	uint8 _upgradeGemsSpecial,
	uint16 _gemAttackConversion,
	uint16 _gemDefenseConversion,
	uint16 _gemHpConversion,
	uint16 _gemSpeedConversion,
	uint16 _gemCriticalRateConversion,
	uint8 _goldPercentage,
	uint8 _silverPercentage,
	uint32 _eventCardRangeMin,
	uint32 _eventCardRangeMax,
	uint8 _newMaxBattleRounds) external onlyOwner {
		require(_eventCardRangeMax >= _eventCardRangeMin, "range max must be larger or equals range min" );
		require(_eventCardRangeMax<100000000, "range max cannot exceed 99999999");
		require((_newMaxBattleRounds <= 128) && (_newMaxBattleRounds >0), "battle rounds must be between 0 and 128");
		upgradeGems = _upgradeGems;
		upgradeGemsSpecial = _upgradeGemsSpecial;
		gemAttackConversion = _gemAttackConversion;
		gemDefenseConversion = _gemDefenseConversion;
		gemHpConversion = _gemHpConversion;
		gemSpeedConversion = _gemSpeedConversion;
		gemCriticalRateConversion = _gemCriticalRateConversion;
		goldPercentage = _goldPercentage;
		silverPercentage = _silverPercentage;
		eventCardRangeMin = _eventCardRangeMin;
		eventCardRangeMax = _eventCardRangeMax;
		maxBattleRounds = _newMaxBattleRounds;
	}
	
	
	// @dev function to allow contract owner to change the price (in wei) per card draw
	function setStarterPack(uint256 _newStarterPackPrice, uint16 _newStarterPackCardLevel) external onlyOwner {
		require(_newStarterPackCardLevel<=20, "starter pack level cannot exceed 20"); //starter pack level cannot exceed 20
		starterPackPrice = _newStarterPackPrice;
		starterPackCardLevel = _newStarterPackCardLevel;		
	} 	
	
	// @dev function to allow contract owner to enable/disable starter pack sale
	function setStarterPackOnSale(bool _newStarterPackOnSale) external onlyOwner {
		starterPackOnSale = _newStarterPackOnSale;
	}
	
	// @dev function to allow contract owner to start/stop the battle
	function setBattleStart(bool _newBattleStart) external onlyOwner {
		battleStart = _newBattleStart;
	}
	
	// @dev function to allow contract owner to change the price (in wei) per card draw
	function setCardDrawPrice(uint256 _newCardDrawPrice) external onlyOwner {
		cardDrawPrice = _newCardDrawPrice;
	}
	
	// @dev function to allow contract owner to change the referrer fee (in %, eg 3.75% is 375)
	function setReferrerFee(uint256 _newReferrerFee) external onlyOwner {
		referrerFee = _newReferrerFee;
	}

	// @dev function to allow contract owner to change the challenge fee (in wei)
	function setChallengeFee(uint256 _newChallengeFee) external onlyOwner {
		challengeFee = _newChallengeFee;
	}

	// @dev function to allow contract owner to change the upgrade fee (in wei)
	function setUpgradeFee(uint256 _newUpgradeFee) external onlyOwner {
		upgradeFee = _newUpgradeFee;
	}
	
	// @dev function to allow contract owner to change the avatar fee (in wei)
	function setAvatarFee(uint256 _newAvatarFee) external onlyOwner {
		avatarFee = _newAvatarFee;
	}
	
	// @dev function to allow contract owner to change the developer cut (%) divide by 100
	function setDeveloperCut(uint256 _newDeveloperCut) external onlyOwner {
		developerCut = _newDeveloperCut;
	}
		
	function getTotalDeveloperCut() external view onlyOwner returns (uint256) {
		return totalDeveloperCut;
	}
		
	function getTotalRankTokens() external view returns (uint256) {
		return totalRankTokens;
	}
	
	
	/***********************************************************************************/
	/* GET SETTINGS FUNCTION
	/***********************************************************************************/	
	/**
	* @dev get upgrade gems and conversion ratios of each field
	* @return _upgradeGems upgrade gems for normal levels
	* @return _upgradeGemsSpecial upgrade gems for every n levels
	* @return _gemAttackConversion gem to attack conversion
	* @return _gemDefenseConversion gem to defense conversion
	* @return _gemHpConversion gem to hp conversion
	* @return _gemSpeedConversion gem to speed conversion
	* @return _gemCriticalRateConversion gem to critical rate conversion
	*/
	function getSettingValues() external view returns(  uint8 _upgradeGems,
															uint8 _upgradeGemsSpecial,
															uint16 _gemAttackConversion,
															uint16 _gemDefenseConversion,
															uint16 _gemHpConversion,
															uint16 _gemSpeedConversion,
															uint16 _gemCriticalRateConversion,
															uint8 _maxBattleRounds)
	{
		_upgradeGems = uint8(upgradeGems);
		_upgradeGemsSpecial = uint8(upgradeGemsSpecial);
		_gemAttackConversion = uint16(gemAttackConversion);
		_gemDefenseConversion = uint16(gemDefenseConversion);
		_gemHpConversion = uint16(gemHpConversion);
		_gemSpeedConversion = uint16(gemSpeedConversion);
		_gemCriticalRateConversion = uint16(gemCriticalRateConversion);
		_maxBattleRounds = uint8(maxBattleRounds);
	}
		

}
function setMarketplaceAddress(address _address) external onlyOwner {
	Marketplace candidateContract = Marketplace(_address);

	require(candidateContract.isMarketplace(),"needs to be marketplace");

	// Set the new contract address
	marketplace = candidateContract;
}

/// @dev Sets the reference to the marketplace.
/// @param _address - Address of marketplace.