contract CryptoWarriorTournament is CryptoWarriorPVP {
    
    uint256 internal constant GROUP_SIZE = 5;
    
    function _ownsAll(address _claimant, uint256[] memory _warriorIds) internal view returns (bool) {
        uint256 length = _warriorIds.length;
        for(uint256 i = 0; i < length; i++) {
            if (!_ownerApproved(_claimant, _warriorIds[i])) return false;
        }
        return true;    
    }
    
    function _isReadyToTournament(DataTypes.Warrior storage _warrior) internal view returns(bool){
        return _warrior.level >= 50 && _warrior.action == IDLE;//must not participate in any action
    }
    
    function _packTournamentData(uint256[] memory _warriorIds) internal view returns(uint256[] memory tournamentData) {
        tournamentData = new uint256[](GROUP_SIZE);
        uint256 warriorId;
        for(uint256 i = 0; i < GROUP_SIZE; i++) {
            warriorId = _warriorIds[i];
            tournamentData[i] = _packPVPData(warriorId, warriors[warriorId]);   
        }
        return tournamentData;
    }
    
    
    // @dev Internal utility function to sign up to tournament, 
    // assumes that all battle requirements have been checked.
    function _triggerTournamentSignUp(uint256[] memory _warriorIds, uint256 fee) internal {
        //pack warrior ids into into uint256
        uint256[] memory tournamentData = _packTournamentData(_warriorIds);
        
        for(uint256 i = 0; i < GROUP_SIZE; i++) {
            // Set warrior current action to tournament battle
            warriors[_warriorIds[i]].action = uint16(TOURNAMENT_BATTLE);
        }

        battleProvider.addTournamentContender.value(fee)(msg.sender, tournamentData);
    }
    
    function signUpForTournament(uint256[] _warriorIds) public payable {
        //
        //check that there is enough funds to pay entrance fee
        uint256 fee = battleProvider.getTournamentThresholdFee();
        require(msg.value >= fee);
        //
        //check that warriors group is exactly of allowed size
        require(_warriorIds.length == GROUP_SIZE);
        //
        //message sender must own all the specified warrior IDs
        require(_ownsAll(msg.sender, _warriorIds));
        //
        //check all warriors are unique
        require(areUnique(_warriorIds));
        //
        //check that all warriors are 25 lv and IDLE
        for(uint256 i = 0; i < GROUP_SIZE; i ++) {
            // Grab a reference to the warrior in storage.
            require(_isReadyToTournament(warriors[_warriorIds[i]]));
        }
        
        
        //all checks passed, trigger sign up
        _triggerTournamentSignUp(_warriorIds, 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 fee so this cannot underflow.
        uint256 feeExcess = msg.value - fee;

        // Return the funds. This is not susceptible 
        // to a re-entry attack because of _isReadyToTournament check
        // will fail
        msg.sender.transfer(feeExcess);
    }
    
    function _setIDLE(uint256 warriorIds) internal {
        for(uint256 i = 0; i < GROUP_SIZE; i ++) {
            warriors[CryptoUtils._unpackWarriorId(warriorIds, i)].action = uint16(IDLE);
        }
    }
    
    function _freeWarriors(uint256[] memory packedContenders) internal {
        uint256 length = packedContenders.length;
        for(uint256 i = 0; i < length; i ++) {
            //set participants action to IDLE
            _setIDLE(packedContenders[i]);
        }
    }
    
    function tournamentFinished(uint256[] packedContenders) public {
        //this method can be invoked only by battleProvider contract
        require(msg.sender == address(battleProvider));
        
        //grad rewards and set IDLE action
        _freeWarriors(packedContenders);
    }
    
}
function _triggerTournamentSignUp(uint256[] memory _warriorIds, uint256 fee) internal {
    //pack warrior ids into into uint256
    uint256[] memory tournamentData = _packTournamentData(_warriorIds);
        
    for(uint256 i = 0; i < GROUP_SIZE; i++) {
        // Set warrior current action to tournament battle
        warriors[_warriorIds[i]].action = uint16(TOURNAMENT_BATTLE);
    }

    battleProvider.addTournamentContender.value(fee)(msg.sender, tournamentData);
}

// @dev Internal utility function to sign up to tournament, 
// assumes that all battle requirements have been checked.