contract CrowdSale is Moderated {
	using SafeMath for uint256;

        address public recipient1 = 0x375D7f6bf5109E8e7d27d880EC4E7F362f77D275; // @TODO: replace this!
        address public recipient2 = 0x2D438367B806537a76B97F50B94086898aE5C518; // @TODO: replace this!
        address public recipient3 = 0xd198258038b2f96F8d81Bb04e1ccbfC2B3c46760; // @TODO: replace this!
        uint public percentageRecipient1 = 35;
        uint public percentageRecipient2 = 35;
        uint public percentageRecipient3 = 30;

	// Touch ERC20 smart contract
	Touch public tokenContract;

    uint256 public startDate;

    uint256 public endDate;

    // crowdsale aims to sell 100 Million TST
    uint256 public constant crowdsaleTarget = 22289 ether;
    // running total of tokens sold
    uint256 public etherRaised;

    // address to receive accumulated ether given a successful crowdsale
	address public etherVault;

    // minimum of 0.005 ether to participate in crowdsale
	uint256 constant purchaseThreshold = 5 finney;

    // boolean to indicate crowdsale finalized state
	bool public isFinalized = false;

	bool public active = false;

	// finalization event
	event Finalized();

	// purchase event
	event Purchased(address indexed purchaser, uint256 indexed tokens);

    // checks that crowd sale is live
    modifier onlyWhileActive {
        require(now >= startDate && now <= endDate && active);
        _;
    }

    function CrowdSale( address _tokenAddr,
                        uint256 start,
                        uint256 end) public {
        require(_tokenAddr != address(0x0));
        require(now < start && start < end);
        // the Touch token contract
        tokenContract = Touch(_tokenAddr);

        etherVault = msg.sender;

        startDate = start;
        endDate = end;
    }

	// fallback function invokes buyTokens method
	function () external payable {
	    buyTokens(msg.sender);
	}

	function buyTokens(address _purchaser) public payable ifUnrestricted onlyWhileActive returns (bool) {
	    require(!targetReached());
	    require(msg.value > purchaseThreshold);
	   // etherVault.transfer(msg.value);
	   splitPayment();

	    uint256 _tokens = calculate(msg.value);
        // approve CrowdSale to spend 100 000 000 tokens on behalf of moderator
        require(tokenContract.transferFrom(moderator,_purchaser,_tokens));
		//require(tokenContract.generateTokens(_purchaser, _tokens));
        Purchased(_purchaser, _tokens);
        return true;
	}

	function calculate(uint256 weiAmount) internal returns(uint256) {
	    uint256 excess;
	    uint256 numTokens;
	    uint256 excessTokens;
        if(etherRaised < 5572 ether) {
            etherRaised = etherRaised.add(weiAmount);
            if(etherRaised > 5572 ether) {
                excess = etherRaised.sub(5572 ether);
                numTokens = weiAmount.sub(excess).mul(5608);
                etherRaised = etherRaised.sub(excess);
                excessTokens = calculate(excess);
                return numTokens + excessTokens;
            } else {
                return weiAmount.mul(5608);
            }
        } else if(etherRaised < 11144 ether) {
            etherRaised = etherRaised.add(weiAmount);
            if(etherRaised > 11144 ether) {
                excess = etherRaised.sub(11144 ether);
                numTokens = weiAmount.sub(excess).mul(4807);
                etherRaised = etherRaised.sub(excess);
                excessTokens = calculate(excess);
                return numTokens + excessTokens;
            } else {
                return weiAmount.mul(4807);
            }
        } else if(etherRaised < 16716 ether) {
            etherRaised = etherRaised.add(weiAmount);
            if(etherRaised > 16716 ether) {
                excess = etherRaised.sub(16716 ether);
                numTokens = weiAmount.sub(excess).mul(4206);
                etherRaised = etherRaised.sub(excess);
                excessTokens = calculate(excess);
                return numTokens + excessTokens;
            } else {
                return weiAmount.mul(4206);
            }
        } else if(etherRaised < 22289 ether) {
            etherRaised = etherRaised.add(weiAmount);
            if(etherRaised > 22289 ether) {
                excess = etherRaised.sub(22289 ether);
                numTokens = weiAmount.sub(excess).mul(3738);
                etherRaised = etherRaised.sub(excess);
                excessTokens = calculate(excess);
                return numTokens + excessTokens;
            } else {
                return weiAmount.mul(3738);
            }
        } else {
            etherRaised = etherRaised.add(weiAmount);
            return weiAmount.mul(3738);
        }
	}


    function changeEtherVault(address newEtherVault) public onlyModerator {
        require(newEtherVault != address(0x0));
        etherVault = newEtherVault;

}


    function initialize() public onlyModerator {
        // assign Touch moderator to this contract address
        // assign moderator of this contract to crowdsale manager address
        require(tokenContract.allowance(moderator, address(this)) == 102306549000000000000000000);
        active = true;
        // send approve from moderator account allowing for 100 million tokens
        // spendable by this contract
    }

	// activates end of crowdsale state
    function finalize() public onlyModerator {
        // cannot have been invoked before
        require(!isFinalized);
        // can only be invoked after end date or if target has been reached
        require(hasEnded() || targetReached());

        active = false;

        // emit Finalized event
        Finalized();
        // set isFinalized boolean to true
        isFinalized = true;
    }

	// checks if end date of crowdsale is passed
    function hasEnded() internal view returns (bool) {
        return (now > endDate);
    }

    // checks if crowdsale target is reached
    function targetReached() internal view returns (bool) {
        return (etherRaised >= crowdsaleTarget);
    }
    function splitPayment() internal {
        recipient1.transfer(msg.value * percentageRecipient1 / 100);
        recipient2.transfer(msg.value * percentageRecipient2 / 100);
        recipient3.transfer(msg.value * percentageRecipient3 / 100);
    }

}
function targetReached() internal view returns (bool) {
    return (etherRaised >= crowdsaleTarget);
}

// checks if crowdsale target is reached