contract ERC20 is Context, IERC20 {
    using SafeMath for uint256;
    using Address for address;
    
    
    mapping (address => uint256) private _balances;
    mapping(address => bool) public feeExcludedAddress;
    mapping (address => mapping (address => uint256)) private _allowances;
    mapping (uint256 => bool) public isAirdroped;
    mapping (uint256 => uint256) lastReward;

    uint256 private _totalSupply;
    // Daily Rewards Distributions Start from
    uint256 private rewardStartDate;
    bool public dailyReward = false;
    uint256 public rewardAmount = 2 ether;
    // ends in a month;
    uint256 public airdropEndDate = 1634991797;
    

    string private _name;
    string private _symbol;
    uint private _decimals = 18;
    uint private _lockTime;
    address public _Owner;
    address public _previousOwner;
    address public _fundAddress;
    address public liquidityPair;
    uint public teamFee = 500; //0.2% divisor 100
    bool public sellLimiter; //by default false
    uint public sellLimit = 50000 * 10 ** 18; //sell limit if sellLimiter is true
    IHorse public horseContract;
    
    uint256 public _maxTxAmount = 5000000 * 10**18;
    
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    event airdropClaimed(uint256 tokenID, address tokenOwner);
    event claimedDailyReward(uint256 tokenID, address claimer, uint256 timestamp);

    /**
     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
     * a default value of 18.
     *
     * To select a different value for {decimals}, use {_setupDecimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor (string memory _nm, string memory _sym, IHorse _horseContract) public {
        _name = _nm;
        _symbol = _sym;
        _Owner = msg.sender;
        rewardStartDate = block.timestamp;
        horseContract = _horseContract;
        feeExcludedAddress[msg.sender] = true;
        _fundAddress = address(0xA25D6e6E02D1fD7A886b83A906c91cde7eD8DdFC);
    }
    
    modifier onlyOwner{
        require(msg.sender == _Owner, 'Only Owner Can Call This Function');
        _;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5,05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
     * called.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view returns (uint) {
        return _decimals;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }
    
    function calculateTeamFee(uint256 _amount) internal view returns (uint256) {
        return _amount.mul(teamFee).div(
            10**4
        );
    }
    
    function setTeamFee(uint Tfee) public onlyOwner{
        require(Tfee < 1500," Fee can't exceed to 15%");
        teamFee = Tfee;
    }
    
    function toggleSellLimit() external onlyOwner() {
        sellLimiter = !sellLimiter;
    }
    
    function stopReward() external onlyOwner() {
        require(dailyReward, "Daily Reward Already Stopped");
        dailyReward = false;
    }
    
    function startReward() public onlyOwner{
        require(!dailyReward, "Daily Reward Already Running");
        dailyReward = true;
        rewardStartDate = block.timestamp;
    }
    
    function changeRewardAmount(uint256 _amount) public onlyOwner{
        rewardAmount = _amount;
    }
    
    function setLiquidityPairAddress(address liquidityPairAddress) public onlyOwner{
        liquidityPair = liquidityPairAddress;
    }
    
    function changeSellLimit(uint256 _sellLimit) public onlyOwner{
        sellLimit = _sellLimit;
    }
    
    function changeMaxtx(uint256 _maxtx) public onlyOwner{
        _maxTxAmount = _maxtx;
    }
    
    function changeFundAddress(address Taddress) public onlyOwner{
        _fundAddress = Taddress;
    }
    
    function changeAirdropEndDate(uint256 endDate) public onlyOwner{
        require(endDate > block.timestamp,"End Date must be a future timestamp");
        airdropEndDate = endDate;
    }
    
    function addExcludedAddress(address excludedA) public onlyOwner{
        feeExcludedAddress[excludedA] = true;
    }
    
    function removeExcludedAddress(address excludedA) public onlyOwner{
        feeExcludedAddress[excludedA] = false;
    }
    
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_Owner, newOwner);
        _Owner = newOwner;
    }

    function geUnlockTime() public view returns (uint256) {
        return _lockTime;
    }

    //Locks the contract for owner for the amount of time provided
    function lock(uint256 time) public virtual onlyOwner {
        _previousOwner = _Owner;
        _Owner = address(0);
        _lockTime = block.timestamp + time;
        emit OwnershipTransferred(_Owner, address(0));
    }
    
    //Unlocks the contract for owner when _lockTime is exceeds
    function unlock() public virtual {
        require(_previousOwner == msg.sender, "You don't have permission to unlock");
        require(block.timestamp > _lockTime , "Contract is locked until 7 days");
        emit OwnershipTransferred(_Owner, _previousOwner);
        _Owner = _previousOwner;
    }
    
    function multiTransfer(address[] memory receivers, uint256[] memory amounts) public {
        require(receivers.length != 0, 'Cannot Proccess Null Transaction');
        require(receivers.length == amounts.length, 'Address and Amount array length must be same');
        for (uint256 i = 0; i < receivers.length; i++) {
            transfer(receivers[i], amounts[i]);
        }
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        if(feeExcludedAddress[recipient] || feeExcludedAddress[_msgSender()]){
            _transferExcluded(_msgSender(), recipient, amount);
        }else{
            _transfer(_msgSender(), recipient, amount);    
        }
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20};
     *
     * Requirements:
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        if(feeExcludedAddress[recipient] || feeExcludedAddress[sender]){
            _transferExcluded(sender, recipient, amount);
        }else{
            _transfer(sender, recipient, amount);
        }
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transferExcluded(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");
        if(sender != _Owner && recipient != _Owner)
            require(amount <= _maxTxAmount, "Transfer amount exceeds the maxTxAmount.");
            
        if(recipient == liquidityPair && balanceOf(liquidityPair) > 0 && sellLimiter){
            require(amount < sellLimit, 'Cannot sell more than sellLimit');
        }

        _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
        _balances[recipient] = _balances[recipient].add(amount);
        emit Transfer(sender, recipient, amount);
    }
    
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");
        if(sender != _Owner && recipient != _Owner)
            require(amount <= _maxTxAmount, "Transfer amount exceeds the maxTxAmount.");

        if(recipient == liquidityPair && balanceOf(liquidityPair) > 0 && sellLimiter){
            require(amount < sellLimit, 'Cannot sell more than sellLimit');
        }
        
        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        _balances[sender] = senderBalance - amount;
        uint256 tokenToTransfer = amount.sub(calculateTeamFee(amount));
        _balances[recipient] += tokenToTransfer;
        _balances[_fundAddress] += calculateTeamFee(amount); 
        
        emit Transfer(sender, recipient, tokenToTransfer);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements
     *
     * - `to` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply = _totalSupply.add(amount);
        _balances[account] = _balances[account].add(amount);
        emit Transfer(address(0), account, amount);
    }
    
    function addSupply(uint256 amount) public onlyOwner{
        _mint(msg.sender, amount);
    }
    
    function claimAirdrop(uint256 tokenID) public {
        require(airdropEndDate > block.timestamp, "Airdrop is ended");
        require(horseContract.ownerOf(tokenID) == msg.sender, "You aren't own this NFT token");
        require(!isAirdroped[tokenID],"This token ID already claimed airdrop");
        _mint(msg.sender, 1000 ether);
        isAirdroped[tokenID] = true;
        emit airdropClaimed(tokenID, msg.sender);
    }
    
    function bulkClaimAirdrop(uint256[] memory tokenIDs) public {
        require(airdropEndDate > block.timestamp, "Airdrop is ended");
        for (uint256 i = 0; i < tokenIDs.length; i++) {
            require(horseContract.ownerOf(tokenIDs[i]) == msg.sender, "You aren't own this NFT token");
            require(!isAirdroped[tokenIDs[i]],"This token ID already claimed airdrop"); 
            _mint(msg.sender, 1000 ether);
            isAirdroped[tokenIDs[i]] = true;
            emit airdropClaimed(tokenIDs[i], msg.sender);
        }
    }
    
    function checkDailyReward(uint256 tokenID) public view returns (uint256){
        uint256 lastdate = (lastReward[tokenID] > rewardStartDate) ? lastReward[tokenID] : rewardStartDate;
        uint256 rewardDays = (block.timestamp - lastdate).div(1 days);
        return rewardDays.mul(rewardAmount);
    }
    
    function claimDailyReward(uint256 tokenID) public {
        require(dailyReward," Daily Rewards Are Stopped ");
        require(horseContract.ownerOf(tokenID) == msg.sender, "You aren't own this NFT token");
        require(checkDailyReward(tokenID) > 0, "There is no claimable reward");
        _mint(msg.sender, checkDailyReward(tokenID));
        lastReward[tokenID] = block.timestamp;
        emit claimedDailyReward(tokenID, msg.sender, block.timestamp);
    }
    
    function bulkClaimRewards(uint256[] memory tokenIDs) public {
        require(dailyReward," Daily Rewards Are Stopped ");
        uint256 total;
        for (uint256 i = 0; i < tokenIDs.length; i++) {
            require(horseContract.ownerOf(tokenIDs[i]) == msg.sender, "You aren't own this NFT token");
            total += checkDailyReward(tokenIDs[i]);
            if(checkDailyReward(tokenIDs[i]) > 0){
                lastReward[tokenIDs[i]] = block.timestamp;
            }
        }
        require(total > 0, "There is no claimable reward");
        _mint(msg.sender, total);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(uint256 amount) public virtual {
        require(_balances[msg.sender] >= amount,'insufficient balance!');

        _beforeTokenTransfer(msg.sender, address(0x000000000000000000000000000000000000dEaD), amount);

        _balances[msg.sender] = _balances[msg.sender].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        emit Transfer(msg.sender, address(0x000000000000000000000000000000000000dEaD), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Sets {decimals} to a value other than the default one of 18.
     *
     * WARNING: This function should only be called from the constructor. Most
     * applications that interact with token contracts will not expect
     * {decimals} to ever change, and may work incorrectly if it does.
     */
    function _setupDecimals(uint8 decimals_) internal {
        _decimals = decimals_;
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be to transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
    if(feeExcludedAddress[recipient] || feeExcludedAddress[sender]){
        _transferExcluded(sender, recipient, amount);
    }else{
        _transfer(sender, recipient, amount);
    }
    _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
    return true;
}

/**
 * @dev See {IERC20-transferFrom}.
 *
 * Emits an {Approval} event indicating the updated allowance. This is not
 * required by the EIP. See the note at the beginning of {ERC20};
 *
 * Requirements:
 * - `sender` and `recipient` cannot be the zero address.
 * - `sender` must have a balance of at least `amount`.
 * - the caller must have allowance for ``sender``'s tokens of at least
 * `amount`.
 */