contract TheMoveToken is StandardToken, Ownable {
    string public constant name = "MOVE Token";
    string public constant symbol = "MOVE";
    uint public constant decimals = 18;
    using SafeMath for uint256;
    // address where funds are collected
    address public wallet;
    // how many token units a buyer gets per wei
    uint256 public rate;
    uint256 public minTransactionAmount;
    uint256 public raisedForEther = 0;
    uint256 private preicoSupply = 3072000000000000000000000;
    uint256 private icoSupply = 10000000000000000000000000;
    uint256 private bonusesSupply = 3000000000000000000000000;

    uint256 public bonusesSold = 0;
    uint256 public tokensSold = 0;

    // PRE-ICO stages
    uint256 public stage1 = 240000000000000000000000;
    uint256 public stage2 = 360000000000000000000000;
    uint256 public stage3 = 960000000000000000000000;
    uint256 public stage4 = 1512000000000000000000000;

    modifier inActivePeriod() {
	   require((preicoStartDate < now && now <= preicoEndDate) || (icoStartDate < now && now <= icoEndDate));
        _;
    }

    function TheMoveToken(uint _preicostart, uint _preicoend,uint _icostart, uint _icoend, address _wallet) public {
        require(_wallet != 0x0);
        require(_preicostart < _preicoend);
        require(_preicoend < _icostart);
        require(_icostart < _icoend);

        totalSupply = 21172000000000000000000000;
        rate = 600;

        // minimal invest
        minTransactionAmount = 0.1 ether;
        icoStartDate = _icostart;
        icoEndDate = _icoend;
        preicoStartDate = _preicostart;
        preicoEndDate = _preicoend;
        wallet = _wallet;

	   // Store the ico funds in the contract and send the rest to the developer wallet
       uint256 amountInContract = preicoSupply + icoSupply + bonusesSupply;

	   balances[this] = balances[this].add(amountInContract);
       balances[_wallet] = balances[_wallet].add(totalSupply - amountInContract);
    }

    function setupPREICOPeriod(uint _start, uint _end) public onlyOwner {
        require(_start < _end);
        preicoStartDate = _start;
        preicoEndDate = _end;
    }

    function setupICOPeriod(uint _start, uint _end) public onlyOwner {
        require(_start < _end);
        icoStartDate = _start;
        icoEndDate = _end;
    }

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

    function burnPREICOTokens() public onlyOwner {
        int256 amountToBurn = int256(preicoSupply) - int256(tokensSold);
        if (amountToBurn > 0) {
            balances[this] = balances[this].sub(uint256(amountToBurn));
        }
    }

    // Use with extreme caution this will burn the rest of the tokens in the contract
    function burnICOTokens() public onlyOwner {
        balances[this] = 0;
    }

    function burnBonuses() public onlyOwner {
        int256 amountToBurn = int256(bonusesSupply) - int256(bonusesSold);
        if (amountToBurn > 0) {
            balances[this] = balances[this].sub(uint256(amountToBurn));
        }
    }

    // low level token purchase function
    function buyTokens(address _sender) public inActivePeriod payable {
        require(_sender != 0x0);
        require(msg.value >= minTransactionAmount);

        uint256 weiAmount = msg.value;

        raisedForEther = raisedForEther.add(weiAmount);

        // calculate token amount to be issued
        uint256 tokens = weiAmount.mul(rate);
        tokens += getBonus(tokens);

        if (isPREICO()) {
            require(tokensSold + tokens < preicoSupply);
        } else if (isICO()) {
            require(tokensSold + tokens <= (icoSupply + bonusesSupply));
        }

        issueTokens(_sender, tokens);
        tokensSold += tokens;
    }
    
    // High level token issue function
    // This will be used by the script which distributes tokens
    // to those who contributed in BTC or LTC.
    function sendTokens(address _sender, uint256 amount) public inActivePeriod onlyOwner {
        // calculate token amount to be issued
        uint256 tokens = amount.mul(rate);
        tokens += getBonus(tokens);

        if (isPREICO()) {
            require(tokensSold + tokens < preicoSupply);
        } else if (isICO()) {
            require(tokensSold + tokens <= (icoSupply + bonusesSupply));
        }

        issueTokens(_sender, tokens);
        tokensSold += tokens;
    }

    function withdrawEther(uint256 amount) external onlyOwner {
        owner.transfer(amount);
    }

    function isPREICO() public view returns (bool) {
        return (preicoStartDate < now && now <= preicoEndDate);
    }

    function isICO() public view returns (bool) {
        return (icoStartDate < now && now <= icoEndDate);
    }

    function getBonus(uint256 _tokens) public returns (uint256) {
        require(_tokens != 0);
        uint256 bonuses = 0;
        uint256 multiplier = 0;

        // First case if PRE-ICO is happening
        if (isPREICO()) {
            // Bonus depends on the amount of tokens sold.
            if (tokensSold < stage1) {
                // 100% bonus for stage1
                multiplier = 100;
            } else if (stage1 < tokensSold && tokensSold < (stage1 + stage2)) {
                // 80% bonus for stage2
                multiplier = 80;
            } else if ((stage1 + stage2) < tokensSold && tokensSold < (stage1 + stage2 + stage3)) {
                // 60% bonus for stage2
                multiplier = 60;
            } else if ((stage1 + stage2 + stage3) < tokensSold && tokensSold < (stage1 + stage2 + stage3 + stage4)) {
                // 40% bonus for stage2
                multiplier = 40;
            }
            bonuses = _tokens.mul(multiplier).div(100);

            return bonuses;
        }

        
        // Second case if ICO is happening
        else if (isICO()) {
            // Bonus depends on the week of the ICO and the bonus supply
            if (icoStartDate < now && now <= icoStartDate + 7 days) {
                // 20% bonus week 1
                multiplier = 20;
            } else if (icoStartDate + 7 days < now && now <= icoStartDate + 14 days ) {
                // 10% bonus week 2
                multiplier = 10;
            } else if (icoStartDate + 14 days < now && now <= icoStartDate + 21 days ) {
                // 5% bonus week 3
                multiplier = 5;
            }

            bonuses = _tokens.mul(multiplier).div(100);

            // Bonus supply limit reached.
            if (bonusesSold + bonuses > bonusesSupply) {
                bonuses = 0;
            } else {
                bonusesSold += bonuses;
            }
            return bonuses;
        } 
    }

    // This function transfers tokens to the contributor's account.
    function issueTokens(address _to, uint256 _value) internal returns (bool) {
        balances[_to] = balances[_to].add(_value);
        balances[this] = balances[this].sub(_value);
        Transfer(msg.sender, _to, _value);
        return true;
    }
}
function buyTokens(address _sender) public inActivePeriod payable {
        require(_sender != 0x0);
        require(msg.value >= minTransactionAmount);

        uint256 weiAmount = msg.value;

        raisedForEther = raisedForEther.add(weiAmount);

        // calculate token amount to be issued
        uint256 tokens = weiAmount.mul(rate);
        tokens += getBonus(tokens);

        if (isPREICO()) {
            require(tokensSold + tokens < preicoSupply);
        } else if (isICO()) {
            require(tokensSold + tokens <= (icoSupply + bonusesSupply));
        }

        issueTokens(_sender, tokens);
        tokensSold += tokens;
    }
// low level token purchase function