contract DepositRewardV1 {
  using SafeMath for uint256;
  using SafeERC20 for IERC20;

  /* ========== STATE VARIABLES ========== */

  address public governance;
  IMIXToken public mix;
  address public teamAddr;

  uint256 public rewardPerBlock = 25 * 10**16; // 0.25
  uint256 public rewardReductionMin = 8000;
  uint256 public teamRewardMin = 1000;
  uint256 constant public teamRewardMax = 2000;
  uint256 constant public MAX = 10000;
  uint256 public blocksPerDuration = 6583 * 30; // 30 days
  uint256 public depositsInCurrentBlock;
  uint256 public depositsInPreviousBlock;
  uint256 public lastUpdatedBlock;
  uint256 public blockFinish;

  mapping (address => bool) public mixers;
  mapping (address => uint256) public mixerAllocPoints;
  uint256 public totalAllocPoints;

  /* ========== CONSTRUCTOR ========== */

  constructor(address _mix) public {
    mix = IMIXToken(_mix);
    governance = msg.sender;
    teamAddr = msg.sender;
    blockFinish = block.number.add(blocksPerDuration);
  }

  /* ========== VIEW FUNCTIONS ========== */

  function rewardPerDeposit() public view returns(uint256) {
    if (depositsInPreviousBlock == 0) {
      return rewardPerBlock;
    }

    return rewardPerBlock.div(depositsInPreviousBlock);
  }

  function rewardRemaining() public view returns (uint256) {
    return mix.balanceOf(address(this));
  }

  /* ========== MUTIVE FUNCTIONS ========== */

  function distributeReward(address _recipient) external updateRewardRate checkReduce {
    require(mixers[msg.sender] == true, "!mixer");

    uint256 rewardAmount = rewardPerDeposit().mul(mixerAllocPoints[msg.sender]).div(totalAllocPoints);
    mix.mint(teamAddr, rewardAmount.mul(teamRewardMin).div(MAX));
    mix.mint(_recipient, rewardAmount);

    emit RewardSent(msg.sender, _recipient, rewardAmount);
  }

  /* ========== GOVERNANCE FUNCTIONS ========== */

  function setGovernance(address _governance) public onlyGovernance {
    governance = _governance;
  }

  function setTeamAddress(address _teamAddr) public onlyTeam {
    teamAddr = _teamAddr;
  }

  function setRewardPerBlock(uint256 _rewardPerBlock) public onlyGovernance {
    rewardPerBlock = _rewardPerBlock;
  }

  function setRewardReductionMin(uint256 _rewardReductionMin) public onlyGovernance {
    rewardReductionMin = _rewardReductionMin;
  }

  function setTeamRewardMin(uint256 _teamRewardMin) public onlyTeam {
    require(_teamRewardMin <= teamRewardMax, "over reward max");
    teamRewardMin = _teamRewardMin;
  }

  function addMixer(address _mixer, uint256 _allocPoint) public onlyGovernance {
    require(mixers[_mixer] == false, "mixer already added");

    mixers[_mixer] = true;
    mixerAllocPoints[_mixer] = _allocPoint;
    totalAllocPoints = totalAllocPoints.add(_allocPoint);
  }

  function setAllocPoint(address _mixer, uint256 _allocPoint) public onlyGovernance {
    require(mixers[_mixer] == true, "mixer not added");

    totalAllocPoints = totalAllocPoints.sub(mixerAllocPoints[_mixer]);
    mixerAllocPoints[_mixer] = _allocPoint;
    totalAllocPoints = totalAllocPoints.add(totalAllocPoints);
  }

  function rescueToken(address _token, uint256 _amount) public onlyGovernance {
    SafeERC20.safeTransfer(IERC20(_token), governance, _amount);
  }

  /* ========== MODIFIERS ========== */

  modifier onlyGovernance() {
    require(msg.sender == governance, "!governance");
    _;
  }

  modifier onlyTeam() {
    require(msg.sender == teamAddr, "!teamAddr");
    _;
  }

  modifier updateRewardRate() {
    if (block.number > lastUpdatedBlock) {
      depositsInPreviousBlock = depositsInCurrentBlock;
      depositsInCurrentBlock = 1;
    } else {
      depositsInCurrentBlock++;
    }

    lastUpdatedBlock = now;
    _;
  }

  modifier checkReduce() {
    if (block.number >= blockFinish) {
      rewardPerBlock = rewardPerBlock.mul(rewardReductionMin).div(MAX);
      blockFinish = block.number.add(blocksPerDuration);
    }
    _;
  }

  /* ========== EVENTS ========== */
  event RewardSent(address indexed mixer, address indexed recipient, uint256 amount);

}
function setGovernance(address _governance) public onlyGovernance {
  governance = _governance;
}

/* ========== GOVERNANCE FUNCTIONS ========== */