contract FluencePreSale is Haltable, SafeMath {

    mapping (address => uint256) public balanceOf;

    /*/
     *  Constants
    /*/

    string public constant name = "Fluence Presale Token";

    string public constant symbol = "FPT";

    uint   public constant decimals = 18;

    // 6% of tokens
    uint256 public constant SUPPLY_LIMIT = 6000000 ether;

    // What is given to contributors, <= SUPPLY_LIMIT
    uint256 public totalSupply;

    // If soft cap is not reached, refund process is started
    uint256 public softCap = 1000 ether;

    // Basic price
    uint256 public constant basicThreshold = 500 finney;

    uint public constant basicTokensPerEth = 1500;

    // Advanced price
    uint256 public constant advancedThreshold = 5 ether;

    uint public constant advancedTokensPerEth = 2250;

    // Expert price
    uint256 public constant expertThreshold = 100 ether;

    uint public constant expertTokensPerEth = 3000;

    // As we have different prices for different amounts,
    // we keep a mapping of contributions to make refund
    mapping (address => uint256) public etherContributions;

    // Max balance of the contract
    uint256 public etherCollected;

    // Address to withdraw ether to
    address public beneficiary;

    uint public startAtBlock;

    uint public endAtBlock;

    // All tokens are sold
    event GoalReached(uint amountRaised);

    // Minimal ether cap collected
    event SoftCapReached(uint softCap);

    // New contribution received and tokens are issued
    event NewContribution(address indexed holder, uint256 tokenAmount, uint256 etherAmount);

    // Ether is taken back
    event Refunded(address indexed holder, uint256 amount);

    // If soft cap is reached, withdraw should be available
    modifier softCapReached {
        if (etherCollected < softCap) {
            revert();
        }
        assert(etherCollected >= softCap);
        _;
    }

    // Allow contribution only during presale
    modifier duringPresale {
        if (block.number < startAtBlock || block.number > endAtBlock || totalSupply >= SUPPLY_LIMIT) {
            revert();
        }
        assert(block.number >= startAtBlock && block.number <= endAtBlock && totalSupply < SUPPLY_LIMIT);
        _;
    }

    // Allow withdraw only during refund
    modifier duringRefund {
        if(block.number <= endAtBlock || etherCollected >= softCap || this.balance == 0) {
            revert();
        }
        assert(block.number > endAtBlock && etherCollected < softCap && this.balance > 0);
        _;
    }

    function FluencePreSale(uint _startAtBlock, uint _endAtBlock, uint softCapInEther){
        require(_startAtBlock > 0 && _endAtBlock > 0);
        beneficiary = msg.sender;
        startAtBlock = _startAtBlock;
        endAtBlock = _endAtBlock;
        softCap = softCapInEther * 1 ether;
    }

    // Change beneficiary address
    function setBeneficiary(address to) onlyOwner external {
        require(to != address(0));
        beneficiary = to;
    }

    // Withdraw contract's balance to beneficiary account
    function withdraw() onlyOwner softCapReached external {
        require(this.balance > 0);
        beneficiary.transfer(this.balance);
    }

    // Process contribution, issue tokens to user
    function contribute(address _address) private stopInEmergency duringPresale {
        if(msg.value < basicThreshold && owner != _address) {
            revert();
        }
        assert(msg.value >= basicThreshold || owner == _address);
        // Minimal contribution

        uint256 tokensToIssue;

        if (msg.value >= expertThreshold) {
            tokensToIssue = safeMult(msg.value, expertTokensPerEth);
        }
        else if (msg.value >= advancedThreshold) {
            tokensToIssue = safeMult(msg.value, advancedTokensPerEth);
        }
        else {
            tokensToIssue = safeMult(msg.value, basicTokensPerEth);
        }

        assert(tokensToIssue > 0);

        totalSupply = safeAdd(totalSupply, tokensToIssue);

        // Goal is already reached, can't issue any more tokens
        if(totalSupply > SUPPLY_LIMIT) {
            revert();
        }
        assert(totalSupply <= SUPPLY_LIMIT);

        // Saving ether contributions for the case of refund
        etherContributions[_address] = safeAdd(etherContributions[_address], msg.value);

        // Track ether before adding current contribution to notice the event of reaching soft cap
        uint collectedBefore = etherCollected;
        etherCollected = safeAdd(etherCollected, msg.value);

        // Tokens are issued
        balanceOf[_address] = safeAdd(balanceOf[_address], tokensToIssue);

        NewContribution(_address, tokensToIssue, msg.value);

        if (totalSupply == SUPPLY_LIMIT) {
            GoalReached(etherCollected);
        }
        if (etherCollected >= softCap && collectedBefore < softCap) {
            SoftCapReached(etherCollected);
        }
    }

    function() external payable {
        contribute(msg.sender);
    }

    function refund() stopInEmergency duringRefund external {
        uint tokensToBurn = balanceOf[msg.sender];


        // Sender must have tokens
        require(tokensToBurn > 0);

        // Burn
        balanceOf[msg.sender] = 0;

        // User contribution amount
        uint amount = etherContributions[msg.sender];

        // Amount must be positive -- refund is not processed yet
        assert(amount > 0);

        etherContributions[msg.sender] = 0;
        // Clear state

        // Reduce counters
        etherCollected = safeSubtract(etherCollected, amount);
        totalSupply = safeSubtract(totalSupply, tokensToBurn);

        // Process refund. In case of error, it will be thrown
        msg.sender.transfer(amount);

        Refunded(msg.sender, amount);
    }


}
function withdraw() onlyOwner softCapReached external {
    require(this.balance > 0);
    beneficiary.transfer(this.balance);
}

// Withdraw contract's balance to beneficiary account