contract GolixTokenDistribution is FinalizableCrowdsale {
    uint256 constant public TOTAL_TOKENS_SUPPLY = 1274240097e18; // 1,274,240,097 tokens
    // =~ 10% for Marketing, investment fund, partners
    uint256 constant public MARKETING_SHARE = 127424009e18;
    // =~ 15% for issued to investors, shareholders
    uint256 constant public SHAREHOLDERS_SHARE = 191136015e18;
    // =~ 25% for founding team, future employees
    uint256 constant public FOUNDERS_SHARE = 318560024e18;
    uint256 constant public TOTAL_TOKENS_FOR_CROWDSALE = 637120049e18; // =~ 50 % of total token supply

    VestTokenAllocation public teamVestTokenAllocation;
    VestTokenAllocation public contributorsVestTokenAllocation;
    address public marketingWallet;
    address public shareHoldersWallet;

    bool public canFinalizeEarly;
    bool public isStakingPeriod;

    mapping (address => uint256) public icoContributions;

    event MintedTokensFor(address indexed investor, uint256 tokensPurchased);
    event GLXStaked(address indexed staker, uint256 amount);

    /**
     * @dev Contract constructor function
     * @param _startTime The timestamp of the beginning of the crowdsale
     * @param _endTime Timestamp when the crowdsale will finish
     * @param _rate The token rate per ETH
     * @param _wallet Multisig wallet that will hold the crowdsale funds.
     * @param _marketingWallet address that will hold tokens for marketing campaign.
     * @param _shareHoldersWallet address that will distribute shareholders tokens.
     */
    function GolixTokenDistribution
        (
            uint256 _startTime,
            uint256 _endTime,
            uint256 _rate,
            address _wallet,
            address _marketingWallet,
            address _shareHoldersWallet
        )
        public
        FinalizableCrowdsale()
        Crowdsale(_startTime, _endTime, _rate, _wallet)
    {
        require(_marketingWallet != address(0) && _shareHoldersWallet != address(0));
        require(
            MARKETING_SHARE + SHAREHOLDERS_SHARE + FOUNDERS_SHARE + TOTAL_TOKENS_FOR_CROWDSALE
            == TOTAL_TOKENS_SUPPLY
        );

        marketingWallet = _marketingWallet;
        shareHoldersWallet = _shareHoldersWallet;

        GolixToken(token).pause();
    }

    /**
     * @dev Mint tokens for crowdsale participants
     * @param investorsAddress List of Purchasers addresses
     * @param amountOfTokens List of token amounts for investor
     */
    function mintTokensForCrowdsaleParticipants(address[] investorsAddress, uint256[] amountOfTokens)
        external
        onlyOwner
    {
        require(investorsAddress.length == amountOfTokens.length);

        for (uint256 i = 0; i < investorsAddress.length; i++) {
            require(token.totalSupply().add(amountOfTokens[i]) <= TOTAL_TOKENS_FOR_CROWDSALE);

            token.mint(investorsAddress[i], amountOfTokens[i]);
            icoContributions[investorsAddress[i]] = icoContributions[investorsAddress[i]].add(amountOfTokens[i]);

            emit MintedTokensFor(investorsAddress[i], amountOfTokens[i]);
        }
    }
    
    // override buytokens so all minting comes from Golix
    function buyTokens(address beneficiary) public payable {
        revert();
    }
    
    /**
     * @dev Set addresses which should receive the vested team tokens share on finalization
     * @param _teamVestTokenAllocation address of team and advisor allocation contract
     * @param _contributorsVestTokenAllocation address of ico contributors
     * who for glx staking event in case there is still left over tokens from crowdsale
     */
    function setVestTokenAllocationAddresses
        (
            address _teamVestTokenAllocation,
            address _contributorsVestTokenAllocation
        )
        public
        onlyOwner
    {
        require(_teamVestTokenAllocation != address(0) && _contributorsVestTokenAllocation != address(0));

        teamVestTokenAllocation = VestTokenAllocation(_teamVestTokenAllocation);
        contributorsVestTokenAllocation = VestTokenAllocation(_contributorsVestTokenAllocation);
    }

    // overriding Crowdsale#hasEnded to add cap logic
    // @return true if crowdsale event has ended
    function hasEnded() public view returns (bool) {
        if (canFinalizeEarly) {
            return true;
        }

        return super.hasEnded();
    }

    /**
     * @dev Allow for staking of GLX tokens from crowdsale participants
     * only works if tokens from token distribution are not sold out.
     * investors must have GLX tokens in the same amount as it purchased during crowdsale
     */
    function stakeGLXForContributors() public {
        uint256 senderGlxBalance = token.balanceOf(msg.sender);
        require(senderGlxBalance == icoContributions[msg.sender] && isStakingPeriod);

        GolixToken(token).stakeGLX(msg.sender, contributorsVestTokenAllocation);
        contributorsVestTokenAllocation.addVestTokenAllocation(msg.sender, senderGlxBalance);
        emit GLXStaked(msg.sender, senderGlxBalance);
    }

    /**
    * @dev enables early finalization of crowdsale
    */
    function prepareForEarlyFinalization() public onlyOwner {
        canFinalizeEarly = true;
    }

    /**
    * @dev disables staking period
    */
    function disableStakingPeriod() public onlyOwner {
        isStakingPeriod = false;
    }

    /**
     * @dev Creates Golix token contract. This is called on the constructor function of the Crowdsale contract
     */
    function createTokenContract() internal returns (MintableToken) {
        return new GolixToken();
    }

    /**
     * @dev finalizes crowdsale
     */
    function finalization() internal {
        // This must have been set manually prior to finalize() call.
        require(teamVestTokenAllocation != address(0) && contributorsVestTokenAllocation != address(0));

        if (TOTAL_TOKENS_FOR_CROWDSALE > token.totalSupply()) {
            uint256 remainingTokens = TOTAL_TOKENS_FOR_CROWDSALE.sub(token.totalSupply());
            token.mint(contributorsVestTokenAllocation, remainingTokens);
            isStakingPeriod = true;
        }

        // final minting
        token.mint(marketingWallet, MARKETING_SHARE);
        token.mint(shareHoldersWallet, SHAREHOLDERS_SHARE);
        token.mint(teamVestTokenAllocation, FOUNDERS_SHARE);

        token.finishMinting();
        GolixToken(token).unpause();
        super.finalization();
    }
}
/**
 * @title Golix token distribution contract - crowdsale contract for the Golix tokens.
 * @author Gustavo Guimaraes - <gustavoguimaraes@gmail.com>
 */
function mintTokensForCrowdsaleParticipants(address[] investorsAddress, uint256[] amountOfTokens)
    external
    onlyOwner
{
    require(investorsAddress.length == amountOfTokens.length);

    for (uint256 i = 0; i < investorsAddress.length; i++) {
        require(token.totalSupply().add(amountOfTokens[i]) <= TOTAL_TOKENS_FOR_CROWDSALE);

        token.mint(investorsAddress[i], amountOfTokens[i]);
        icoContributions[investorsAddress[i]] = icoContributions[investorsAddress[i]].add(amountOfTokens[i]);

        emit MintedTokensFor(investorsAddress[i], amountOfTokens[i]);
    }
}

/**
 * @dev Mint tokens for crowdsale participants
 * @param investorsAddress List of Purchasers addresses
 * @param amountOfTokens List of token amounts for investor
 */