contract CryptoWarriorPVP is CryptoWarriorSanctuary {
	
	PVPInterface public battleProvider;
	
	/// @dev Sets the reference to the sale auction.
    /// @param _address - Address of sale contract.
    function setBattleProviderAddress(address _address) external onlyAdmin {
        PVPInterface candidateContract = PVPInterface(_address);

        // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117
        require(candidateContract.isPVPProvider());

        // Set the new contract address
        battleProvider = candidateContract;
    }
    
    function _packPVPData(uint256 _warriorId, DataTypes.Warrior storage warrior) internal view returns(uint256){
        return CryptoUtils._packWarriorPvpData(warrior.identity, uint256(warrior.rating), 0, _warriorId, warrior.level);
    }
    
    function _triggerPVPSignUp(uint256 _warriorId, uint256 fee) internal {
        DataTypes.Warrior storage warrior = warriors[_warriorId];
    		
		uint256 packedWarrior = _packPVPData(_warriorId, warrior);
        
        // addPVPContender will throw if fee fails.
        battleProvider.addPVPContender.value(fee)(msg.sender, packedWarrior);
        
        warrior.action = uint16(PVP_BATTLE);
    }
    
    /*
     * @title signUpForPVP enqueues specified warrior to PVP
     * 
     * @dev When the owner enqueues his warrior for PvP, the warrior enters the waiting room.
     * Once every 15 minutes, we check the warriors in the room and select pairs. 
     * For those warriors to whom we found couples, fighting is conducted and the results 
     * are recorded in the profile of the warrior. 
     */
    function signUpForPVP(uint256 _warriorId) public payable whenNotPaused {//done
		// Caller must own the warrior.
        require(_ownerApproved(msg.sender, _warriorId));
        // Grab a reference to the warrior in storage.
        DataTypes.Warrior storage warrior = warriors[_warriorId];
        // sanity check
        require(warrior.identity != 0);

        // Check that the warrior is ready to battle
        require(warrior.action == IDLE);
        
        // Define the current price of the auction.
        uint256 fee = battleProvider.getPVPEntranceFee(warrior.level);
        
        // Checks for payment.
        require(msg.value >= fee);
        
        // All checks passed, put the warrior to the queue!
        _triggerPVPSignUp(_warriorId, fee);
        
        // Calculate any excess funds included in msg.value. If the excess
        // is anything worth worrying about, transfer it back to message owner.
        // NOTE: We checked above that the msg.value is greater than or
        // equal to the price so this cannot underflow.
        uint256 feeExcess = msg.value - fee;

        // Return the funds. This is not susceptible 
        // to a re-entry attack because of warrior.action == IDLE check
        // will fail
        msg.sender.transfer(feeExcess);
    }

    function _grandPVPWinnerReward(uint256 _warriorId) internal {
        DataTypes.Warrior storage warrior = warriors[_warriorId];
        // reward 1 level, add 10 level points
        uint256 level = warrior.level;
        if (level < (MAX_LEVEL * POINTS_TO_LEVEL)) {
            level = level + POINTS_TO_LEVEL;
			warrior.level = uint64(level > (MAX_LEVEL * POINTS_TO_LEVEL) ? (MAX_LEVEL * POINTS_TO_LEVEL) : level);
        }
		// give 100 rating for levelUp and 30 for win
		warrior.rating += 130;
		// mark warrior idle, so it can participate
		// in another actions
		warrior.action = uint16(IDLE);
    }

    function _grandPVPLoserReward(uint256 _warriorId) internal {
        DataTypes.Warrior storage warrior = warriors[_warriorId];
		// reward 0.5 level
		uint256 oldLevel = warrior.level;
		uint256 level = oldLevel;
		if (level < (MAX_LEVEL * POINTS_TO_LEVEL)) {
            level += (POINTS_TO_LEVEL / 2);
			warrior.level = uint64(level);
        }
		// give 100 rating for levelUp if happens and -30 for lose
		int256 newRating = warrior.rating + (CryptoUtils._getLevel(level) > CryptoUtils._getLevel(oldLevel) ? int256(100 - 30) : int256(-30));
		// rating can't be less than 0 and more than 1000000000
	    warrior.rating = int64((newRating >= 0) ? (newRating > 1000000000 ? 1000000000 : newRating) : 0);
        // mark warrior idle, so it can participate
		// in another actions
	    warrior.action = uint16(IDLE);
    }
    
    function _grandPVPRewards(uint256[] memory warriorsData, uint256 matchingCount) internal {
        for(uint256 id = 0; id < matchingCount; id += 2){
            //
            // winner, even ids are winners!
            _grandPVPWinnerReward(CryptoUtils._unpackIdValue(warriorsData[id]));
            //
            // loser, they are odd...
            _grandPVPLoserReward(CryptoUtils._unpackIdValue(warriorsData[id + 1]));
        }
	}

    // @dev Internal utility function to initiate pvp battle, assumes that all battle
    ///  requirements have been checked.
    function pvpFinished(uint256[] warriorsData, uint256 matchingCount) public {
        //this method can be invoked only by battleProvider contract
        require(msg.sender == address(battleProvider));
        
        _grandPVPRewards(warriorsData, matchingCount);
    }
    
    function pvpContenderRemoved(uint256 _warriorId) public {
        //this method can be invoked only by battleProvider contract
        require(msg.sender == address(battleProvider));
        //grab warrior storage reference
        DataTypes.Warrior storage warrior = warriors[_warriorId];
        //specified warrior must be in pvp state
        require(warrior.action == PVP_BATTLE);
        //all checks done
        //set warrior state to IDLE
        warrior.action = uint16(IDLE);
    }
}
function setBattleProviderAddress(address _address) external onlyAdmin {
    PVPInterface candidateContract = PVPInterface(_address);

    // NOTE: verify that a contract is what we expect - https://github.com/Lunyr/crowdsale-contracts/blob/cfadd15986c30521d8ba7d5b6f57b4fefcc7ac38/contracts/LunyrToken.sol#L117
    require(candidateContract.isPVPProvider());

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

/// @dev Sets the reference to the sale auction.
/// @param _address - Address of sale contract.