contract StyrasCrowdsale is Ownable {

    using SafeMath for uint256;
  
    enum State { preSale, publicSale, hasFinalized }

    // how many token units a buyer gets per ether
    // minimum amount of funds (soft-cap) to be raised in weis
    // maximum amount of funds (hard-cap) to be raised in weis
    // minimum amount of weis to invest per investor
    uint256 public rate;
    uint256 public goal;
    uint256 public cap;
    uint256 public minInvest = 100000000000000000; // 0.1 ETH

    // presale treats
    uint256 public presaleDeadline = 1511827200; // GMT: Tuesday, November 28, 2017 00:00:00
    uint256 public presaleRate = 4000; // 1 ETH == 4000 STY 33% bonus
    uint256 public presaleCap = 50000000000000000000000000; // 50 millions STY
  
    // pubsale treats
    uint256 public pubsaleDeadline = 1514678400; // GMT: Sunday, December 31, 2017 0:00:00
    uint256 public pubsaleRate = 3000; // 1 ETH == 3000 STY
    uint256 public pubsaleCap = 180000000000000000000000000;

    // harrd cap = pubsaleCap + reservedSupply -> 200000000 DTY
    uint256 public reservedSupply = 20000000000000000000000000; // 10% max totalSupply

    uint256 public softCap = 840000000000000000000000; // 840 thousands STY

    // start and end timestamps where investments are allowed (both inclusive)
    // flag for investments finalization
    uint256 public startTime = 1511276400; // GMT: Tuesday, November 21, 2017 15:00:00
    uint256 public endTime;

    // amount of raised money in wei
    // address where funds are collected
    uint256 public weiRaised = 0;
    address public escrowWallet;
    address public partnersWallet;

    // contract of the token being sold
    // contract of the vault used to hold funds while crowdsale is running
    StyrasToken public token;
    StyrasVault public vault;

    State public state;

    /**
     * event for token purchase logging
     * @param purchaser who paid for the tokens
     * @param beneficiary who got the tokens
     * @param value weis paid for purchase
     * @param amount amount of tokens purchased
     */
    event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);
    event PresaleFinalized();
    event Finalized();

    function StyrasCrowdsale(address escrow, address partners) public {
        require(now < startTime);
        require(partners != address(0));
        require(startTime < presaleDeadline);
        require(presaleDeadline < pubsaleDeadline);
        require(pubsaleRate < presaleRate);
        require(presaleCap < pubsaleCap);
        require(softCap <= pubsaleCap);
        endTime = presaleDeadline;
        escrowWallet = escrow;
        partnersWallet = partners;
        token = new StyrasToken(partnersWallet, reservedSupply);
        vault = new StyrasVault(escrowWallet);
        rate = presaleRate;
        goal = softCap.div(rate);
        cap = presaleCap.div(rate);
        state = State.preSale;
        assert(goal < cap);
        assert(startTime < endTime);
    }

    // fallback function can be used to buy tokens
    function () public payable {
        buyTokens(msg.sender);
    }
  
    // low level token purchase function
    function buyTokens(address beneficiary) public payable {
        require(beneficiary != address(0));
        require(state < State.hasFinalized);
        require(validPurchase());
        uint256 weiAmount = msg.value;
        // calculate token amount to be created
        uint256 tokenAmount = weiAmount.mul(rate);
        // update state
        weiRaised = weiRaised.add(weiAmount);
        token.mint(beneficiary, tokenAmount);
        TokenPurchase(msg.sender, beneficiary, weiAmount, tokenAmount);
        forwardFunds();
    }

    // send ether to the fund collection wallet
    // override to create custom fund forwarding mechanisms
    function forwardFunds() internal {
        vault.deposit.value(msg.value)(msg.sender);
        assert(vault.balance == weiRaised);
    }

    // @return true if the transaction can buy tokens
    function validPurchase() internal constant returns (bool) {
        bool withinPeriod = startTime <= now && now <= endTime;
        bool nonZeroPurchase = msg.value > 0;
        bool withinCap = weiRaised < cap;
        bool overMinInvest = msg.value >= minInvest || vault.balanceOf(msg.sender) >= minInvest;
        return withinPeriod && nonZeroPurchase && withinCap && overMinInvest;
    }

    function hardCap() public constant returns (uint256) {
        return pubsaleCap + reservedSupply;
    }

    function goalReached() public constant returns (bool) {
        return weiRaised >= goal;
    }

    // @return true if crowdsale event has ended
    function hasEnded() public constant returns (bool) {
        bool afterPeriod = now > endTime;
        bool capReached = weiRaised >= cap;
        return afterPeriod || capReached;
    }

    // if crowdsale is unsuccessful, investors can claim refunds here
    function claimRefund() public {
        require(state == State.hasFinalized);
        require(!goalReached());
        vault.refund(msg.sender);
    }

    function enableWithdraw() onlyOwner public {
        require(goalReached());
        vault.enableWithdraw();
    }
  
    // if crowdsale is successful, owner can withdraw ether here
    function withdraw(uint256 _weiAmountToWithdraw) onlyOwner public {
        require(goalReached());
        vault.withdraw(_weiAmountToWithdraw);
    }

    function finalizePresale() onlyOwner public {
        require(state == State.preSale);
        require(hasEnded());
        uint256 weiDiff = 0;
        uint256 raisedTokens = token.totalSupply();
        rate = pubsaleRate;
        if (!goalReached()) {
            weiDiff = (softCap.sub(raisedTokens)).div(rate);
            goal = weiRaised.add(weiDiff);
        }
        weiDiff = (pubsaleCap.sub(raisedTokens)).div(rate);
        cap = weiRaised.add(weiDiff);
        endTime = pubsaleDeadline;
        state = State.publicSale;
        assert(goal < cap);
        assert(startTime < endTime);
        PresaleFinalized();
    }

    /**
     * @dev Must be called after crowdsale ends, to do some extra finalization
     * work. Calls the contract's finalization function.
     */
    function finalize() onlyOwner public {
        require(state == State.publicSale);
        require(hasEnded());
        finalization();
        state = State.hasFinalized;
        Finalized();
    }

    // vault finalization task, called when owner calls finalize()
    function finalization() internal {
        if (goalReached()) {
            vault.close();
            token.mintPartners(reservedSupply);
        } else {
            vault.enableRefunds();
        }
        vault.transferOwnership(owner);
        token.transferOwnership(owner);
    }

}
/**
 * @title StyrasCrowdsale
 * @dev This is a capped and refundable crowdsale.
 */
function buyTokens(address beneficiary) public payable {
    require(beneficiary != address(0));
    require(state < State.hasFinalized);
    require(validPurchase());
    uint256 weiAmount = msg.value;
    // calculate token amount to be created
    uint256 tokenAmount = weiAmount.mul(rate);
    // update state
    weiRaised = weiRaised.add(weiAmount);
    token.mint(beneficiary, tokenAmount);
    TokenPurchase(msg.sender, beneficiary, weiAmount, tokenAmount);
    forwardFunds();
}

// low level token purchase function