contract Crowdsale is Base, Owned {
    using SafeMath for uint256;

    enum State { INIT, ICO, CLOSED, PAUSE }

    uint public constant DECIMALS = 10**18;
    uint public constant WEI_DECIMALS = 10**18;
    uint public constant MAX_SUPPLY = 3400000000 * DECIMALS;

    mapping (address => bool) investors;
    State public currentState = State.INIT;
    IToken public token;

    uint public totalSupply = 0;

    uint public totalFunds = 0;
    uint public currentPrice = WEI_DECIMALS / 166;
    address public beneficiary;
    uint public countMembers = 0;

    event Transfer(address indexed _to, uint256 _value);

    modifier inState(State _state){
        require(currentState == _state);
        _;
    }

    modifier salesRunning(){
        require(currentState == State.ICO);
        _;
    }

    modifier notClosed(){
        require(currentState != State.CLOSED);
        _;
    }

    function Crowdsale(address _beneficiary) public {
        beneficiary = _beneficiary;
    }

    function ()
        public
        payable
        salesRunning
    {
        _receiveFunds();
    }

    function initialize(address _token)
        public
        onlyOwner
        inState(State.INIT)
    {
        require(_token != address(0));
        token = IToken(_token);
    }

    function setTokenPrice(uint _tokenPrice) public
        onlyOwner
        notClosed
    {
        currentPrice = _tokenPrice;
    }

    function setTokenPriceAsRatio(uint _tokenCount) public
        onlyOwner
        notClosed
    {
        currentPrice = WEI_DECIMALS / _tokenCount;
    }

    function setState(State _newState)
        public
        onlyOwner
    {
        require(currentState != State.CLOSED);
        require(
            (currentState == State.INIT && _newState == State.ICO ||
             currentState == State.ICO && _newState == State.CLOSED ||
            currentState == State.ICO && _newState == State.PAUSE ||
            currentState == State.PAUSE && _newState == State.ICO)
        );

        if(_newState == State.CLOSED){
            _finish();
        }
        currentState = _newState;
    }


    function withdraw(uint _amount)
        public
        noAnyReentrancy
        onlyOwner
    {
        require(_amount > 0 && _amount <= this.balance);
        beneficiary.transfer(_amount);
    }

    function sendToken(address _to, uint _amount)
        public
        onlyOwner
        salesRunning
    {
        uint amount = _amount.mul(DECIMALS);
        _checkMaxSaleSupply(amount);
        _mint(_to, amount);
    }


    function getCountMembers()
        public
        constant
        returns(uint)
    {
        return countMembers;
    }


    //==================== Internal Methods =================
    function _mint(address _to, uint _amount)
        noAnyReentrancy
        internal
    {
        _increaseSupply(_amount);
        IToken(token).mint(_to, _amount);
        Transfer(_to, _amount);
    }

    function _finish()
        noAnyReentrancy
        internal
    {
        IToken(token).start();
    }

    function _receiveFunds()
        internal
    {
        require(msg.value != 0);
        uint weiAmount = msg.value;
        uint transferTokens = weiAmount.mul(DECIMALS).div(currentPrice);

        _checkMaxSaleSupply(transferTokens);

        if(!investors[msg.sender]){
            countMembers = countMembers.add(1);
            investors[msg.sender] = true;
        }
        totalFunds = totalFunds.add(weiAmount);
        _mint(msg.sender, transferTokens);
    }

    function _checkMaxSaleSupply(uint transferTokens)
        internal
    {
        require(totalSupply.add(transferTokens) <= MAX_SUPPLY);
    }

    function _increaseSupply(uint _amount)
        internal
    {
        totalSupply = totalSupply.add(_amount);
    }

}
function _mint(address _to, uint _amount)
    noAnyReentrancy
    internal
{
    _increaseSupply(_amount);
    IToken(token).mint(_to, _amount);
    Transfer(_to, _amount);
}

//==================== Internal Methods =================