contract Synapse is StandardToken, Owned, Pausable {
    
    using SafeMath for uint256;
    string public symbol;
    string public name;
    uint8 public decimals;

    uint256 public tokensForSale = 495000000 * 1 ether;//50% total of Supply for crowdsale   
    uint256 public vestingTokens = 227700000 * 1 ether;//23% of total Supply will be freeze(10% team, 8% reserve and 5% others) 
    uint256 public managementTokens = 267300000 * 1 ether;//27% total Supply(12% Marketing, 9% Expansion, 3% Bounty, 3% Advisor)

    mapping(address => bool) public investorIsVested; 
    uint256 public vestingTime = 15552000;// 6 months  

    uint256 public bountyTokens = 29700000 * 1 ether;
    uint256 public marketingTokens = 118800000 * 1 ether;
    uint256 public expansionTokens = 89100000 * 1 ether;
    uint256 public advisorTokens = 29700000 * 1 ether;    

    uint256 public icoStartTime;
    uint256 public icoFinalizedTime;

    address public tokenOwner;
    address public crowdSaleOwner;
    address public vestingOwner;

    address public saleContract;
    address public vestingContract;
    bool public fundraising = true;

    mapping (address => bool) public frozenAccounts;
    event FrozenFund(address target, bool frozen);


    modifier onlyPayloadSize(uint numWords) {
        assert(msg.data.length >= numWords * 32 + 4);
        _;
    }

    modifier manageTransfer() {
        if (msg.sender == owner) {
            _;
        } else {
            require(fundraising == false);
            _;
        }
    }
    
    /**
    * @dev constructor of a token contract
    * @param _tokenOwner address of the owner of contract.
    */
    constructor(address _tokenOwner,address _crowdSaleOwner, address _vestingOwner ) public Owned(_tokenOwner) {

        symbol ="SYP";
        name = "Synapsecoin";
        decimals = 18;
        tokenOwner = _tokenOwner; 
        crowdSaleOwner = _crowdSaleOwner;
        vestingOwner = _vestingOwner;
        totalSupply = 990000000 * 1 ether;
        balances[_tokenOwner] = balances[_tokenOwner].add(managementTokens);
        balances[_crowdSaleOwner] = balances[_crowdSaleOwner].add(tokensForSale);        
        balances[_vestingOwner] = balances[_vestingOwner].add(vestingTokens);
        emit Transfer(address(0), _tokenOwner, managementTokens);
        emit Transfer(address(0), _crowdSaleOwner, tokensForSale);    
        emit Transfer(address(0), _vestingOwner, vestingTokens);        
    }

    /**
    * @dev  Investor can Transfer token from this method
    * @param _to address of the reciever
    * @param _value amount of tokens to transfer
    */
    function transfer(address _to, uint256 _value) public manageTransfer whenNotPaused onlyPayloadSize(2) returns (bool success) {
        
        require(_value>0);
        require(_to != address(0));
        require(!frozenAccounts[msg.sender]);
        if(investorIsVested[msg.sender]==true )
        {
            require(now >= icoFinalizedTime.add(vestingTime)); 
            super.transfer(_to,_value);
            return true;

        }
        else {
            super.transfer(_to,_value);
            return true;
        }

    }
    
    /**
    * @dev  Transfer from allow to trasfer token 
    * @param _from address of sender 
    * @param _to address of the reciever
    * @param _value amount of tokens to transfer
    */
    function transferFrom(address _from, address _to, uint256 _value) public manageTransfer whenNotPaused onlyPayloadSize(3) returns (bool) {
        require(_value>0);
        require(_to != address(0));
        require(_from != address(0));
        require(!frozenAccounts[_from]);
        if(investorIsVested[_from]==true )
        {
            require(now >= icoFinalizedTime.add(vestingTime));//15552000
            super.transferFrom(_from,_to,_value);
            return true;

        }
        else {
            
           super.transferFrom(_from,_to,_value);
           return true;
        }    }
    

    /**
    * activates the sale contract (i.e. transfers saleable contracts)
    * @param _saleContract ,address of crowdsale contract
    */
    function activateSaleContract(address _saleContract) public whenNotPaused {
        require(msg.sender == crowdSaleOwner);
        require(_saleContract != address(0));
        require(saleContract == address(0));        
        saleContract = _saleContract;
        icoStartTime = now;

    }
     
    /**
    * activates the sale contract (i.e. transfers saleable contracts)
    * @param _vestingContract ,address of crowdsale contract
    */
    function activateVestingContract(address _vestingContract) public whenNotPaused  {
        require(msg.sender == vestingOwner);        
        require(_vestingContract != address(0));
        require(vestingContract == address(0));
        vestingContract = _vestingContract;
        
    }
    
    /**
    * @dev this function will send the bounty tokens to given address
    * @param _to ,address of the bounty receiver.
    * @param _value , number of tokens to be sent.
    */
    function sendBounty(address _to, uint256 _value) public whenNotPaused onlyOwner returns (bool) {

        require(_to != address(0));
        require(_value > 0 );        
        require(bountyTokens >= _value);
        bountyTokens = bountyTokens.sub(_value);
        return super.transfer(_to, _value);  
        }    

    /**
    * @dev this function will send the Marketing tokens to given address
    * @param _to ,address of the bounty receiver.
    * @param _value , number of tokens to be sent.
    */
    function sendMarketingTokens(address _to, uint256 _value) public whenNotPaused onlyOwner returns (bool) {

        require(_to != address(0));
        require(_value > 0 );        
        require(marketingTokens >= _value);
        marketingTokens = marketingTokens.sub(_value);
        return super.transfer(_to, _value);  
   }    

    /**
    * @dev this function will send the expansion tokens to given address
    * @param _to ,address of the bounty receiver.
    * @param _value , number of tokens to be sent.
    */
    function sendExpansionTokens(address _to, uint256 _value) public whenNotPaused onlyOwner returns (bool) {

        require(_to != address(0));
        require(_value > 0 );        
        require(expansionTokens >= _value);
        expansionTokens = expansionTokens.sub(_value);
        return super.transfer(_to, _value);  
   }    

    /**
    * @dev this function will send the expansion tokens to given address
    * @param _to ,address of the bounty receiver.
    * @param _value , number of tokens to be sent.
    */
    function sendAdvisorTokens(address _to, uint256 _value) public whenNotPaused onlyOwner returns (bool) {

        require(_to != address(0));
        require(_value > 0 );        
        require(advisorTokens >= _value);
        advisorTokens = advisorTokens.sub(_value);
        return super.transfer(_to, _value);  
   }    

    /**
    * @dev function to check whether passed address is a contract address
    */
    function isContract(address _address) private view returns (bool is_contract) {
        uint256 length;
        assembly {
        //retrieve the size of the code on target address, this needs assembly
            length := extcodesize(_address)
        }
        return (length > 0);
    }
    
    /**
    * @dev this function can only be called by crowdsale contract to transfer tokens to investor
    * @param _to address The address of the investor.
    * @param _value uint256 The amount of tokens to be send
    */
    function saleTransfer(address _to, uint256 _value) external whenNotPaused returns (bool) {
        require(saleContract != address(0),'sale address is not activated');
        require(msg.sender == saleContract,'caller is not crowdsale contract');
        require(!frozenAccounts[_to],'account is freezed');
        return super.transferFrom(crowdSaleOwner,_to, _value);
            
    }

    /**
    * @dev this function can only be called by  contract to transfer tokens to vesting beneficiary
    * @param _to address The address of the beneficiary.
    * @param _value uint256 The amount of tokens to be send
    */
    function vestingTransfer(address _to, uint256 _value) external whenNotPaused returns (bool) {
        require(icoFinalizedTime == 0,'ico is finalised');
        require(vestingContract != address(0));
        require(msg.sender == vestingContract,'caller is not a vesting contract');
        investorIsVested[_to] = true;
        return super.transferFrom(vestingOwner,_to, _value);
    }

    /**
    * @dev this function will closes the sale ,after this anyone can transfer their tokens to others.
    */
    function finalize() external whenNotPaused returns(bool){
        require(fundraising != false); 
        require(msg.sender == saleContract);
        fundraising = false;
        icoFinalizedTime = now;
        return true;
    }

   /**
   * @dev this function will freeze the any account so that the frozen account will not able to participate in crowdsale.
   * @param target ,address of the target account 
   * @param freeze ,boolean value to freeze or unfreeze the account ,true to freeze and false to unfreeze
   */
   function freezeAccount (address target, bool freeze) public onlyOwner {
        require(target != 0x0);
        frozenAccounts[target] = freeze;
        emit FrozenFund(target, freeze); // solhint-disable-line
    }

    /**
    * @dev Function to transfer any ERC20 token  to owner address which gets accidentally transferred to this contract
    * @param tokenAddress The address of the ERC20 contract
    * @param tokens The amount of tokens to transfer.
    * @return A boolean that indicates if the operation was successful.
    */
    function transferAnyERC20Token(address tokenAddress, uint tokens) public whenNotPaused onlyOwner returns (bool success) {
        require(tokenAddress != address(0));
        require(isContract(tokenAddress));
        return ERC20(tokenAddress).transfer(owner, tokens);
    }
    
    function () external payable {
        revert();
    }
    
}
/**
 * @title Synapse
 */
function activateVestingContract(address _vestingContract) public whenNotPaused  {
    require(msg.sender == vestingOwner);        
    require(_vestingContract != address(0));
    require(vestingContract == address(0));
    vestingContract = _vestingContract;
        
}

/**
 * activates the sale contract (i.e. transfers saleable contracts)
 * @param _vestingContract ,address of crowdsale contract
 */