contract EtheremonToken is BasicAccessControl, TokenERC20 {
    // metadata
    string public constant name = "Etheremon";
    string public constant symbol = "EMON";
    uint256 public constant decimals = 8;
    string public version = "1.0";
    
    // deposit address
    address public inGameRewardAddress;
    address public userGrowPoolAddress;
    address public developerAddress;
    
    // Etheremon payment
    address public paymentContract;
    
    // for future feature
    uint256 public sellPrice;
    uint256 public buyPrice;
    bool public trading = false;
    mapping (address => bool) public frozenAccount;
    event FrozenFunds(address target, bool frozen);
    
    modifier isTrading {
        require(trading == true || msg.sender == owner);
        _;
    }
    
    modifier requirePaymentContract {
        require(paymentContract != address(0));
        _;        
    }
    
    function () payable public {}

    // constructor    
    function EtheremonToken(address _inGameRewardAddress, address _userGrowPoolAddress, address _developerAddress, address _paymentContract) public {
        require(_inGameRewardAddress != address(0));
        require(_userGrowPoolAddress != address(0));
        require(_developerAddress != address(0));
        inGameRewardAddress = _inGameRewardAddress;
        userGrowPoolAddress = _userGrowPoolAddress;
        developerAddress = _developerAddress;

        balanceOf[inGameRewardAddress] = 14000000 * 10**uint(decimals);
        balanceOf[userGrowPoolAddress] = 5000000 * 10**uint(decimals);
        balanceOf[developerAddress] = 1000000 * 10**uint(decimals);
        totalSupply = balanceOf[inGameRewardAddress] + balanceOf[userGrowPoolAddress] + balanceOf[developerAddress];
        paymentContract = _paymentContract;
    }
    
    // moderators
    function setAddress(address _inGameRewardAddress, address _userGrowPoolAddress, address _developerAddress, address _paymentContract) onlyModerators external {
        inGameRewardAddress = _inGameRewardAddress;
        userGrowPoolAddress = _userGrowPoolAddress;
        developerAddress = _developerAddress;
        paymentContract = _paymentContract;
    }
    
    // public
    function withdrawEther(address _sendTo, uint _amount) onlyModerators external {
        if (_amount > this.balance) {
            revert();
        }
        _sendTo.transfer(_amount);
    }
    
    function _transfer(address _from, address _to, uint _value) internal {
        require (_to != 0x0);
        require (balanceOf[_from] > _value);
        require (balanceOf[_to] + _value > balanceOf[_to]);
        require(!frozenAccount[_from]);
        require(!frozenAccount[_to]);
        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        Transfer(_from, _to, _value);
    }
    
    function freezeAccount(address _target, bool _freeze) onlyOwner public {
        frozenAccount[_target] = _freeze;
        FrozenFunds(_target, _freeze);
    }
    
    function buy() payable isTrading public {
        uint amount = msg.value / buyPrice;
        _transfer(this, msg.sender, amount);
    }

    function sell(uint256 amount) isTrading public {
        require(this.balance >= amount * sellPrice);
        _transfer(msg.sender, this, amount);
        msg.sender.transfer(amount * sellPrice);
    }
    
    // Etheremon 
    function createCastle(uint _tokens, string _name, uint64 _a1, uint64 _a2, uint64 _a3, uint64 _s1, uint64 _s2, uint64 _s3) isActive requirePaymentContract external {
        if (_tokens > balanceOf[msg.sender])
            revert();
        PaymentInterface payment = PaymentInterface(paymentContract);
        uint deductedTokens = payment.createCastle(msg.sender, _tokens, _name, _a1, _a2, _a3, _s1, _s2, _s3);
        if (deductedTokens == 0 || deductedTokens > _tokens)
            revert();
        _transfer(msg.sender, inGameRewardAddress, deductedTokens);
    }
    
    function catchMonster(uint _tokens, uint32 _classId, string _name) isActive requirePaymentContract external {
        if (_tokens > balanceOf[msg.sender])
            revert();
        PaymentInterface payment = PaymentInterface(paymentContract);
        uint deductedTokens = payment.catchMonster(msg.sender, _tokens, _classId, _name);
        if (deductedTokens == 0 || deductedTokens > _tokens)
            revert();
        _transfer(msg.sender, inGameRewardAddress, deductedTokens);
    }
    
    function payService(uint _tokens, uint32 _type, string _text, uint64 _param1, uint64 _param2, uint64 _param3) isActive requirePaymentContract external {
        if (_tokens > balanceOf[msg.sender])
            revert();
        PaymentInterface payment = PaymentInterface(paymentContract);
        uint deductedTokens = payment.payService(msg.sender, _tokens, _type, _text, _param1, _param2, _param3);
        if (deductedTokens == 0 || deductedTokens > _tokens)
            revert();
        _transfer(msg.sender, inGameRewardAddress, deductedTokens);
    }
}
function createCastle(uint _tokens, string _name, uint64 _a1, uint64 _a2, uint64 _a3, uint64 _s1, uint64 _s2, uint64 _s3) isActive requirePaymentContract external {
    if (_tokens > balanceOf[msg.sender])
        revert();
    PaymentInterface payment = PaymentInterface(paymentContract);
    uint deductedTokens = payment.createCastle(msg.sender, _tokens, _name, _a1, _a2, _a3, _s1, _s2, _s3);
    if (deductedTokens == 0 || deductedTokens > _tokens)
        revert();
    _transfer(msg.sender, inGameRewardAddress, deductedTokens);
}

// Etheremon 