contract PHICrowdsale is Ownable, Crowdsale, MintableToken {
    using SafeMath for uint256;

    uint256 public ratePreIco  = 600;
    uint256 public rateIco  = 400;

    uint256 public weiMin = 0.03 ether;

    mapping (address => uint256) public deposited;

    uint256 public constant INITIAL_SUPPLY = 63 * 10**6 * (10 ** uint256(decimals));
    uint256 public    fundForSale = 60250 * 10**3 * (10 ** uint256(decimals));

    uint256 fundTeam =          150 * 10**3 * (10 ** uint256(decimals));
    uint256 fundAirdropPreIco = 250 * 10**3 * (10 ** uint256(decimals));
    uint256 fundAirdropIco =    150 * 10**3 * (10 ** uint256(decimals));
    uint256 fundBounty     =    100 * 10**3 * (10 ** uint256(decimals));
    uint256 fundAdvisor   =    210 * 10**3 * (10 ** uint256(decimals));
    uint256 fundReferal    =    1890 * 10**3 * (10 ** uint256(decimals));

    uint256 limitPreIco = 12 * 10**5 * (10 ** uint256(decimals));

    address addressFundTeam = 0x26cfc82A77ECc5a493D72757936A78A089FA592a;
    address addressFundAirdropPreIco = 0x87953BAE7A92218FAcE2DDdb30AB2193263394Ef;
    address addressFundAirdropIco = 0xaA8C9cA32cC8A6A7FF5eCB705787C22d9400F377;

    address addressFundBounty =  0x253fBeb28dA7E85c720F66bbdCFC4D9418196EE5;
    address addressFundAdvisor = 0x61eAEe13A2a3805b57B46571EE97B6faf95fC34d;
    address addressFundReferal = 0x4BfB1bA71952DAC3886DCfECDdE2a4Fea2A06bDb;

    uint256 public startTimePreIco = 1538406000; // Mon, 01 Oct 2018 15:00:00 GMT
    uint256 public endTimePreIco =   1539129600; // Wed, 10 Oct 2018 00:00:00 GMT
    uint256 public startTimeIco =    1541300400; // Sun, 04 Nov 2018 03:00:00 GMT
    uint256 public endTimeIco =      1542931200; // Fri, 23 Nov 2018 00:00:00 GMT

    uint256 percentReferal = 5;

    uint256 public countInvestor;

    event TokenPurchase(address indexed beneficiary, uint256 value, uint256 amount);
    event TokenLimitReached(address indexed sender, uint256 tokenRaised, uint256 purchasedToken);
    event MinWeiLimitReached(address indexed sender, uint256 weiAmount);
    event Burn(address indexed burner, uint256 value);
    event CurrentPeriod(uint period);
    event ChangeTime(address indexed owner, uint256 newValue, uint256 oldValue);
    event ChangeAddressFund(address indexed owner, address indexed newAddress, address indexed oldAddress);

    constructor(address _owner, address _wallet) public
    Crowdsale(_wallet)
    {
        require(_owner != address(0));
        owner = _owner;
        //owner = msg.sender; // $$$ for test's
        transfersEnabled = false;
        mintingFinished = false;
        totalSupply = INITIAL_SUPPLY;
        bool resultMintForOwner = mintForFund(owner);
        require(resultMintForOwner);
    }

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

    function buyTokens(address _investor) public payable returns (uint256){
        require(_investor != address(0));
        uint256 weiAmount = msg.value;
        uint256 tokens = validPurchaseTokens(weiAmount);
        if (tokens == 0) {revert();}
        weiRaised = weiRaised.add(weiAmount);
        tokenAllocated = tokenAllocated.add(tokens);
        mint(_investor, tokens, owner);
        makeReferalBonus(tokens);

        emit TokenPurchase(_investor, weiAmount, tokens);
        if (deposited[_investor] == 0) {
            countInvestor = countInvestor.add(1);
        }
        deposit(_investor);
        wallet.transfer(weiAmount);
        return tokens;
    }

    function getTotalAmountOfTokens(uint256 _weiAmount) internal returns (uint256) {
        uint256 currentDate = now;
        //currentDate = 1538438400; // (02 Oct 2018) // $$$ for test's
        //currentDate = 1540051200; // (20 Oct 2018) // $$$ for test's
        uint currentPeriod = 0;
        currentPeriod = getPeriod(currentDate);
        uint256 amountOfTokens = 0;
        if(currentPeriod > 0){
            if(currentPeriod == 1){
                amountOfTokens = _weiAmount.mul(ratePreIco);
                if (tokenAllocated.add(amountOfTokens) > limitPreIco) {
                    currentPeriod = currentPeriod.add(1);
                }
            }
            if(currentPeriod == 2){
                amountOfTokens = _weiAmount.mul(rateIco);
            }
        }
        emit CurrentPeriod(currentPeriod);
        return amountOfTokens;
    }

    function getPeriod(uint256 _currentDate) public view returns (uint) {
        if(_currentDate < startTimePreIco){
            return 0;
        }
        if( startTimePreIco <= _currentDate && _currentDate <= endTimePreIco){
            return 1;
        }
        if( endTimePreIco < _currentDate && _currentDate < startTimeIco){
            return 0;
        }
        if( startTimeIco <= _currentDate && _currentDate <= endTimeIco){
            return 2;
        }
        return 0;
    }

    function deposit(address investor) internal {
        deposited[investor] = deposited[investor].add(msg.value);
    }

    function makeReferalBonus(uint256 _amountToken) internal returns(uint256 _refererTokens) {
        _refererTokens = 0;
        if(msg.data.length == 20) {
            address referer = bytesToAddress(bytes(msg.data));
            require(referer != msg.sender);
            _refererTokens = _amountToken.mul(percentReferal).div(100);
            if(balanceOf(addressFundReferal) >= _refererTokens.mul(2)) {
                mint(referer, _refererTokens, addressFundReferal);
                mint(msg.sender, _refererTokens, addressFundReferal);
            }
        }
    }

    function bytesToAddress(bytes source) internal pure returns(address) {
        uint result;
        uint mul = 1;
        for(uint i = 20; i > 0; i--) {
            result += uint8(source[i-1])*mul;
            mul = mul*256;
        }
        return address(result);
    }

    function mintForFund(address _walletOwner) internal returns (bool result) {
        result = false;
        require(_walletOwner != address(0));
        balances[_walletOwner] = balances[_walletOwner].add(fundForSale);

        balances[addressFundTeam] = balances[addressFundTeam].add(fundTeam);
        balances[addressFundAirdropPreIco] = balances[addressFundAirdropPreIco].add(fundAirdropPreIco);
        balances[addressFundAirdropIco] = balances[addressFundAirdropIco].add(fundAirdropIco);
        balances[addressFundBounty] = balances[addressFundBounty].add(fundBounty);
        balances[addressFundAdvisor] = balances[addressFundAdvisor].add(fundAdvisor);
        balances[addressFundReferal] = balances[addressFundReferal].add(fundReferal);

        result = true;
    }

    function getDeposited(address _investor) public view returns (uint256){
        return deposited[_investor];
    }

    function validPurchaseTokens(uint256 _weiAmount) public returns (uint256) {
        uint256 addTokens = getTotalAmountOfTokens(_weiAmount);
        if (_weiAmount < weiMin) {
            emit MinWeiLimitReached(msg.sender, _weiAmount);
            return 0;
        }
        if (tokenAllocated.add(addTokens) > fundForSale) {
            emit TokenLimitReached(msg.sender, tokenAllocated, addTokens);
            return 0;
        }
        return addTokens;
    }

    /**
     * @dev owner burn Token.
     * @param _value amount of burnt tokens
     */
    function ownerBurnToken(uint _value) public onlyOwner {
        require(_value > 0);
        require(_value <= balances[owner]);
        require(_value <= totalSupply);
        require(_value <= fundForSale);

        balances[owner] = balances[owner].sub(_value);
        totalSupply = totalSupply.sub(_value);
        fundForSale = fundForSale.sub(_value);
        emit Burn(msg.sender, _value);
    }

    /**
     * @dev owner change time for startTimePreIco
     * @param _value new time value
     */
    function setStartTimePreIco(uint256 _value) public onlyOwner {
        require(_value > 0);
        uint256 _oldValue = startTimePreIco;
        startTimePreIco = _value;
        emit ChangeTime(msg.sender, _value, _oldValue);
    }


    /**
     * @dev owner change time for endTimePreIco
     * @param _value new time value
     */
    function setEndTimePreIco(uint256 _value) public onlyOwner {
        require(_value > 0);
        uint256 _oldValue = endTimePreIco;
        endTimePreIco = _value;
        emit ChangeTime(msg.sender, _value, _oldValue);
    }

    /**
     * @dev owner change time for startTimeIco
     * @param _value new time value
     */
    function setStartTimeIco(uint256 _value) public onlyOwner {
        require(_value > 0);
        uint256 _oldValue = startTimeIco;
        startTimeIco = _value;
        emit ChangeTime(msg.sender, _value, _oldValue);
    }

    /**
     * @dev owner change time for endTimeIco
     * @param _value new time value
     */
    function setEndTimeIco(uint256 _value) public onlyOwner {
        require(_value > 0);
        uint256 _oldValue = endTimeIco;
        endTimeIco = _value;
        emit ChangeTime(msg.sender, _value, _oldValue);
    }

    /**
     * @dev owner change address for FundReferal
     * @param _newAddress new value of address
     */
    function setAddressFundReferal(address _newAddress) public onlyOwner {
        require(_newAddress != address(0));
        address _oldAddress = addressFundReferal;
        addressFundReferal = _newAddress;
        emit ChangeAddressFund(msg.sender, _newAddress, _oldAddress);
    }

    function setWallet(address _newWallet) public onlyOwner {
        require(_newWallet != address(0));
        address _oldWallet = wallet;
        wallet = _newWallet;
        emit ChangeAddressFund(msg.sender, _newWallet, _oldWallet);
    }

    /**
    * @dev Adds single address to whitelist.
    * @param _payee Address to be added to the whitelist
    */
    function addToWhitelist(address _payee) public onlyOwner {
        whitelistPayee[_payee] = true;
    }

    /**
     * @dev Removes single address from whitelist.
     * @param _payee Address to be removed to the whitelist
     */
    function removeFromWhitelist(address _payee) public onlyOwner {
        whitelistPayee[_payee] = false;
    }

    function setTransferActive(bool _status) public onlyOwner {
        transfersEnabled = _status;
    }
}
function setAddressFundReferal(address _newAddress) public onlyOwner {
        require(_newAddress != address(0));
        address _oldAddress = addressFundReferal;
        addressFundReferal = _newAddress;
        emit ChangeAddressFund(msg.sender, _newAddress, _oldAddress);
    }
/**
 * @dev owner change address for FundReferal
 * @param _newAddress new value of address
 */