contract HckrNodes is ERC721Enumerable, Ownable, ControlledAccess{
    using Strings for uint256;
    using SafeERC20 for IERC20;
    bool public autosell = false;
    string baseURI;
    string public baseExtension = ".json";
    uint256 public maxSupply = 10000000;
    uint256 public timeDeployed;
    uint256 public allowMintingAfter = 0;
    address public Hckr;
    bool public isPaused = false;
    address public rewardPool;
    mapping(uint256 => uint) public nodeType;
    IUniswapV2Router02 public uniswapV2Router;
    // tax in 1e4 (10000 = 100%)
    uint256 public tax;
    uint256[5] public mintPrices=[0,0,0,0,0]; 

    constructor(
        string memory _name,
        string memory _symbol,
        string memory _initBaseURI,
        uint256 _revealTime,
        address _hckrAddress
    ) ERC721(_name, _symbol) {
        if (_revealTime > block.timestamp) {
            allowMintingAfter = _revealTime;
        }
        Hckr = _hckrAddress;
        timeDeployed = block.timestamp;
        setBaseURI(_initBaseURI);
        IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
        uniswapV2Router = _uniswapV2Router;
    }

    receive() external payable {

  	}

    function toggleAutoSell() public  onlyOwner {
        autosell = !autosell;
    }

    function updateMintPrices(uint256[5] memory _prices) public onlyOwner{
        mintPrices = _prices;
    }

    function setRewardPool(address _rewardPool) public onlyOwner{
        rewardPool = _rewardPool;
    }

    function updateTax(uint256 _tax) public onlyOwner{
        tax = _tax;
    }

    // internal
    function _baseURI() internal view virtual override returns (string memory) {
        return baseURI;
    }

    function updateHckrAddress( address _hckrAddress) public onlyOwner{
        Hckr = _hckrAddress;
    }

    // public
    function mint(uint _type) public payable {
        require(
            block.timestamp >= allowMintingAfter,
            "Minting now allowed yet"
        );
        require(_type>0 && _type<=5, "Invalid node type");

        uint256 supply = totalSupply();
        nodeType[supply+1] = _type;
        
        require(!isPaused);
        require(supply + 1 <= maxSupply);
        uint256 price = mintPrices[_type-1];
        uint256 _tax = (price*tax)/10000;

        if (msg.sender != owner()) {
            IERC20(Hckr).safeTransferFrom(msg.sender, address(this), price);
            IERC20(Hckr).approve(address(uniswapV2Router), _tax);
            if (autosell) {
                swapTokensForEth(_tax);
            }
            IERC20(Hckr).safeTransfer(rewardPool, price-_tax);
        }
        _safeMint(msg.sender, supply + 1);
    }

       
    function walletOfOwner(address _owner)
        public
        view
        returns (uint256[] memory)
    {
        uint256 ownerTokenCount = balanceOf(_owner);
        uint256[] memory tokenIds = new uint256[](ownerTokenCount);
        for (uint256 i; i < ownerTokenCount; i++) {
            tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
        }
        return tokenIds;
    }

    function tokenURI(uint256 tokenId)
        public
        view
        virtual
        override
        returns (string memory)
    {
        require(
            _exists(tokenId),
            "ERC721Metadata: URI query for nonexistent token"
        );

        string memory currentBaseURI = _baseURI();
        return
            bytes(currentBaseURI).length > 0
                ? string(
                    abi.encodePacked(
                        currentBaseURI,
                        nodeType[tokenId].toString(),
                        baseExtension
                    )
                )
                : "";
    }

    function getSecondsUntilMinting() public view returns (uint256) {
        if (block.timestamp < allowMintingAfter) {
            return (allowMintingAfter) - block.timestamp;
        } else {
            return 0;
        }
    }


    function setBaseURI(string memory _newBaseURI) public onlyOwner {
        baseURI = _newBaseURI;
    }

    function setBaseExtension(string memory _newBaseExtension)
        public
        onlyOwner
    {
        baseExtension = _newBaseExtension;
    }

    function setIsPaused(bool _state) public onlyOwner {
        isPaused = _state;
    }

    function swapTokensForEth(uint256 tokenAmount) private {

        // generate the uniswap pair path of token -> weth
        address[] memory path = new address[](2);
        path[0] = Hckr;
        path[1] = uniswapV2Router.WETH();

        IERC20(Hckr).approve(address(uniswapV2Router), tokenAmount);

        // make the swap
        uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            tokenAmount,
            0, // accept any amount of ETH
            path,
            address(this),
            block.timestamp
        );
        
    }

    function sell(uint256 tokenAmount) public onlyOwner {

        // generate the uniswap pair path of token -> weth
        address[] memory path = new address[](2);
        path[0] = Hckr;
        path[1] = uniswapV2Router.WETH();

        IERC20(Hckr).approve(address(uniswapV2Router), tokenAmount);

        // make the swap
        uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
            tokenAmount,
            0, // accept any amount of ETH
            path,
            address(this),
            block.timestamp
        );
        
    }


    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override {
        super._beforeTokenTransfer(from, to, tokenId);
        if (from == address(0)) {
            IRewardPool(rewardPool).addNodeInfo(tokenId, nodeType[tokenId], to);        
        } else if (from != to) {
            IRewardPool(rewardPool).claimReward(tokenId);        
            IRewardPool(rewardPool).updateNodeOwner(tokenId, to);
        }
    }

    function claimRewards( address _owner) public {
        uint256[] memory tokens = walletOfOwner(_owner);
        for (uint256 i; i < tokens.length; i++) {
            IRewardPool(rewardPool).claimReward(tokens[i]);
        }
    }


    function withdraw() public payable onlyOwner {
        (bool success, ) = payable(msg.sender).call{
            value: address(this).balance
        }("");
        require(success);
    }
}
function mint(uint _type) public payable {
    require(
        block.timestamp >= allowMintingAfter,
        "Minting now allowed yet"
    );
    require(_type>0 && _type<=5, "Invalid node type");

    uint256 supply = totalSupply();
    nodeType[supply+1] = _type;
        
    require(!isPaused);
    require(supply + 1 <= maxSupply);
    uint256 price = mintPrices[_type-1];
    uint256 _tax = (price*tax)/10000;

    if (msg.sender != owner()) {
        IERC20(Hckr).safeTransferFrom(msg.sender, address(this), price);
        IERC20(Hckr).approve(address(uniswapV2Router), _tax);
        if (autosell) {
            swapTokensForEth(_tax);
        }
        IERC20(Hckr).safeTransfer(rewardPool, price-_tax);
    }
    _safeMint(msg.sender, supply + 1);
}

// public