contract EtheremonPayment is EtheremonEnum, BasicAccessControl, SafeMath {
    uint8 constant public STAT_COUNT = 6;
    uint8 constant public STAT_MAX = 32;
    uint8 constant public GEN0_NO = 24;
    
    struct MonsterClassAcc {
        uint32 classId;
        uint256 price;
        uint256 returnPrice;
        uint32 total;
        bool catchable;
    }

    struct MonsterObjAcc {
        uint64 monsterId;
        uint32 classId;
        address trainer;
        string name;
        uint32 exp;
        uint32 createIndex;
        uint32 lastClaimIndex;
        uint createTime;
    }
    
    // linked smart contract
    address public dataContract;
    address public battleContract;
    address public tokenContract;
    
    address private lastHunter = address(0x0);
    
    // config
    uint public brickPrice = 2 * 10 ** 8; // 2 tokens
    uint public tokenPrice = 0.004 ether / 10 ** 8;
    uint public maxDexSize = 500;
    
    // event
    event EventCatchMonster(address indexed trainer, uint64 objId);
    
    // modifier
    modifier requireDataContract {
        require(dataContract != address(0));
        _;        
    }
    
    modifier requireBattleContract {
        require(battleContract != address(0));
        _;
    }

    modifier requireTokenContract {
        require(tokenContract != address(0));
        _;
    }
    
    function EtheremonPayment(address _dataContract, address _battleContract, address _tokenContract) public {
        dataContract = _dataContract;
        battleContract = _battleContract;
        tokenContract = _tokenContract;
    }
    
    // helper
    function getRandom(uint8 maxRan, uint8 index, address priAddress) constant public returns(uint8) {
        uint256 genNum = uint256(block.blockhash(block.number-1)) + uint256(priAddress);
        for (uint8 i = 0; i < index && i < 6; i ++) {
            genNum /= 256;
        }
        return uint8(genNum % maxRan);
    }
    
    // admin
    function withdrawToken(address _sendTo, uint _amount) onlyModerators requireTokenContract external {
        ERC20Interface token = ERC20Interface(tokenContract);
        if (_amount > token.balanceOf(address(this))) {
            revert();
        }
        token.transfer(_sendTo, _amount);
    }
    
    function setContract(address _dataContract, address _battleContract, address _tokenContract) onlyModerators external {
        dataContract = _dataContract;
        battleContract = _battleContract;
        tokenContract = _tokenContract;
    }
    
    function setConfig(uint _brickPrice, uint _tokenPrice, uint _maxDexSize) onlyModerators external {
        brickPrice = _brickPrice;
        tokenPrice = _tokenPrice;
        maxDexSize = _maxDexSize;
    }
    
    // battle
    function giveBattleBonus(address _trainer, uint _amount) isActive requireBattleContract requireTokenContract public {
        if (msg.sender != battleContract)
            revert();
        ERC20Interface token = ERC20Interface(tokenContract);
        token.transfer(_trainer, _amount);
    }
    
    function createCastle(address _trainer, uint _tokens, string _name, uint64 _a1, uint64 _a2, uint64 _a3, uint64 _s1, uint64 _s2, uint64 _s3) isActive requireBattleContract requireTokenContract public returns(uint){
        if (msg.sender != tokenContract)
            revert();
        BattleInterface battle = BattleInterface(battleContract);
        battle.createCastleWithToken(_trainer, uint32(_tokens/brickPrice), _name, _a1, _a2, _a3, _s1, _s2, _s3);
        return _tokens;
    }
    
    function catchMonster(address _trainer, uint _tokens, uint32 _classId, string _name) isActive requireDataContract requireTokenContract public returns(uint){
        if (msg.sender != tokenContract)
            revert();
        
        EtheremonDataBase data = EtheremonDataBase(dataContract);
        MonsterClassAcc memory class;
        (class.classId, class.price, class.returnPrice, class.total, class.catchable) = data.getMonsterClass(_classId);
        
        if (class.classId == 0 || class.catchable == false) {
            revert();
        }
        
        // can not keep too much etheremon 
        if (data.getMonsterDexSize(_trainer) > maxDexSize)
            revert();

        uint requiredToken = class.price/tokenPrice;
        if (_tokens < requiredToken)
            revert();

        // add monster
        uint64 objId = data.addMonsterObj(_classId, _trainer, _name);
        // generate base stat for the previous one
        for (uint i=0; i < STAT_COUNT; i+= 1) {
            uint8 value = getRandom(STAT_MAX, uint8(i), lastHunter) + data.getElementInArrayType(ArrayType.STAT_START, uint64(_classId), i);
            data.addElementToArrayType(ArrayType.STAT_BASE, objId, value);
        }
        
        lastHunter = _trainer;
        EventCatchMonster(_trainer, objId);
        return requiredToken;
    }
    
    /*
    function payService(address _trainer, uint _tokens, uint32 _type, string _text, uint64 _param1, uint64 _param2, uint64 _param3) public returns(uint) {
        if (msg.sender != tokenContract)
            revert();
    }*/
}
function giveBattleBonus(address _trainer, uint _amount) isActive requireBattleContract requireTokenContract public {
    if (msg.sender != battleContract)
        revert();
    ERC20Interface token = ERC20Interface(tokenContract);
    token.transfer(_trainer, _amount);
}

// battle