contract Dragon is ERC721Enumerable, Ownable{
    using SafeMath for uint256;
    using Address for address;
    using Strings for uint256;

    string public DRAGON_PROVENANCE = "";
    string public LIZARD_PROVENANCE = "";

    address public lizardContract;

    uint256 public MAX_SUPPLY = 10000;
    bool public saleIsActive = false;
    bool public openBet = false;

    uint256 public startingIndex;
    uint256 public startingIndexBlock;

    string private _baseURIExtended;
    mapping (uint256 => string) _tokenURIs;

    constructor() ERC721("Crazy Dragon Corps","Dragon"){
    }

    function setLizardContractAddress(address contractAddress) public onlyOwner {
        lizardContract = contractAddress;
    }

    function flipSaleState() public onlyOwner {
        saleIsActive = !saleIsActive;
    }

    function flipBetState() public onlyOwner {
        openBet = !openBet;
    }

    function setLizardProvenanceHash(string memory provenanceHash) public onlyOwner {
        LIZARD_PROVENANCE = provenanceHash;
    }

    function setDragonProvenanceHash(string memory provenanceHash) public onlyOwner {
        DRAGON_PROVENANCE = provenanceHash;
    }

    function composeDragon(address owner) public returns (bool) {
        require(saleIsActive, "Dragon is not active at the moment");
        require(totalSupply().add(1) <= MAX_SUPPLY, "Compose would exceed max supply of Dragon");
        require(msg.sender == lizardContract, "wrong caller");

        _safeMint(owner, totalSupply());
        return true;
    }

    function betDragon(address owner) public returns (bool) {
        require(saleIsActive, "Dragon is not active at the moment");
        require(openBet, "The bet function is disabled");
        require(totalSupply().add(1) <= MAX_SUPPLY, "Compose would exceed max supply of Dragon");
        require(msg.sender == lizardContract, "wrong caller");
        require(!isContractAddress(owner), "owner should not be a contract");

        if (_rand() <= 33) {
            _safeMint(owner, totalSupply());
        }
        return true;
    }

    function isContractAddress(address _addr) private view returns (bool isContract){
        uint32 size;
        assembly {
            size := extcodesize(_addr)
        }
        return (size > 0);
    }

    function _rand() internal view returns(uint256) {
        uint256 seed = uint256(keccak256(abi.encodePacked(
                block.timestamp + block.difficulty +
                ((uint256(keccak256(abi.encodePacked(block.coinbase)))) / (block.timestamp)) +
                block.gaslimit +
                ((uint256(keccak256(abi.encodePacked(msg.sender)))) / (block.timestamp)) +
                block.number
            )));

        return seed % 100;
    }

    function calcStartingIndex() public onlyOwner {
        require(startingIndex == 0, "Starting index has already been set");
        require(startingIndexBlock != 0, "Starting index has not been set yet");

        startingIndex = uint(blockhash(startingIndexBlock)) % MAX_SUPPLY;
        // Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes)
        if(block.number.sub(startingIndexBlock) > 255) {
            startingIndex = uint(blockhash(block.number - 1)) % MAX_SUPPLY;
        }

        // To prevent original sequence
        if (startingIndex == 0) {
            startingIndex = startingIndex.add(1);
        }
    }

    function emergencySetStartingIndexBlock() public onlyOwner {
        require(startingIndex == 0, "Starting index is already set");
        startingIndexBlock = block.number;
    }

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

    // Sets base URI for all tokens, only able to be called by contract owner
    function setBaseURI(string memory baseURI_) external onlyOwner {
        _baseURIExtended = baseURI_;
    }

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

        string memory _tokenURI = _tokenURIs[tokenId];
        string memory base = _baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }
        // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
        return string(abi.encodePacked(base, tokenId.toString()));
    }
}
function setBaseURI(string memory baseURI_) external onlyOwner {
    _baseURIExtended = baseURI_;
}

// Sets base URI for all tokens, only able to be called by contract owner