contract SWP4_Contest is ReentrancyGuard {

  using SafeMath for uint256;

  //Contest finish time
  uint public finishTime;
  // How many token units a buyer gets per wei.
  // The rate is the conversion between wei and the smallest and indivisible token unit.
  // So, if you are using a rate of 1 with a ERC20Detailed token with 3 decimals called TOK
  // 1 wei will give you 1 unit, or 0.001 TOK.
  uint256 public rate;
  // amount of raised money in wei
  uint256 public weiRaised;
  // amount of taken money in wei
  uint256 public weiTakenByOwner;
  //Contest owner
  address payable public owner;
  //Contest winner
  address payable public winner;
  // the token being sold
  ERC20Mintable public token;

  enum ContestStates {
        IN_PROGRESS,
        COLLECTION_FINISHED,
        WINNER_PICKED
  }

  constructor (ERC20Mintable _token, uint _rate, uint _finishTime) public {
    require(address(_token) != 0x0000000000000000000000000000000000000000, "Token address cannot be 0.");
    require(_token.isMinter(msg.sender), "Contract owner must be a token minter as well.");
    require(_rate != 0, "Rate cannot be 0.");
    require(_finishTime != 0, "Finish time cannot be 0.");

    owner = msg.sender;
    token = _token;
    rate = _rate;
    finishTime = _finishTime;
    winner = 0x0000000000000000000000000000000000000000;
  }

  //Function that can be called by creator to withdraw up to 50% of eth deposited. Contract will always hold 50% until contest is over.
  function withdraw(uint256 _amount) public payable onlyBy(owner) {
    require(weiRaised >= weiTakenByOwner.add(_amount)*2, "Cannot take too much, owner can take up to 50% of total WEI deposited");
    weiTakenByOwner = weiTakenByOwner.add(_amount);
    owner.transfer(_amount);
  }

  //Function that can be called by creator to deposit eth without having tokens issues.
  function deposit() public payable onlyBy(owner) atState(ContestStates.IN_PROGRESS) {
    require(msg.value > 0, "Cannot deposit zero ETH");
    uint256 weiAmount = msg.value;
    weiRaised = weiRaised.add(weiAmount);
  }

  //Receive eth and issue tokens at a given rate per token.
  //Contest ends at finishTime and will not except eth after that date.
  function buyTokens() public nonReentrant payable atState(ContestStates.IN_PROGRESS) {
    require(msg.value > 0, "Cannot buy tokens with zero ETH");
    require(token.isMinter(address(this)), "Contest contract MUST became a minter of token before token sale");
    uint256 weiAmount = msg.value;
    uint256 tokens = weiAmount.div(rate);
    weiRaised = weiRaised.add(weiAmount);
    token.mint(msg.sender, tokens);
  }

  //Function that can be called by creator only after finishTime, that allows creator to enter a wallet address
  //and send out remaining balance of eth in contract to entered wallet.
  function pickWinner(address payable _winner) public onlyBy(owner) atState(ContestStates.COLLECTION_FINISHED) {
    require(_winner != 0x0000000000000000000000000000000000000000, "Winner address cannot be 0");
    _winner.transfer(weiRaised.sub(weiTakenByOwner));
    winner = _winner;
  }

  function state() private view returns (ContestStates) {
    if (winner != 0x0000000000000000000000000000000000000000) return ContestStates.WINNER_PICKED;
    if (now >= finishTime) return ContestStates.COLLECTION_FINISHED;
    //'now' generates a warning, but it does not concern this contract too much.
    //Just like with any crowdsale (see OpenZeppelin examples) it is ok to base limit
    //logic on 'now'. Worst case - someone will send us some ETH right after Contest
    //finished collection
    return ContestStates.IN_PROGRESS;
  }

function() external payable atState(ContestStates.IN_PROGRESS) { buyTokens(); }

  modifier atState(ContestStates _state) {
      require(state() == _state, "Invalid Contest state");
      _;
  }

  modifier onlyBy(address _account) {
      require(msg.sender == _account, "Function only avialable to Contest owner");
      _;
  }
}
function deposit() public payable onlyBy(owner) atState(ContestStates.IN_PROGRESS) {
    require(msg.value > 0, "Cannot deposit zero ETH");
    uint256 weiAmount = msg.value;
    weiRaised = weiRaised.add(weiAmount);
  }
//Function that can be called by creator to deposit eth without having tokens issues.