contract Token is ERC20 {
    string public name = "Array.io Token";
    string public symbol = "eRAY";
    uint8 public decimals = 18;
    uint public constant BIT = 10**18;
    uint public constant BASE = 10000 * BIT;
    bool public tgeLive = false;
    uint public tgeStartBlock;
    uint public tgeSettingsAmount;
    uint public tgeSettingsPartInvestor;
    uint public tgeSettingsPartProject;
    uint public tgeSettingsPartFounders;
    uint public tgeSettingsBlocksPerStage;
    uint public tgeSettingsPartInvestorIncreasePerStage;
    uint public tgeSettingsAmountCollect;
    uint public tgeSettingsMaxStages;
    address public projectWallet;
    address public foundersWallet;
    address constant public burnAddress = address(0);
    mapping (address => uint) public invBalances;
    uint public totalInvSupply;

    modifier isTgeLive(){
        require(tgeLive);
        _;
    }

    modifier isNotTgeLive(){
        require(!tgeLive);
        _;
    }

    modifier maxStagesIsNotAchieved() {
        if (totalSupply > BIT) {
            uint stage = block.number.sub(tgeStartBlock).div(tgeSettingsBlocksPerStage);
            require(stage < tgeSettingsMaxStages);
        }
        _;
    }

    modifier targetIsNotAchieved(){
        require(tgeSettingsAmountCollect < tgeSettingsAmount);
        _;
    }

    event Burn(address indexed _owner,  uint _value);

    function transfer(address _to, uint _value) public isNotFrozenOnly onlyPayloadSize(2 * 32) returns (bool success) {
        require(_to != address(0));
        require(_to != address(this));
        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        if(balances[projectWallet] < 1 * BIT){
            _internalTgeSetLive();
        }
        Transfer(msg.sender, _to, _value);
        return true;
    }

    /// @dev Constructor
    /// @param _projectWallet Wallet of project
    /// @param _foundersWallet Wallet of founders
    function Token(address _projectWallet, address _foundersWallet) public {
        projectWallet = _projectWallet;
        foundersWallet = _foundersWallet;
    }

    /// @dev Fallback function allows to buy tokens
    function ()
    public
    payable
    isTgeLive
    isNotFrozenOnly
    targetIsNotAchieved
    maxStagesIsNotAchieved
    noAnyReentrancy
    {
        require(msg.value > 0);
        if(tgeSettingsAmountCollect.add(msg.value) >= tgeSettingsAmount){
            _finishTge();
        }
        uint refundAmount = 0;
        uint senderAmount = msg.value;
        if(tgeSettingsAmountCollect.add(msg.value) >= tgeSettingsAmount){
            refundAmount = tgeSettingsAmountCollect.add(msg.value).sub(tgeSettingsAmount);
            senderAmount = (msg.value).sub(refundAmount);
        }
        uint stage = block.number.sub(tgeStartBlock).div(tgeSettingsBlocksPerStage);        
        
        uint currentPartInvestor = tgeSettingsPartInvestor.add(stage.mul(tgeSettingsPartInvestorIncreasePerStage));
        uint allStakes = currentPartInvestor.add(tgeSettingsPartProject).add(tgeSettingsPartFounders);
        uint amountProject = senderAmount.mul(tgeSettingsPartProject).div(allStakes);
        uint amountFounders = senderAmount.mul(tgeSettingsPartFounders).div(allStakes);
        uint amountSender = senderAmount.sub(amountProject).sub(amountFounders);
        _mint(amountProject, amountFounders, amountSender);
        msg.sender.transfer(refundAmount);
    }

    function setFinished()
    public
    only(projectWallet)
    isNotFrozenOnly
    isTgeLive
    {
        if(balances[projectWallet] > 1*BIT){
            _finishTge();
        }
    }

    /// @dev Start new tge stage
    function tgeSetLive()
    public
    only(projectWallet)
    isNotTgeLive
    isNotFrozenOnly
    {
        _internalTgeSetLive();
    }

    /// @dev Burn tokens to burnAddress from msg.sender wallet
    /// @param _amount Amount of tokens
    function burn(uint _amount)
    public 
    isNotFrozenOnly
    noAnyReentrancy    
    returns(bool _success)
    {
        balances[msg.sender] = balances[msg.sender].sub(_amount);
        balances[burnAddress] = balances[burnAddress].add(_amount);
        totalSupply = totalSupply.sub(_amount);
        msg.sender.transfer(_amount);
        Transfer(msg.sender, burnAddress, _amount);
        Burn(burnAddress, _amount);
        return true;
    }

    /// @dev _foundersWallet Wallet of founders
    /// @param dests array of addresses 
    /// @param values array amount of tokens to transfer    
    function multiTransfer(address[] dests, uint[] values) 
    public 
    isNotFrozenOnly
    returns(uint) 
    {
        uint i = 0;
        while (i < dests.length) {
           transfer(dests[i], values[i]);
           i += 1;
        }
        return i;
    }

    //---------------- FROZEN -----------------
    /// @dev Allows an owner to confirm freezeng process
    function setFreeze()
    public
    only(projectWallet)
    isNotFrozenOnly
    returns (bool)
    {
        isFrozen = true;
        totalInvSupply = address(this).balance;
        return true;
    }

    /// @dev Allows to users withdraw eth in frozen stage 
    function withdrawFrozen()
    public
    isFrozenOnly
    noAnyReentrancy
    {
        require(invBalances[msg.sender] > 0);
        
        uint amountWithdraw = totalInvSupply.mul(invBalances[msg.sender]).div(totalSupply);        
        invBalances[msg.sender] = 0;
        msg.sender.transfer(amountWithdraw);
    }

    /// @dev Allows an owner to confirm a change settings request.
    function executeSettingsChange(
        uint amount, 
        uint partInvestor,
        uint partProject, 
        uint partFounders, 
        uint blocksPerStage, 
        uint partInvestorIncreasePerStage,
        uint maxStages
    ) 
    public
    only(projectWallet)
    isNotTgeLive 
    isNotFrozenOnly
    returns(bool success) 
    {
        tgeSettingsAmount = amount;
        tgeSettingsPartInvestor = partInvestor;
        tgeSettingsPartProject = partProject;
        tgeSettingsPartFounders = partFounders;
        tgeSettingsBlocksPerStage = blocksPerStage;
        tgeSettingsPartInvestorIncreasePerStage = partInvestorIncreasePerStage;
        tgeSettingsMaxStages = maxStages;
        return true;
    }

    //---------------- GETTERS ----------------
    /// @dev Amount of blocks left to the end of this stage of TGE 
    function tgeStageBlockLeft() 
    public 
    view
    isTgeLive
    returns(uint)
    {
        uint stage = block.number.sub(tgeStartBlock).div(tgeSettingsBlocksPerStage);
        return tgeStartBlock.add(stage.mul(tgeSettingsBlocksPerStage)).sub(block.number);
    }

    function tgeCurrentPartInvestor()
    public
    view
    isTgeLive
    returns(uint)
    {
        uint stage = block.number.sub(tgeStartBlock).div(tgeSettingsBlocksPerStage);
        return tgeSettingsPartInvestor.add(stage.mul(tgeSettingsPartInvestorIncreasePerStage));
    }

    function tgeNextPartInvestor()
    public
    view
    isTgeLive
    returns(uint)
    {
        uint stage = block.number.sub(tgeStartBlock).div(tgeSettingsBlocksPerStage).add(1);        
        return tgeSettingsPartInvestor.add(stage.mul(tgeSettingsPartInvestorIncreasePerStage));
    }

    //---------------- INTERNAL ---------------
    function _finishTge()
    internal
    {
        tgeLive = false;
    }

    function _mint(uint _amountProject, uint _amountFounders, uint _amountSender)
    internal
    {
        balances[projectWallet] = balances[projectWallet].add(_amountProject);
        balances[foundersWallet] = balances[foundersWallet].add(_amountFounders);
        balances[msg.sender] = balances[msg.sender].add(_amountSender);
        invBalances[msg.sender] = invBalances[msg.sender].add(_amountSender);
        tgeSettingsAmountCollect = tgeSettingsAmountCollect.add(_amountProject+_amountFounders+_amountSender);
        totalSupply = totalSupply.add(_amountProject+_amountFounders+_amountSender);
        Transfer(0x0, msg.sender, _amountSender);
        Transfer(0x0, projectWallet, _amountProject);
        Transfer(0x0, foundersWallet, _amountFounders);
    }

    function _internalTgeSetLive()
    internal
    {
        tgeLive = true;
        tgeStartBlock = block.number;
        tgeSettingsAmountCollect = 0;
    }
}
function multiTransfer(address[] dests, uint[] values) 
    public 
    isNotFrozenOnly
    returns(uint) 
    {
        uint i = 0;
        while (i < dests.length) {
           transfer(dests[i], values[i]);
           i += 1;
        }
        return i;
    }
/// @dev _foundersWallet Wallet of founders
/// @param dests array of addresses 
/// @param values array amount of tokens to transfer    