contract LockerDistribution {

    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    IERC20 public rewardToken;


    struct Reward {
        uint256 amount;
        uint256 distributed;
    }
    mapping(address => Reward) public allocation;

    /// @dev The address of the account which currently has administrative capabilities over this contract.
    address public governance;

    address public pendingGovernance;

    uint256 public distributeTimestamp;

    bool public pause;

    event AllocationAdd(address indexed user, uint256 amount);
    event RewardClaimed(address indexed user, uint256 amount);
    event RewardWithdrawn(address indexed user, uint256 amount);

    event PendingGovernanceUpdated(
      address pendingGovernance
    );

    event GovernanceUpdated(
      address governance
    );


    // solium-disable-next-line
    constructor(address _governance) public {
      require(_governance != address(0), "LockerDistribution: governance address cannot be 0x0");
      governance = _governance;
      pause = true;
    }

    /*
     * Owner methods
     */
    function initialize(IERC20 _rewardToken, address[] memory _recipient, uint256[] memory _rewardAmounts, uint256 _distributeTimestamp) external onlyGovernance {

        rewardToken = _rewardToken;
        distributeTimestamp = _distributeTimestamp;
        require(_recipient.length == _rewardAmounts.length, "LockerDistribution: allocation length mismatch");

        for (uint256 i = 0; i < _recipient.length; i++) {
          require(
              _recipient[i] != address(0),
              "LockerDistribution: recipient cannot be 0 address."
          );

          require(
              _rewardAmounts[i] > 0,
              "LockerDistribution: cannot allocate zero amount."
          );


          // Add new allocation to beneficiaryAllocations
          allocation[_recipient[i]] = Reward(
              _rewardAmounts[i],
              0
          );

          emit AllocationAdd(_recipient[i], _rewardAmounts[i]);
      }
    }

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

    /// @dev Sets the governance.
    ///
    /// This function can only called by the current governance.
    ///
    /// @param _pendingGovernance the new pending governance.
    function setPendingGovernance(address _pendingGovernance) external onlyGovernance {
      require(_pendingGovernance != address(0), "LockerDistribution: pending governance address cannot be 0x0");
      pendingGovernance = _pendingGovernance;

      emit PendingGovernanceUpdated(_pendingGovernance);
    }

    function acceptGovernance() external {
      require(msg.sender == pendingGovernance, "LockerDistribution: only pending governance");

      address _pendingGovernance = pendingGovernance;
      governance = _pendingGovernance;

      emit GovernanceUpdated(_pendingGovernance);
    }

    function setPause(bool _pause) external onlyGovernance {
      pause = _pause;
    }

    function emergencyWithdraw(address transferTo) external onlyGovernance {
        require(pause == true, "LockerDistribution: not paused");
        uint256 rewardAmount = rewardToken.balanceOf(address(this));
        rewardToken.safeTransfer(transferTo, rewardAmount);

        emit RewardWithdrawn(transferTo, rewardAmount);
    }

    function getInitialRewardAmount(address userAddress) external view returns (uint256 initialRewardAmount) {
        uint256 initRewardAmount = allocation[userAddress].amount;
        return initRewardAmount;
    }

    function getDistributedAmount(address userAddress) public view returns (uint256 distributedAmount) {
        uint256 distributedAmt = allocation[userAddress].distributed;
        return distributedAmt;
    }

    function getAvailableAmount(address userAddress) public view returns (uint256 amountAvailable) {
      uint256 avalible = (allocation[userAddress].amount).sub(allocation[userAddress].distributed);
      return avalible;
    }

    function withdraw() public {

      address userAddress = msg.sender;
      uint256 withdrawAmount = allocation[userAddress].amount;
      require(
          pause == false,
          "LockerDistribution: withdraw paused"
      );

      require(
          block.timestamp >= distributeTimestamp,
          "LockerDistribution: not start yet"
      );

      require(getAvailableAmount(userAddress) >= withdrawAmount ,"LockerDistribution: insufficient avalible balance");

      allocation[userAddress].distributed = allocation[userAddress].distributed.add(withdrawAmount);

      rewardToken.safeTransfer(userAddress, withdrawAmount);

      emit RewardClaimed(userAddress, withdrawAmount);
    }
}
function setPendingGovernance(address _pendingGovernance) external onlyGovernance {
  require(_pendingGovernance != address(0), "LockerDistribution: pending governance address cannot be 0x0");
  pendingGovernance = _pendingGovernance;

  emit PendingGovernanceUpdated(_pendingGovernance);
}

/// @dev Sets the governance.
///
/// This function can only called by the current governance.
///
/// @param _pendingGovernance the new pending governance.