contract MMMTokenCoin is StandardToken, Ownable {
    using SafeMath for uint256;
    
    string public constant name = "Make More Money";
    string public constant symbol = "MMM";
    uint32 public constant decimals = 2;
    
	
	// Dates
	uint256 public stage2StartTime;					// timestamp when compound interest will begin
    uint256 globalInterestDate;             // last date when amount of tokens with interest was changed
    uint256 globalInterestAmount;           // amount of tokens with interest
	mapping(address => uint256) dateOfStart;     // timestamp of last operation, from which interest calc will be started
	uint256 public currentDate;						// current date timestamp
	uint256 public debugNow=0;



    // Crowdsale 
    uint256 public totalSupply=99900000000;			
 uint256 public  softcap;
    uint256 public  step0Rate=100000;       // rate of our tokens. 1 eth = 1000 MMM coins = 100000 tokens (seen as 1000,00 because of decimals)
    uint256 public  currentRate=100000;   
    uint256 public constant tokensForOwner=2000000000;   // tokens for owner won't dealt with compound interest
    uint256 public tokensFromEther=0;
    uint public saleStatus=0;      // 0 - sale is running, 1 - sale failed, 2 - sale successful
    address multisig=0x8216A5958f05ad61898e3A6F97ae5118C0e4b1A6;
    // counters of tokens for futher refund
    mapping(address => uint256) boughtWithEther;                // tokens, bought with ether. can be refunded to ether
    mapping(address => uint256) boughtWithOther;    			// tokens, bought with other payment systems. can be refunded to other payment systems, using site
    mapping(address => uint256) bountyAndRefsWithEther;  		// bounty tokens, given to some people. can be converted to ether, if ico is succeed
  
    

		
		
    // events
    event RefundEther(address indexed to, uint256 tokens, uint256 eth); 
    event DateUpdated(uint256 cdate);    
    event DebugLog(string what, uint256 param);
    event Sale(address indexed to, uint256 amount);
    event Step0Finished();
    event RateSet(uint256 newRate);	
    event Burn(address indexed who, uint256 amount);
   // DEBUG

    bool bDbgEnabled=false;
	
	
	
    function MMMTokenCoin() public   {  
        // Crowdsale     
        currentDate=(getNow()/1 days)*1 days;
        stage2StartTime=getNow()+61 days;
        
        balances[owner]=tokensForOwner;
        globalInterestAmount=0;
        
        if(bDbgEnabled) softcap=20000;
        else  softcap=50000000;
    }
	
	
	function debugSetNow(uint256 n) public
	{
	    require(bDbgEnabled);
		debugNow=n;
	}
	
	
	 /**
     * @dev Returns current timestamp. In case of debugging, this function can return timestamp representing any other time
     */
     
     
	function getNow() public view returns (uint256)
	{
	    
	    if(!bDbgEnabled) return now;
	    
	    if(debugNow==0) return now;
		else return debugNow;
//		return now;
	}
   
    /**
     * @dev Sets date from which interest will be calculated for specified address
     * @param _owner - address of balance owner
     */
   
    
    function updateDate(address _owner) private {
        if(currentDate<stage2StartTime) dateOfStart[_owner]=stage2StartTime;
        else dateOfStart[_owner]=currentDate;
    }
    

	
    /**
    * @dev Gets the balance of the specified address.
    * @param _owner The address to query the the balance of. 
    * @return An uint25664 representing the amount owned by the passed address.
    */
    function balanceOf(address _owner) public constant returns (uint256 balance) 
    { 
        
         return balanceWithInterest(_owner);
    }   
   
	
    /**
     * @dev Gets balance including interest for specified address
   	 * @param _owner The address to query the the balance of. 
     */
		
		
    function balanceWithInterest(address _owner)  private constant returns (uint256 ret)
    {
        if( _owner==owner || saleStatus!=2) return balances[_owner]; 
        return balances[_owner].compoundInterest(stage2StartTime, dateOfStart[_owner], currentDate);
    }
    
    
    
    
    


    /**
    * @dev transfer token for a specified address
    * @param _to The address to transfer to.
    * @param _value The amount to be transferred.
    */
		 
  function transfer(address _to, uint256 _value)  public returns (bool) {
    if(msg.sender==owner) {
    	// if owner sends tokens before sale finish, consider then as ether-refundable bonus
    	// else as simple transfer
        if(saleStatus==0) {
            	transferFromOwner(_to, _value,1);
            	tokensFromEther=tokensFromEther.add(_value);
				bountyAndRefsWithEther[_to]=bountyAndRefsWithEther[_to].add(_value);
        	}
        	else transferFromOwner(_to, _value,0);
        	
        	increaseGlobalInterestAmount(_value);
        	return true;   
    }
    
    balances[msg.sender] = balanceWithInterest(msg.sender).sub(_value);

    emit Transfer(msg.sender, _to, _value);
    if(_to==address(this)) {
		// make refund if tokens sent to contract
        uint256 left; left=processRefundEther(msg.sender, _value);
        balances[msg.sender]=balances[msg.sender].add(left);
    }
    else {
        balances[_to] = balanceWithInterest(_to).add(_value);
        updateDate(_to);
    }
    
    if(_to==owner) 
    {
    	// before sale finish, tokens can't be sent to owner
        require(saleStatus!=0);
        decreaseGlobalInterestAmount(_value);
    }
    
    updateDate(msg.sender);
    return true;
  }
  
  
  /**
    * @dev Transfer tokens from one address to another
    * @param _from address The address which you want to send tokens from
    * @param _to address The address which you want to transfer to
    * @param _value uint256 the amount of tokens to be transfered
    */
	  
  
   function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
           require(_to!=owner);
    uint256 _allowance = allowed[_from][msg.sender];

     allowed[_from][msg.sender] = _allowance.sub(_value);

    if(_from==owner) {
        if(saleStatus==0) {
            transferFromOwner(_to, _value,1);
            tokensFromEther=tokensFromEther.add(_value);
			bountyAndRefsWithEther[_to]=bountyAndRefsWithEther[_to].add(_value);			
        }
        else transferFromOwner(_to, _value,0);
      
        increaseGlobalInterestAmount(_value);
        return true;
    }
     
     
    balances[_from] = balanceWithInterest(_from).sub(_value);

     emit Transfer(_from, _to, _value);

    if(_to==address(this)) {
		// make refund if tokens sent to contract		   
        uint256 left; left=processRefundEther(_from, _value);
        balances[_from]=balances[_from].add(left);
    }
    else {
        balances[_to] = balanceWithInterest(_to).add(_value);
        updateDate(_to);
    }
    
    if(_to==owner) 
    {
        require(saleStatus!=0);
        decreaseGlobalInterestAmount(_value);
    }

    updateDate(_from);

    return true;
  }
  
  
  
    /**
    * @dev Burns tokens
    * @param _amount amount of tokens to burn
    */
	  
	  
	  
  function burn(uint256 _amount) public 
  {
	  	require(_amount>0);
        balances[msg.sender]=balanceOf(msg.sender).sub(_amount);
		decreaseGlobalInterestAmount(_amount);
        emit Burn(msg.sender, _amount);
  }
   
   //// SALE ////
   
    /**
     * @dev updates rate with whic tokens are being sold
     */

 	function setRate(uint256 r) public {
		require(saleStatus!=0);
		currentRate=r;
		emit RateSet(currentRate);
	}

    /**
     * @dev updates current date value. For compound interest calculation
     */
    
    function newDay() public   returns (bool b)
    {
        
       uint256 g; uint256 newDate;
       require(getNow()>=stage2StartTime);
       require(getNow()>=currentDate);
       newDate=(getNow()/1 days)*1 days;
        if(getNow()>=stage2StartTime && saleStatus==0)
        {
            if(tokensForOwner.sub(balances[owner])>=softcap) saleStatus=2;
            else saleStatus=1;
         
            emit Step0Finished();
        }
      
	   // check if overall compound interest of tokens will be less than total supply
	  
       g=globalInterestAmount.compoundInterest(stage2StartTime, globalInterestDate, newDate);
       if(g<=totalSupply && saleStatus==2) {
             currentDate=(getNow()/1 days)*1 days; 
             globalInterestAmount=g;
             globalInterestDate=currentDate;
             emit DateUpdated(currentDate);
             return true;
       }
       else if(saleStatus==1) currentDate=(getNow()/1 days)*1 days; 
       
       return false;
    }
    
    
    /**
     * @dev Sends collected ether to owner. If sale is not success, contract will hold ether for half year, and after, ether can be sent to owner
     * @return amount of owner's ether
     */
     
    function sendEtherToMultisig() public  returns(uint256 e) {
        uint256 req;
        require(msg.sender==owner || msg.sender==multisig);
        require(saleStatus!=0);

        if(saleStatus==2) {
        	// calculate ether for refunds
        	req=tokensFromEther.mul(1 ether).div(step0Rate).div(2);

        	if(bDbgEnabled) emit DebugLog("This balance is", this.balance);
        	if(req>=this.balance) return 0;
    	}
    	else if(saleStatus==1) {
    		require(getNow()-stage2StartTime>15768000);
    		req=0; 
    	}
        uint256 amount;
        amount=this.balance.sub(req);
        multisig.transfer(amount);
        return amount;
        
    }
    
	


	
	
	/**
		Refund functions. 
		If ico is success, anyone can get 0.000005 eth for 1 token,  else 00001 eth
		
	*/
	
    /**
     * @dev Refunds ether to sender if he trasnfered tokens to contract address. Calculates max possible amount of refund. If sent tokens>refund amound, tokens will be returned to sender.
     * @param _to Address of refund receiver
     * @param _value Tokens requested for refund
     */
	
    function processRefundEther(address _to, uint256 _value) private returns (uint256 left)
    {
        require(saleStatus!=0);
        require(_value>0);
        uint256 Ether=0; uint256 bounty=0;  uint256 total=0;

        uint256 rate2=saleStatus;

        
        if(_value>=boughtWithEther[_to]) {Ether=Ether.add(boughtWithEther[_to]); _value=_value.sub(boughtWithEther[_to]); }
        else {Ether=Ether.add(_value); _value=_value.sub(Ether);}
        boughtWithEther[_to]=boughtWithEther[_to].sub(Ether);
        
        if(rate2==2) {        
            if(_value>=bountyAndRefsWithEther[_to]) {bounty=bounty.add(bountyAndRefsWithEther[_to]); _value=_value.sub(bountyAndRefsWithEther[_to]); }
            else { bounty=bounty.add(_value); _value=_value.sub(bounty); }
            bountyAndRefsWithEther[_to]=bountyAndRefsWithEther[_to].sub(bounty);
        }
        total=Ether.add(bounty);
     //   if(_value>total) _value=_value.sub(total);
        tokensFromEther=tokensFromEther.sub(total);
       uint256 eth=total.mul(1 ether).div(step0Rate).div(rate2);
         _to.transfer(eth);
        if(bDbgEnabled) emit DebugLog("Will refund ", eth);

        emit RefundEther(_to, total, eth);
        decreaseGlobalInterestAmount(total);
        return _value;
    }
    
    
	

	     /**
     * @dev Returns info about refundable tokens- bought with ether, payment systems, and bonus tokens convertable to ether
     */
	
	function getRefundInfo(address _to) public returns (uint256, uint256, uint256)
	{
	    return  ( boughtWithEther[_to],  boughtWithOther[_to],  bountyAndRefsWithEther[_to]);
	    
	}
	
    
    /**
     * @dev Withdraw tokens  refunded to other payment systems.
     * @param _to Address of refund receiver
     */
    
    function refundToOtherProcess(address _to, uint256 _value) public onlyOwner returns (uint256 o) {
        require(saleStatus!=0);
        //uint256 maxValue=refundToOtherGet(_to);
        uint256 maxValue=0;
        require(_value<=maxValue);
        
        uint256 Other=0; uint256 bounty=0; 



        
        if(_value>=boughtWithOther[_to]) {Other=Other.add(boughtWithOther[_to]); _value=_value.sub(boughtWithOther[_to]); }
        else {Other=Other.add(_value); _value=_value.sub(Other);}
        boughtWithOther[_to]=boughtWithOther[_to].sub(Other);

       
        balances[_to]=balanceOf(_to).sub(Other).sub(bounty);
        updateDate(_to);
        decreaseGlobalInterestAmount(Other.add(bounty));
        return _value;
        
        
    }
    
 
    /**
     * @dev Converts ether to our tokens 
     */
		  
    
    function createTokensFromEther()  private   {
               
        assert(msg.value >= 1 ether / 1000);
       
         uint256 tokens = currentRate.mul(msg.value).div(1 ether);


        transferFromOwner(msg.sender, tokens,2);
      
       if(saleStatus==0) {
           boughtWithEther[msg.sender]=boughtWithEther[msg.sender].add(tokens);
            tokensFromEther=tokensFromEther.add(tokens);
       }
      
    }
	
	
    /**
     * @dev Converts other payments system payment to  tokens. Main logic is on site
     */
    
    function createTokensFromOther(address _to, uint256 howMuch, address referer) public  onlyOwner   { 
      
        require(_to!=address(this));
         transferFromOwner(_to, howMuch,2);
         if(referer!=0 && referer!=address(this) && referer!=0x0000000000000000000000000000000000000000 && howMuch.div(10)>0) {
             transferFromOwner(referer, howMuch.div(10),1);
	         if(saleStatus==0) {
	             	tokensFromEther=tokensFromEther.add( howMuch.div(10));
	 				bountyAndRefsWithEther[_to]=bountyAndRefsWithEther[_to].add( howMuch.div(10));
	         	}
         }
         if(saleStatus==0) boughtWithOther[_to]= boughtWithOther[_to].add(howMuch);
    }

	   /**
     * @dev Gives refs tokens through payment on site. Main logic is on site
     * @param _to Address of  receiver
     * @param _amount Amount of tokens		
	 * @param t type of transfer. 0 is transfer, 1 bonus tokens, 2 - sale
     */
	
	function transferFromOwner(address _to, uint256 _amount, uint t) private {
	   require(_to!=address(this) && _to!=address(owner) );
        balances[owner]=balances[owner].sub(_amount); 
        balances[_to]=balanceOf(_to).add(_amount);
        updateDate(_to);

        increaseGlobalInterestAmount(_amount);
	    
	   
	     if(t==2) emit Sale(_to, _amount);
        emit Transfer(owner, _to, _amount);	     
	}
	

    function increaseGlobalInterestAmount(uint256 c) private 
    {
        globalInterestAmount=globalInterestAmount.add(c);
		
    }
    
    function decreaseGlobalInterestAmount(uint256 c) private
    {
        if(c<globalInterestAmount) {
            globalInterestAmount=globalInterestAmount.sub(c);
        }
            
        
    }
    
    function() external payable {
        createTokensFromEther();
    }

    
}
function sendEtherToMultisig() public  returns(uint256 e) {
    uint256 req;
    require(msg.sender==owner || msg.sender==multisig);
    require(saleStatus!=0);

    if(saleStatus==2) {
    	// calculate ether for refunds
    	req=tokensFromEther.mul(1 ether).div(step0Rate).div(2);

    	if(bDbgEnabled) emit DebugLog("This balance is", this.balance);
    	if(req>=this.balance) return 0;
	}
	else if(saleStatus==1) {
		require(getNow()-stage2StartTime>15768000);
		req=0; 
	}
    uint256 amount;
    amount=this.balance.sub(req);
    multisig.transfer(amount);
    return amount;
        
}

/**
 * @dev Sends collected ether to owner. If sale is not success, contract will hold ether for half year, and after, ether can be sent to owner
 * @return amount of owner's ether
 */