contract NOLLYCOINCrowdFund {

    using SafeMath for uint256;

    NOLLYCOIN public token;                                    // Token contract reference

    //variables
    uint256 public preSaleStartTime = 1514874072; //1519898430;             // 01-MARCH-18 00:10:00 UTC //CHANGE THIS    
    uint256 public preSaleEndTime = 1522490430;               // 31-MARCH-18 00:10:00 UTC           //CHANGE THIS
    uint256 public crowdfundStartDate = 1522576830;           // 1-APRIL-18 00:10:00 UTC      //CHANGE THIS
    uint256 public crowdfundEndDate = 1525155672;             // 31-MARCH-17 00:10:00 UTC      //CHANGE THIS
    uint256 public totalWeiRaised;                            // Counter to track the amount raised //CHANGE THIS
    uint256 public exchangeRateForETH = 32000;                  // No. of NOLLY Tokens in 1 ETH  // CHANGE THIS 
    uint256 public exchangeRateForBTC = 60000;                 // No. of NOLLY Tokens in 1 BTC  //CHANGE THIS
    uint256 internal tokenSoldInPresale = 0;
    uint256 internal tokenSoldInCrowdsale = 0;
    uint256 internal minAmount = 1 * 10 ** 17;                // Equivalent to 0.1 ETH

    bool internal isTokenDeployed = false;                    // Flag to track the token deployment -- only can be set once


    // addresses
    // Founders multisig address
    address public founderMultiSigAddress = 0x59b645EB51B1e47e45F14A56F271030182393Efd;   //CHANGE THIS                          
    // Owner of the contract
    address public owner;

    enum State { PreSale, Crowdfund, Finish }

    //events
    event TokenPurchase(address indexed beneficiary, uint256 value, uint256 amount);
    event CrowdFundClosed(uint256 _blockTimeStamp);
    event ChangeFoundersWalletAddress(uint256 _blockTimeStamp, address indexed _foundersWalletAddress);

    //Modifiers
    modifier tokenIsDeployed() {
        require(isTokenDeployed == true);
        _;
    }
    modifier nonZeroEth() {
        require(msg.value > 0);
        _;
    }

    modifier nonZeroAddress(address _to) {
        require(_to != 0x0);
        _;
    }

    modifier onlyFounders() {
        require(msg.sender == founderMultiSigAddress);
        _;
    }

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    modifier onlyPublic() {
        require(msg.sender != founderMultiSigAddress);
        _;
    }

    modifier inState(State state) {
        require(getState() == state);
        _;
    }

    // Constructor to initialize the local variables 
    function NOLLYCOINCrowdFund() {
        owner = msg.sender;
    }

    // Function to change the founders multisig address 
    function setFounderMultiSigAddress(address _newFounderAddress) onlyFounders  nonZeroAddress(_newFounderAddress) {
        founderMultiSigAddress = _newFounderAddress;
        ChangeFoundersWalletAddress(now, founderMultiSigAddress);
    }

    // Attach the token contract, can only be done once     
    function setTokenAddress(address _tokenAddress) external onlyOwner nonZeroAddress(_tokenAddress) {
        require(isTokenDeployed == false);
        token = NOLLYCOIN(_tokenAddress);
        isTokenDeployed = true;
    }

    // function call after crowdFundEndTime.
    // It transfers the remaining tokens to remainingTokenHolder address
    function endCrowdfund() onlyFounders inState(State.Finish) returns(bool) {
        require(now > crowdfundEndDate);
        uint256 remainingToken = token.balanceOf(this);  // remaining tokens

        if (remainingToken != 0)
            token.transfer(founderMultiSigAddress, remainingToken);
        CrowdFundClosed(now);
        return true;
    }

    // Buy token function call only in duration of crowdfund active 
    function buyTokens(address beneficiary) 
    nonZeroEth 
    tokenIsDeployed 
    onlyPublic 
    nonZeroAddress(beneficiary) 
    payable 
    returns(bool) 
    {
        require(msg.value >= minAmount);

        if (getState() == State.PreSale) {
            if (buyPreSaleTokens(beneficiary)) {
                return true;
            }
            return false;
        } else {
            require(now >= crowdfundStartDate && now <= crowdfundEndDate);
            fundTransfer(msg.value);

            uint256 amount = getNoOfTokens(exchangeRateForETH, msg.value);

            if (token.transfer(beneficiary, amount)) {
                tokenSoldInCrowdsale = tokenSoldInCrowdsale.add(amount);
                token.changeTotalSupply(amount);
                totalWeiRaised = totalWeiRaised.add(msg.value);
                TokenPurchase(beneficiary, msg.value, amount);
                return true;
            }
            return false;
        }

    }

    // function to buy the tokens at presale 
    function buyPreSaleTokens(address beneficiary) internal returns(bool) {

        uint256 amount = getTokensForPreSale(exchangeRateForETH, msg.value);
        fundTransfer(msg.value);

        if (token.transfer(beneficiary, amount)) {
            tokenSoldInPresale = tokenSoldInPresale.add(amount);
            token.changeTotalSupply(amount);
            totalWeiRaised = totalWeiRaised.add(msg.value);
            TokenPurchase(beneficiary, msg.value, amount);
            return true;
        }
        return false;
    }

    // function to calculate the total no of tokens with bonus multiplication
    function getNoOfTokens(uint256 _exchangeRate, uint256 _amount) internal constant returns(uint256) {
        uint256 noOfToken = _amount.mul(_exchangeRate);
        uint256 noOfTokenWithBonus = ((100 + getCurrentBonusRate()) * noOfToken).div(100);
        return noOfTokenWithBonus;
    }

    function getTokensForPreSale(uint256 _exchangeRate, uint256 _amount) internal constant returns(uint256) {
        uint256 noOfToken = _amount.mul(_exchangeRate);
        uint256 noOfTokenWithBonus = ((100 + getCurrentBonusRate()) * noOfToken).div(100);
        if (noOfTokenWithBonus + tokenSoldInPresale > (50000000 * 10 ** 18)) { //change this to reflect current max
            revert();
        }
        return noOfTokenWithBonus;
    }

    // function to transfer the funds to founders account
    function fundTransfer(uint256 weiAmount) internal {
        founderMultiSigAddress.transfer(weiAmount);
    }


    // Get functions 

    // function to get the current state of the crowdsale
    function getState() public constant returns(State) {
       if (now >= preSaleStartTime && now <= preSaleEndTime) {
            return State.PreSale;
        }
        if (now >= crowdfundStartDate && now <= crowdfundEndDate) {
            return State.Crowdfund;
        } 
        return State.Finish;
    }


    // function provide the current bonus rate
    function getCurrentBonusRate() internal returns(uint8) {

        if (getState() == State.PreSale) {
            return 30; //presale bonus rate is 33%
        }
        if (getState() == State.Crowdfund) {
            

        //  week 1: 8th of April 1523197901
            if (now > crowdfundStartDate && now <= 1523197901) { 
                return 25;
            }

        //  week 2: 15th of April 1523802701
            if (now > 1523197901 && now <= 1523802701) { 
                return 20;
            }


        // week 3: 
            if (now > 1523802701 && now <= 1524565102 ) {
                return 15;

            } else {

                return 10;

            }
        }
    }


    // provides the bonus % 
    function currentBonus() public constant returns(uint8) {
        return getCurrentBonusRate();
    }

    // GET functions
    function getContractTimestamp() public constant returns(
        uint256 _presaleStartDate,
        uint256 _presaleEndDate,
        uint256 _crowdsaleStartDate,
        uint256 _crowdsaleEndDate)
    {
        return (preSaleStartTime, preSaleEndTime, crowdfundStartDate, crowdfundEndDate);
    }

    function getExchangeRate() public constant returns(uint256 _exchangeRateForETH, uint256 _exchangeRateForBTC) {
        return (exchangeRateForETH, exchangeRateForBTC);
    }

    function getNoOfSoldToken() public constant returns(uint256 _tokenSoldInPresale, uint256 _tokenSoldInCrowdsale) {
        return (tokenSoldInPresale, tokenSoldInCrowdsale);
    }

    function getWeiRaised() public constant returns(uint256 _totalWeiRaised) {
        return totalWeiRaised;
    }

    // Crowdfund entry
    // send ether to the contract address
    // With at least 200 000 gas
    function() public payable {
        buyTokens(msg.sender);
    }
}
function getState() public constant returns(State) {
       if (now >= preSaleStartTime && now <= preSaleEndTime) {
            return State.PreSale;
        }
        if (now >= crowdfundStartDate && now <= crowdfundEndDate) {
            return State.Crowdfund;
        } 
        return State.Finish;
    }
// Get functions 
// function to get the current state of the crowdsale