contract GOeurekaSale is Claimable, gotTokenSaleConfig, Pausable, Salvageable {
    using SafeMath for uint256;

    // The token being sold
    GOeureka public token;

    WhiteListedBasic public whiteListed;

    uint256 public presaleEnd;
    uint256 public saleEnd;

    // Minimum contribution is now calculated
    uint256 public minContribution;

    // address where funds are collected
    address public multiSig;

    // amount of raised funds in wei from private, presale and main sale
    uint256 public weiRaised;

    // amount of raised tokens
    uint256 public tokensRaised;

    // number of participants
    mapping(address => uint256) public contributions;
    uint256 public numberOfContributors = 0;

    //  for rate
    uint public basicRate;
 
    // EVENTS

    event TokenPurchase(address indexed beneficiary, uint256 value, uint256 amount);
    event SaleClosed();
    event HardcapReached();
    event NewCapActivated(uint256 newCap);

 
    // CONSTRUCTOR

    constructor(GOeureka token_, WhiteListedBasic _whiteListed) public {

        basicRate = 3000;  // TokensPerEther
        calculateRates();
        
        presaleEnd = 1536508800; //20180910 00:00 +8
        saleEnd = 1543593600; //20181201 00:00 +8

        multiSig = MULTISIG_ETH;

        // NOTE - toke
        token = token_;

        whiteListed = _whiteListed;
    }

    // This sale contract must be the minter before we mintAllocations or do anything else.
    //
    bool allocated = false;
    function mintAllocations() external onlyOwner {
        require(!allocated);
        allocated = true;
        token.mint(VESTING_WALLET,VESTING_AMOUNT);
        token.mint(RESERVE_WALLET,RESERVE_AMOUNT);
    }

    function setWallet(address _newWallet) public onlyOwner {
        multiSig = _newWallet;
    } 


    // @return true if crowdsale event has ended
    function hasEnded() public view returns (bool) {
        if (now > saleEnd)
            return true;
        if (tokensRaised >= SALE_CAP)
            return true; // if we reach the tokensForSale
        return false;
    }

    // Buyer must be whitelisted
    function isWhiteListed(address beneficiary) internal view returns (bool) {
        return whiteListed.isWhiteListed(beneficiary);
    }

    modifier onlyAuthorised(address beneficiary) {
        require(isWhiteListed(beneficiary),"Not authorised");
        
        require (!hasEnded(),"ended");
        require (multiSig != 0x0,"MultiSig empty");
        require ((msg.value > minContribution) || (tokensRaised.add(getTokens(msg.value)) == SALE_CAP),"Value too small");
        _;
    }

    function setNewRate(uint newRate) onlyOwner public {
        require(weiRaised == 0);
        require(1000 < newRate && newRate < 10000);
        basicRate = newRate;
        calculateRates();
    }

    function calculateRates() internal {
        minContribution = uint(100 * DECIMALSFACTOR).div(basicRate);
    }


    function getTokens(uint256 amountInWei) 
    internal
    view
    returns (uint256 tokens)
    {
        if (now <= presaleEnd) {
            uint theseTokens = amountInWei.mul(basicRate).mul(1125).div(1000);
            require((amountInWei >= 1 ether) || (tokensRaised.add(theseTokens)==SALE_CAP));
            return (theseTokens);
        }
        if (now <= saleEnd) { 
            return (amountInWei.mul(basicRate));
        }
        revert();
    }

  
    // low level token purchase function
    function buyTokens(address beneficiary, uint256 value)
        internal
        onlyAuthorised(beneficiary) 
        whenNotPaused
    {
        uint256 newTokens;
 
        newTokens = getTokens(value);
        weiRaised = weiRaised.add(value);
        // if we have bridged two tranches....
        if (contributions[beneficiary] == 0) {
            numberOfContributors++;
        }
        contributions[beneficiary] = contributions[beneficiary].add(value);
        tokensRaised = tokensRaised.add(newTokens);
        token.mint(beneficiary,newTokens);
        emit TokenPurchase(beneficiary, value, newTokens);
        multiSig.transfer(value);
    }

    function placeTokens(address beneficiary, uint256 tokens) 
        public       
        onlyOwner
    {
        require(!hasEnded());
        tokensRaised = tokensRaised.add(tokens);
        token.mint(beneficiary,tokens);
    }


    // Complete the sale
    function finishSale() public onlyOwner {
        require(hasEnded());
        token.finishMinting();
        emit SaleClosed();
    }

    // fallback function can be used to buy tokens
    function () public payable {
        buyTokens(msg.sender, msg.value);
    }

}
function isWhiteListed(address beneficiary) internal view returns (bool) {
    return whiteListed.isWhiteListed(beneficiary);
}

// Buyer must be whitelisted