contract DragonCoin is StandardToken, Usable {
    using SafeMath for uint256;
    
    event Mint(address indexed to, uint256 value);
    event Burn(address indexed burner, uint256 value);
    
    string public name = "DragonSeriesToken"; 
    string public symbol = "DST";
    uint public decimals = 18;
    uint public INITIAL_SUPPLY = 1100000 * (10 ** decimals);     
    uint public MAX_SUPPLY = 10 * 100000000 * (10 ** decimals); 
    address public ceo;
    address public coo;
    address public cfo;

    UseInterface public useAddr;

    // key = useType, value = UseFee
    mapping (uint256 => UseFee) public useFees;
    uint private _maxPercentFee = 1000000;

    struct UseFee {
        uint16 feeType;     // 1: fixed, 2: percentage
        uint256 fee;        // feeType=1: DST, feeType=2: 0-1000000 (0.0000% - 100.0000%)
    }
    
    function DragonCoin() public {
        totalSupply_ = INITIAL_SUPPLY;
        balances[msg.sender] = INITIAL_SUPPLY;
        ceo = msg.sender;
        emit Transfer(0x0, msg.sender, INITIAL_SUPPLY);
    }
    
    function setCEO(address newCEO) public onlyCEO{
        require(newCEO != address(0));
        
        ceo = newCEO;
    }
    
    function setCOO(address newCOO) public onlyCEO{
        require(newCOO != address(0));
        
        coo = newCOO;
    }
    
    function setCFO(address newCFO) public onlyCEO{
        require(newCFO != address(0));
        
        cfo = newCFO;
    }

    function mint(uint256 value) public onlyCFO returns (bool) {
        require(totalSupply_.add(value) <= MAX_SUPPLY);
        
        balances[cfo] = balances[cfo].add(value);
        totalSupply_ = totalSupply_.add(value);
        
        // mint event
        emit Mint(cfo, value);
        emit Transfer(0x0, cfo, value);
        return true;
    }
    
    function burn(uint256 value) public onlyCOO returns (bool) {
        require(balances[coo] >= value); 
        
        balances[coo] = balances[coo].sub(value);
        totalSupply_ = totalSupply_.sub(value);
        
        // burn event
        emit Burn(coo, value);
        emit Transfer(coo, 0x0, value);
        return true;
    }

    // Useable

    function setUseAddr(address addr) external onlyCOO{
        useAddr = UseInterface(addr);
    }

    function setFee(uint256 useType, uint16 feeType, uint256 fee) external onlyCOO{
        require(feeType == 1 || feeType == 2);

        if(feeType == 2){
            require(fee <= _maxPercentFee);
        }

        UseFee memory ufee = UseFee({
            feeType: feeType,
            fee: fee
        });
        useFees[useType] = ufee;
    }

    function use(address to, uint256 value, uint256 useType, uint256 param1, uint256 param2, uint256 param3, string param4) external {
        require(useAddr != address(0));
        require(balances[msg.sender] >= value);
        require(to != address(0) && cfo != address(0));
        
        UseFee memory ufee = useFees[useType];

        require(ufee.feeType == 1 || ufee.feeType == 2);
        
        uint256 actualFee = 0;

        if(ufee.feeType == 1){  // fixed fee
            actualFee = ufee.fee;
        }else if(ufee.feeType == 2){  // percentage fee
            actualFee = value.mul(ufee.fee).div(_maxPercentFee);
        }

        uint256 actualVal = value.sub(actualFee);

        if(useAddr.use(msg.sender, to, value, useType, param1, param2, param3, param4)){
            // transfer token
            balances[msg.sender] = balances[msg.sender].sub(value);
            balances[to] = balances[to].add(actualVal);
            emit Transfer(msg.sender, to, actualVal);

            if(actualFee > 0){
                balances[cfo] = balances[cfo].add(actualFee);
                emit Transfer(msg.sender, cfo, actualFee);
            }

            emit UseSucc(msg.sender, to, useType, value, actualFee, param1, param2, param3, param4);
        }else{
            emit UseFail(msg.sender, to, useType, value, actualFee, param1, param2, param3, param4);
        }
    }

    /// @dev Access modifier for CEO-only functionality
    modifier onlyCEO() {
        require(msg.sender == ceo);
        _;
    }
    
    /// @dev Access modifier for CFO-only functionality
    modifier onlyCFO() {
        require(msg.sender == cfo);
        _;
    }
    
    /// @dev Access modifier for COO-only functionality
    modifier onlyCOO() {
        require(msg.sender == coo);
        _;
    }
    
}
function setUseAddr(address addr) external onlyCOO{
    useAddr = UseInterface(addr);
}

// Useable