contract AdrianoFund {

  using SafeMath for uint256;

  // Logs
  event ContributionLog(address indexed sender, uint amount, uint timestamp);
  event WithdrawalLog(address indexed sender, uint amount, uint timestamp);
  event ClaimLog(address indexed sender, uint originalAmount, uint amount, uint timestamp);

  // Data Types
  address payable public fundManager;
  uint public totalFunds;
  uint public totalContributors;
  uint public fundCreatedAt;
  bool public isOpenForFunds;
  bool public isOpenForWithdrawals;
  uint public fundManagerTake = 7;

  // Tokenomics
  AdrianoERC20 public adrianoToken;
  uint public buyRate = 1000000000000;
  uint public finalRate;

  uint public totalFundsAfterClose;

  struct Contributor {
    uint amount; //wei
    uint timestamp;
    bool claimed;
  }

  mapping (address => Contributor) public contributors;

  // Modifiers
  modifier onlyManager {
    require(msg.sender == fundManager, "Only manager can call this function.");
    _;
  }

  modifier onlyContributor {
    require(contributors[msg.sender].amount > 0, "Only a contributor can widthdraw funds.");
    _;
  }

  modifier isFundOpen {
    require(isOpenForFunds, "Fund must be open to deposit/widthdraw");
    _;
  }

  modifier isFundCompleted {
    require(isOpenForWithdrawals, "Fund must be completed to widthdraw the proceeds");
    _;
  }

  modifier isUnclaimed {
    require(contributors[msg.sender].claimed == false, "You have already claimed your funds");
    _;
  }

  // Functions
  constructor(address payable manager, address tokenAddress) public {
    fundManager = manager;
    fundCreatedAt = now;
    isOpenForFunds = true;
    adrianoToken = AdrianoERC20(tokenAddress);
  }

  function transferEth(address payable _to, uint amount) private {
    // Call returns a boolean value indicating success or failure.
    // This is the current recommended method to use.
    (bool sent, bytes memory data) = _to.call{value: amount}("");
    require(sent, "Failed to send Ether");
  }

  // Phase 1: Deposit / Withdraw ether from contributors
  function deposit() public isFundOpen payable {
    require(msg.value >= buyRate, "Send at least 1000000000000 wei");
    Contributor storage contributor = contributors[msg.sender];
    if (contributor.amount == 0) {
      // Adding a new contributor
      totalContributors = totalContributors.add(1);
      contributor.timestamp = now;
    }
    totalFunds = totalFunds.add(msg.value);
    contributor.amount = contributor.amount.add(msg.value);
    adrianoToken.mint(msg.sender, msg.value.div(buyRate));
    emit ContributionLog(msg.sender, msg.value, now);
  }

  function withdraw(uint amount) public isFundOpen onlyContributor {
    Contributor storage contributor = contributors[msg.sender];
    require(amount <= contributor.amount, 'Withdraw amount must be lower than deposited');
    contributor.amount = contributor.amount.sub(amount);
    totalFunds = totalFunds.sub(amount);
    if (contributor.amount == 0) {
      totalContributors = totalContributors.sub(1);
    }
    adrianoToken.burn(msg.sender, amount.div(buyRate));
    transferEth(msg.sender, amount);
    emit WithdrawalLog(msg.sender, amount, now);
  }

  // Phase 2: Fund is closed
  function startActiveManagement() public onlyManager {
    require(totalFunds > 0, "Fund does not have any ether");
    // 7% reserved for the fund manager
    adrianoToken.mint(fundManager, adrianoToken.totalSupply().mul(fundManagerTake).div(100));
    isOpenForFunds = false;
    transferEth(fundManager, address(this).balance);
  }

  function setFundManagerTake(uint newTake) public onlyManager {
    fundManagerTake = newTake;
  }

  function endActiveManagement() public payable onlyManager {
    require(!isOpenForFunds, "The fund is not under active management");
    totalFundsAfterClose = totalFundsAfterClose.add(msg.value);
    finalRate = totalFundsAfterClose.div(adrianoToken.totalSupply());
    isOpenForWithdrawals = true;
  }

  // Phase 3: Fund is completed
  function claim() public isFundCompleted onlyContributor isUnclaimed {
    require(adrianoToken.balanceOf(msg.sender) > 0, "You do not have any participation tokens in the fund");
    require(!contributors[msg.sender].claimed, "You already claimed your reward");
    uint amount = adrianoToken.balanceOf(msg.sender).mul(finalRate);
    adrianoToken.burn(msg.sender, adrianoToken.balanceOf(msg.sender));
    contributors[msg.sender].claimed = true;
    transferEth(msg.sender, amount);
    emit ClaimLog(msg.sender, contributors[msg.sender].amount, amount, now);
  }

}
// import "@nomiclabs/buidler/console.sol";
function startActiveManagement() public onlyManager {
  require(totalFunds > 0, "Fund does not have any ether");
  // 7% reserved for the fund manager
  adrianoToken.mint(fundManager, adrianoToken.totalSupply().mul(fundManagerTake).div(100));
  isOpenForFunds = false;
  transferEth(fundManager, address(this).balance);
}

// Phase 2: Fund is closed