contract TopChainCoinDistribution is Ownable, Authorizable {
    using SafeMath for uint;

    event AuthorizedCreateToPrivate(address recipient, uint pay_amount);
    event GameMining(address recipient, uint pay_amount);
    event CreateTokenToTeam(address recipient, uint pay_amount);
    event CreateTokenToMarket(address recipient, uint pay_amount);
    event CreateTokenToOperation(address recipient, uint pay_amount);
    event TopChainCoinMintFinished();

    TopChainCoin public token = new TopChainCoin();
    DateTime internal dateTime = new DateTime();

    uint totalToken = 2100000000 * (10 ** 6); //21亿

    uint public privateTokenCap = 210000000 * (10 ** 6); //私募发行2.1亿(10%)

    uint public marketToken = 315000000 * (10 ** 6); //全球推广3.15亿(15%)

    uint public operationToken = 210000000 * (10 ** 6); //社区运营2.1亿(10%)

    uint public gameMiningTokenCap = 1155000000 * (10 ** 6); //游戏挖矿11.55亿(55%)

    uint public teamToken2018 = 105000000 * (10 ** 6); //团队预留2.1亿(10%),2018年发放1.05亿(5%)
    uint public teamToken2019 = 105000000 * (10 ** 6); //团队预留2.1亿(10%),2019年发放1.05亿(5%)

    uint public privateToken = 0; //私募已发行数量

    address public teamAddress;
    address public operationAddress;
    address public marketAddress;

    bool public team2018TokenCreated = false;
    bool public team2019TokenCreated = false;
    bool public operationTokenCreated = false;
    bool public marketTokenCreated = false;

    //year => token
    mapping(uint16 => uint) public gameMiningToken; //游戏挖矿已发行数量

    uint public firstYearGameMiningTokenCap = 577500000 * (10 ** 6); //2018年5.775亿(21亿*0.55*0.5)，以后逐年减半 

    uint public gameMiningTokenStartTime = 1514736000; //new Date("Jan 01 2018 00:00:00 GMT+8").getTime() / 1000;

    function isContract(address _addr) internal view returns(bool) {
        uint size;
        if (_addr == 0) 
            return false;

        assembly {
        size := extcodesize(_addr)
        }
        return size > 0;
    }

    //2018年5.775亿(21亿*0.55*0.5)，以后逐年减半，到2028年发放剩余的全部
    function getCurrentYearGameMiningTokenCap(uint _currentYear) public view returns(uint) {
        require(_currentYear <= 2028);

        if (_currentYear < 2028) {
            uint divTimes = 2 ** (_currentYear - 2018);
            uint currentYearGameMiningTokenCap = firstYearGameMiningTokenCap.div(divTimes).div(10 ** 6).mul(10 ** 6);
            return currentYearGameMiningTokenCap;
        } else if (_currentYear == 2028) {
            return 1127932 * (10 ** 6);
        } else {
            revert();
        }
    }

    function getCurrentYearGameMiningRemainToken(uint16 _currentYear) public view returns(uint) {
        uint currentYearGameMiningTokenCap = getCurrentYearGameMiningTokenCap(_currentYear);

         if (gameMiningToken[_currentYear] == 0) {
             return currentYearGameMiningTokenCap;
         } else {
             return currentYearGameMiningTokenCap.sub(gameMiningToken[_currentYear]);
         }
    }

    function setTeamAddress(address _address) public onlyAuthorized {
        teamAddress = _address;
    }

    function setMarketAddress(address _address) public onlyAuthorized {
        marketAddress = _address;
    }

    function setOperationAddress(address _address) public onlyAuthorized {
        operationAddress = _address;
    }

    function createTokenToMarket() public onlyAuthorized {
        require(marketAddress != address(0));
        require(marketTokenCreated == false);

        marketTokenCreated = true;
        token.mint(marketAddress, marketToken);
        CreateTokenToMarket(marketAddress, marketToken);
    }

    function createTokenToOperation() public onlyAuthorized {
        require(operationAddress != address(0));
        require(operationTokenCreated == false);

        operationTokenCreated = true;
        token.mint(operationAddress, operationToken);
        CreateTokenToOperation(operationAddress, operationToken);
    }

    function _createTokenToTeam(uint16 _currentYear) internal {
        if (_currentYear == 2018) {
            require(team2018TokenCreated == false);
            team2018TokenCreated = true;
            token.mint(teamAddress, teamToken2018);
            CreateTokenToTeam(teamAddress, teamToken2018);
        } else if (_currentYear == 2019) {
            require(team2019TokenCreated == false);
            team2019TokenCreated = true;
            token.mint(teamAddress, teamToken2019);
            CreateTokenToTeam(teamAddress, teamToken2019);
        } else {
            revert();
        }
    }

    function createTokenToTeam() public onlyAuthorized {
        require(teamAddress != address(0));
        uint16 currentYear = dateTime.getYear(now);
        require(currentYear == 2018 || currentYear == 2019);
        _createTokenToTeam(currentYear);
    }

    function gameMining(address recipient, uint _tokens) public onlyAuthorized {
        require(now > gameMiningTokenStartTime);
        uint16 currentYear = dateTime.getYear(now);
        uint currentYearRemainTokens = getCurrentYearGameMiningRemainToken(currentYear);
        require(_tokens <= currentYearRemainTokens);

        gameMiningToken[currentYear] += _tokens; 

        token.mint(recipient, _tokens);
        GameMining(recipient, _tokens); 
    }

    function authorizedCreateTokensToPrivate(address recipient, uint _tokens) public onlyAuthorized {
        require(privateToken + _tokens <= privateTokenCap);
        privateToken += _tokens;
        token.mint(recipient, _tokens);
        AuthorizedCreateToPrivate(recipient, _tokens);
    }

    function finishMinting() public onlyOwner {
        token.finishMinting();
        token.transferOwnership(owner);
        TopChainCoinMintFinished();
    }

    //不允许直接转账以太币购买
    function () external {
        revert();
    }
}
/**
 * @title TopChainCoinDistribution
 * @dev The main TOPC token sale contract
 *
 * ABI
 */
function getCurrentYearGameMiningTokenCap(uint _currentYear) public view returns(uint) {
    require(_currentYear <= 2028);

    if (_currentYear < 2028) {
        uint divTimes = 2 ** (_currentYear - 2018);
        uint currentYearGameMiningTokenCap = firstYearGameMiningTokenCap.div(divTimes).div(10 ** 6).mul(10 ** 6);
        return currentYearGameMiningTokenCap;
    } else if (_currentYear == 2028) {
        return 1127932 * (10 ** 6);
    } else {
        revert();
    }
}

//2018年5.775亿(21亿*0.55*0.5)，以后逐年减半，到2028年发放剩余的全部