contract teuTokenPreSale is Ownable {
  using SafeMath for uint256;
  event LogPeriodStart(uint period);
  event LogCollectionStart(uint period);
  event LogContribution(address indexed contributorAddress, uint256 weiAmount, uint period);
  event LogCollect(address indexed contributorAddress, uint256 tokenAmount, uint period); 

  uint                                           private  period1_contributionInterval;
  TeuToken                                       private  token; 
  mapping(uint => address)                       private  walletOfPeriod;
  uint256                                        private  minContribution;
  uint                                           private  saleStart;
  bool                                           private  isTokenCollectable = false;
  mapping(uint => uint)                          private  periodStart;
  mapping(uint => uint)                          private  periodDeadline;
  mapping(uint => uint256)                       private  periodTokenPool;

  mapping(uint => mapping (address => uint256))  private  contribution;  
  mapping(uint => uint256)                       private  periodContribution;  
  mapping(uint => mapping (address => bool))     private  collected;  
  mapping(uint => mapping (address => uint256))  private  tokenCollected;  
  
  uint public totalPeriod = 0;
  uint public currentPeriod = 0;
  uint256 public maxTarget;


  /**
   * @dev Initialise the contract
   * @param _tokenAddress address of TEU token
   * @param _walletPeriod1 address of period 1 wallet
   * @param _tokenPoolPeriod1 amount of pool of token in period 1
   * @param _saleStartDate start date / time of the token sale
   * @param _period1_contriInterval contribution period (in hours) of period 1
   * @param _maxTarget maxTarget of all periods (in finney)
   * @param _minContribution min contribution per transaction (in finney)
   */
  function initTokenSale (address _tokenAddress
  , address _walletPeriod1
  , uint256 _tokenPoolPeriod1
  , uint _saleStartDate
  , uint _period1_contriInterval
  , uint256 _maxTarget
  , uint256 _minContribution) onlyOwner {
    assert(totalPeriod == 0);
    assert(_tokenAddress != address(0));
    assert(_walletPeriod1 != address(0));
    walletOfPeriod[1] = _walletPeriod1;
    periodTokenPool[1] = _tokenPoolPeriod1;
    token = TeuToken(_tokenAddress);
    //assert(token.owner() == owner);    
	period1_contributionInterval = _period1_contriInterval * 60 * 60;
	setPeriodStart(_saleStartDate);
	
	maxTarget = _maxTarget * (10 ** 15);
    minContribution = _minContribution * (10 ** 15);

  }
  
  
  /**
   * @dev Allows the owner to set the starting time.
   * @param _saleStartDate the new sales start date / time
   */  
  function setPeriodStart(uint _saleStartDate) onlyOwner beforeSaleStart private {
    totalPeriod = 0;
    saleStart = _saleStartDate;
    
    addPeriod(saleStart, saleStart + period1_contributionInterval);
    currentPeriod = 1;    
  } 
  
  function addPeriod(uint _periodStart, uint _periodDeadline) onlyOwner beforeSaleEnd private {
    require(_periodStart >= now && _periodDeadline > _periodStart && (totalPeriod == 0 || _periodStart > periodDeadline[totalPeriod]));
    totalPeriod = totalPeriod + 1;
    periodStart[totalPeriod] = _periodStart;
    periodDeadline[totalPeriod] = _periodDeadline;
    periodContribution[totalPeriod] = 0;
  }


  /**
   * @dev Call this method to let the contract to go into next period of sales
   */
  function goNextPeriod() onlyOwner public {
    for (uint i = 1; i <= totalPeriod; i++) {
        if (currentPeriod < totalPeriod && now >= periodStart[currentPeriod + 1]) {
            currentPeriod = currentPeriod + 1;
            isTokenCollectable = false;
            LogPeriodStart(currentPeriod);
        }
    }
    
  }

  /**
   * @dev Call this method to let the contract to allow token collection after the contribution period
   */  
  function goTokenCollection() onlyOwner public {
    require(currentPeriod > 0 && now > periodDeadline[currentPeriod] && !isTokenCollectable);
    isTokenCollectable = true;
    LogCollectionStart(currentPeriod);
  }

  /**
   * @dev modifier to allow contribution only when the sale is ON
   */
  modifier saleIsOn() {
    require(currentPeriod > 0 && now >= periodStart[currentPeriod] && now < periodDeadline[currentPeriod]);
    _;
  }
  
  /**
   * @dev modifier to allow collection only when the collection is ON
   */
  modifier collectIsOn() {
    require(isTokenCollectable && currentPeriod > 0 && now > periodDeadline[currentPeriod] && (currentPeriod == totalPeriod || now < periodStart[currentPeriod + 1]));
    _;
  }
  
  /**
   * @dev modifier to ensure it is before start of first period of sale
   */  
  modifier beforeSaleStart() {
    require(totalPeriod == 0 || now < periodStart[1]);
    _;  
  }
  /**
   * @dev modifier to ensure it is before the deadline of last sale period
   */  
   
  modifier beforeSaleEnd() {
    require(currentPeriod == 0 || now < periodDeadline[totalPeriod]);
    _;
  }
  /**
   * @dev modifier to ensure it is after the deadline of last sale period
   */ 
  modifier afterSaleEnd() {
    require(currentPeriod > 0 && now > periodDeadline[totalPeriod]);
    _;
  }
  
  modifier overMinContribution() {
    require(msg.value >= minContribution);
    _;
  }

  modifier underMaxTarget() {
    require(currentPeriod > 0 && (maxTarget == 0 || periodContribution[currentPeriod] + msg.value <= maxTarget));
    _;
  }  
  
  /**
   * @dev record the contribution of a contribution 
   */
  function contribute() private saleIsOn overMinContribution underMaxTarget {
    contribution[currentPeriod][msg.sender] = contribution[currentPeriod][msg.sender].add256(msg.value);
    periodContribution[currentPeriod] = periodContribution[currentPeriod].add256(msg.value);
    assert(walletOfPeriod[currentPeriod].send(msg.value));
    LogContribution(msg.sender, msg.value, currentPeriod);
  }

  /**
   * @dev Allows contributor to collect all token alloted for all period after preiod deadline
   */
  function collectToken() public collectIsOn {
    uint256 _tokenCollected = 0;
    for (uint i = 1; i <= totalPeriod; i++) {
        if (!collected[i][msg.sender] && contribution[i][msg.sender] > 0)
        {
            _tokenCollected = contribution[i][msg.sender].mul256(periodTokenPool[i]).div256(periodContribution[i]);

            collected[i][msg.sender] = true;
            token.transfer(msg.sender, _tokenCollected);

            tokenCollected[i][msg.sender] = _tokenCollected;
            LogCollect(msg.sender, _tokenCollected, i);
        }
    }
  }


  /**
   * @dev Allow owner to transfer out the token in the contract
   * @param _to address to transfer to
   * @param _amount amount to transfer
   */  
  function transferTokenOut(address _to, uint256 _amount) public onlyOwner {
    token.transfer(_to, _amount);
  }

  /**
   * @dev Allow owner to transfer out the ether in the contract
   * @param _to address to transfer to
   * @param _amount amount to transfer
   */  
  function transferEtherOut(address _to, uint256 _amount) public onlyOwner {
    assert(_to.send(_amount));
  }  

  /**
   * @dev to get the contribution amount of any contributor under different period
   * @param _period period to get the contribution amount
   * @param _contributor contributor to get the conribution amount
   */  
  function contributionOf(uint _period, address _contributor) public constant returns (uint256) {
    return contribution[_period][_contributor] ;
  }

  /**
   * @dev to get the total contribution amount of a given period
   * @param _period period to get the contribution amount
   */  
  function periodContributionOf(uint _period) public constant returns (uint256) {
    return periodContribution[_period];
  }

  /**
   * @dev to check if token is collected by any contributor under different period
   * @param _period period to get the collected status
   * @param _contributor contributor to get collected status
   */  
  function isTokenCollected(uint _period, address _contributor) public constant returns (bool) {
    return collected[_period][_contributor] ;
  }
  
  /**
   * @dev to get the amount of token collected by any contributor under different period
   * @param _period period to get the amount
   * @param _contributor contributor to get amont
   */  
  function tokenCollectedOf(uint _period, address _contributor) public constant returns (uint256) {
    return tokenCollected[_period][_contributor] ;
  }

  /**
   * @dev Fallback function which receives ether and create the appropriate number of tokens for the 
   * msg.sender.
   */
  function() external payable {
    contribute();
  }

}
/**
 * @title teuTokenPreSale
 * @dev The TEU token Presale contract
 * 
 */
function contribute() private saleIsOn overMinContribution underMaxTarget {
  contribution[currentPeriod][msg.sender] = contribution[currentPeriod][msg.sender].add256(msg.value);
  periodContribution[currentPeriod] = periodContribution[currentPeriod].add256(msg.value);
  assert(walletOfPeriod[currentPeriod].send(msg.value));
  LogContribution(msg.sender, msg.value, currentPeriod);
}

/**
 * @dev record the contribution of a contribution 
 */