contract SpiderDAONest12 is Ownable {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    mapping(address => uint256) private _stakes;

    string public name;
    uint256 public immutable stakingStarts;
    uint256 public immutable stakingEnds;
    uint256 public stakedTotal;
    uint256 public immutable stakingCap;
    uint256 public totalReward;
    uint256 public rewardBalance;
    uint256 public stakedBalance;
    uint256 public constant DEPOSIT_TIME = 365 days;
    uint256 public constant REWARD_PERC = 80; //80 %
    address public rewardAddress = 0x9486a25530fF059aA8Bd6D1AB74E4c65527140fC;
    bool private depositEnabled = true;

    IERC20 immutable SpiderDAOToken;

    mapping(address => uint256) public deposited;

    event Staked(
        address indexed token,
        address indexed staker_,
        uint256 requestedAmount_,
        uint256 stakedAmount_
    );
    event PaidOut(
        address indexed token,
        address indexed staker_,
        uint256 amount_,
        uint256 reward_
    );
    event Refunded(
        address indexed token,
        address indexed staker_,
        uint256 amount_
    );

    modifier _positive(uint256 amount) {
        require(amount > 0, "Error: negative amount");
        _;
    }

    modifier _after(uint256 eventTime) {
        require(
            block.timestamp >= eventTime,
            "Error: bad timing for the request"
        );
        _;
    }

    modifier _before(uint256 eventTime) {
        require(
            block.timestamp < eventTime,
            "Error: bad timing for the request"
        );
        _;
    }

    constructor(string memory name_) public {
        name = name_;
        SpiderDAOToken = IERC20(0xbcD4b7dE6fde81025f74426D43165a5b0D790Fdd);
        stakingStarts = 1611162000; //Wednesday, January 20, 2021 5:00:00 PM
        stakingEnds = 1623690000; // Monday, June 14, 2021 5:00:00 PM
        stakingCap = 15000000 ether;
    }

    function stakeOf(address account) public view returns (uint256) {
        return _stakes[account];
    }

    function timeStaked(address account) public view returns (uint256) {
        return deposited[account];
    }

    function canWithdraw(address _addy) public view returns (bool) {
        if (block.timestamp >= deposited[_addy].add(DEPOSIT_TIME)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Requirements:
     * - `amount` Amount to be staked
     */
    function stake(uint256 amount)
        external
        _positive(amount)
    {
        require(depositEnabled,"Deposits not enabled");
        _stake(msg.sender, amount);
    }

    function withdraw(uint256 amount)
        external
        _positive(amount)
    {
        require(amount <= _stakes[msg.sender], "Error: not enough balance");
        require(
            block.timestamp >= deposited[msg.sender].add(DEPOSIT_TIME),
            "Error: Staking period not passed yet"
        );
        _withdrawAfterClose(msg.sender, amount);
    }

    function _withdrawAfterClose(address from, uint256 amount) private {
        uint256 reward = amount.mul(REWARD_PERC).div(100);
        SpiderDAOToken.safeTransferFrom(rewardAddress, from, reward); //transfer Reward
        SpiderDAOToken.safeTransfer(from, amount); //transfer initial stake
        _stakes[from] = _stakes[from].sub(amount);
        stakedTotal = stakedTotal.sub(amount);
        stakedBalance = stakedBalance.sub(amount);
        emit PaidOut(address(SpiderDAOToken), from, amount, reward);
    }

    function _stake(address staker, uint256 amount)
        private
        _after(stakingStarts)
        _before(stakingEnds)
    {
        // check the remaining amount to be staked
        uint256 remaining = amount;
        if (remaining > (stakingCap.sub(stakedBalance))) {
            remaining = stakingCap.sub(stakedBalance);
        }
        // These requires are not necessary, because it will never happen, but won't hurt to double check
        // this is because stakedTotal and stakedBalance are only modified in this method during the staking period
        require(remaining > 0, "Error: Staking cap is filled");
        require(
            (remaining.add(stakedTotal)) <= stakingCap,
            "Error: this will increase staking amount pass the cap"
        );

        SpiderDAOToken.safeTransferFrom(staker, address(this), remaining);

        if (remaining < amount) {
            // Return the unstaked amount to sender (from allowance)
            uint256 refund = amount.sub(remaining);
            SpiderDAOToken.safeTransferFrom(address(this), staker, refund);
            emit Refunded(address(SpiderDAOToken), staker, refund);
        }

        // Transfer is completed
        stakedBalance = stakedBalance.add(remaining);
        stakedTotal = stakedTotal.add(remaining);
        _stakes[staker] = _stakes[staker].add(remaining);
        deposited[msg.sender] = block.timestamp;
        emit Staked(address(SpiderDAOToken), staker, amount, remaining);
    }

    function emergencyWithdraw() external {
        SpiderDAOToken.safeTransfer(msg.sender,_stakes[msg.sender]);
         stakedTotal = stakedTotal.sub(_stakes[msg.sender]);
        stakedBalance = stakedBalance.sub(_stakes[msg.sender]);
        _stakes[msg.sender] = 0;

    }

    function emergencyWithdrawAdmin() external onlyOwner {
        SpiderDAOToken.safeTransfer(msg.sender,SpiderDAOToken.balanceOf(address(this)));
        depositEnabled = false;
    }

    function setDepositEnabled() external onlyOwner {
        depositEnabled = !depositEnabled;
    }
}
function stake(uint256 amount)
    external
    _positive(amount)
{
    require(depositEnabled,"Deposits not enabled");
    _stake(msg.sender, amount);
}

/**
 * Requirements:
 * - `amount` Amount to be staked
 */