contract LootSpellbook is ERC721Enumerable, ReentrancyGuard, Ownable {

    address public lootAddress = 0xFF9C1b15B16263C61d017ee9F65C50e4AE0113D7;
    LootInterface lootContract = LootInterface(lootAddress);
    
    uint256 public lootersPrice = 30000000000000000; //0.03 ETH
    uint256 public publicPrice = 150000000000000000; //0.15 ETH
    
    function contractURI() public pure returns (string memory) {
        return "https://loot-spells.herokuapp.com/contract/loot-spellbooks";
    }
    
    //generating a random number
    function random(string memory input) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(input)));
    }
    
    function generateSpell(uint256 tokenId, uint256 position) internal pure returns (uint256) {
        uint256 rand = random(string(abi.encodePacked("Spell", toString(tokenId), toString(position))));
        return rand % 410;
    }

    function generateSpellbook(uint256 tokenId) internal pure returns (uint256) {
        uint256 book = 0; 
        for (uint i=0; i<7; i++) {
            book += generateSpell(tokenId, i) * 2 ** (i*16);
        }
        return book;
    }
    
    function baseTokenURI() public pure returns (string memory) {
        return "https://loot-spells.herokuapp.com/api/spellbook/";
    }

    function tokenURI(uint256 _tokenId) override public pure returns (string memory) {
        uint256 book = generateSpellbook(_tokenId);
        return string(abi.encodePacked(baseTokenURI(), Strings.toHexString(book), "/", Strings.toString(_tokenId)));
    }

    //Private sale minting (reserved for Loot owners)
    function mintWithLoot(uint lootId) public payable nonReentrant {
        require(lootersPrice <= msg.value, "Ether value sent is not correct");
        require(lootContract.ownerOf(lootId) == msg.sender, "Not the owner of this loot");
        require(!_exists(lootId), "This token has already been minted");

        _safeMint(msg.sender, lootId);
    }
    function multiMintWithLoot(uint[] memory lootIds) public payable nonReentrant {
        require((lootersPrice * lootIds.length) <= msg.value, "Ether value sent is not correct");
        
        for (uint i=0; i<lootIds.length; i++) {
            require(lootContract.ownerOf(lootIds[i]) == msg.sender, "Not the owner of this loot");
            require(!_exists(lootIds[i]), "One of these tokens has already been minted");
            _safeMint(msg.sender, lootIds[i]);
        }
        
    }

    //Public sale minting
    function mint(uint lootId) public payable nonReentrant {
        require(publicPrice <= msg.value, "Ether value sent is not correct");
        require(lootId > 8000 && lootId <= 12000, "Token ID invalid");
        require(!_exists(lootId), "This token has already been minted");

        _safeMint(msg.sender, lootId);
    }
    function multiMint(uint[] memory lootIds) public payable nonReentrant {
        require((publicPrice * lootIds.length) <= msg.value, "Ether value sent is not correct");
        
        for (uint i=0; i<lootIds.length; i++) {
            require(lootIds[i] > 8000 && lootIds[i] <= 12000, "Token ID invalid");
            require(!_exists(lootIds[i]), "One of these tokens have already been minted");
            _safeMint(msg.sender, lootIds[i]);
        }
    }
    
    function ownerClaim(uint256 tokenId) public nonReentrant onlyOwner {
        require(tokenId > 12000 && tokenId <= 12100, "Token ID invalid");
        _safeMint(owner(), tokenId);
    }
    
    function setLootersPrice(uint256 newPrice) public onlyOwner {
        lootersPrice = newPrice;
    }

    function setPublicPrice(uint256 newPrice) public onlyOwner {
        publicPrice = newPrice;
    }

    
    function toString(uint256 value) internal pure returns (string memory) {
    // Inspired by OraclizeAPI's implementation - MIT license
    // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }
    
    constructor() ERC721("Spellbooks for Looters", "LootSpellbook") Ownable() {}
}
function random(string memory input) internal pure returns (uint256) {
    return uint256(keccak256(abi.encodePacked(input)));
}

//generating a random number