contract Digirocks is ERC721, Ownable {
    using SafeMath for uint256;
    uint public constant MAX_DIGIROCKS = 10000;
    uint public constant MAX_FREE_DIGIROCKS = 250;
    uint public redeemedDigirocks = 0;
    bool public hasSaleStarted = false;
    mapping(address => uint) public redemptions;

    string public METADATA_PROVENANCE_HASH = "";
    
    constructor(string memory baseURI) ERC721("Digirocks","DIGIROCKS")  {
        setBaseURI(baseURI);
        redemptions[0xBF3e23afd790E34A39d2E5B465f57e0ED74b3B9A] = 39;
        redemptions[0xb432cFCa03A7ddd34F9469092316a88D01bA80Fc] = 6;
        redemptions[0x139a0975eA36cEc4C59447002A875749Ac9c460f] = 6;
        redemptions[0xB8e9ba6Ef868352579f1ACc6945e96667000BF82] = 5;
        redemptions[0x9F5c28dA8F8622D36dC0F596142d66BB1512BAb5] = 5;
        redemptions[0xC57B739aA628F821C69A42279D278B41eFc6972E] = 5;
        redemptions[0x4B0292faCb3Bf7C26DEF0B6aeb24133C6B5bd68d] = 5;
        redemptions[0xbC1eB4359ab755Af079F6EF77E3FaAc465e53EDA] = 4;
        redemptions[0x8d809b30Ce1d511c674784c4bcFCd5D28F957F5C] = 4;
        redemptions[0x876A07C532c88f778790651503DfabE159F845a2] = 4;
        redemptions[0x5f1088110EdcBa27Fc206cdcc326B413b5867361] = 4;
        redemptions[0x525bFC44292b26d1c2f0303AA447Db46d7F23056] = 3;
        redemptions[0x75259686c1C967399eDA0b4B16f16FB0990f9615] = 3;
        redemptions[0x9baB7d4E2B2443667017A0FE4E18b662d10EdDa7] = 3;
        redemptions[0x89abF6673dE7e578e3c3CDeCC4CC18c0b9aDCc70] = 3;
        redemptions[0x8C8Ddf9A1390886525866F651aD0Ef1947d10897] = 3;
        redemptions[0x9B5546050984fe69aE9cFa57538C83282A3cBce2] = 2;
        redemptions[0x217d83bbe3693365b6BD40f4Dd2019b4aA7C681B] = 2;
        redemptions[0x593e76d2a379C72F3dC7698A0a87f6Adc92b7C5A] = 2;
        redemptions[0x0985224a811E65c235DBF4a2C2117712AABe4e39] = 2;
        redemptions[0x0614e07756390441CB246c886917487840e74f7d] = 2;
        redemptions[0x3546BD99767246C358ff1497f1580C8365b25AC8] = 2;
        redemptions[0xc9BD470a494E002aECBbBf73cA5BD8a2807F296b] = 2;
        redemptions[0x42F7c5275aC4372156027d939843C9C42523DF2E] = 2;
        redemptions[0x75e4d2CD3e6652226808D2750DEbeaF2023E5dE1] = 2;
        redemptions[0xd4cF19f76addb489d079D0f60F41D6E91E7c79E1] = 2;
        redemptions[0xbbA99dfb76452780d3a8da48D6aAd5f540131Da4] = 2;
        redemptions[0x229D6a31d0CF2225837DB8C82A6c78De5cDe114d] = 2;
        redemptions[0x5540B6425bdC1dF657a3d3934019eB89781F4897] = 2;
        redemptions[0x8a22E82B7f1916aC985593C481F3EfcdE00FDd78] = 2;
        redemptions[0x10A7aDb377e67B1e6080Cdd3dda59f42aebBeFf9] = 2;
        redemptions[0xeEcd39A1F6283bEF2771C004618bb3a982FC126C] = 2;
        redemptions[0xf476Cd75BE8Fdd197AE0b466A2ec2ae44Da41897] = 2;
        redemptions[0x5c5fc4680AAfa2CeD7b5826883c1a26DcE05eC4d] = 1;
        redemptions[0x0A2A3d3C6d4f2D57D307DA3E3e99FF8122077Da5] = 1;
        redemptions[0x1e33Fa287537F185f0cB89F8ef69bBf54fE64Ec2] = 1;
        redemptions[0x2160eD383B9f1C9cF1e01926209DAF5ecC8E1E49] = 1;
        redemptions[0xCdA1a0ECd7D25B49Ecbf0EeC1f45f0B7fb59961b] = 1;
        redemptions[0x33F04Cf7fa115165aF33CEA6846AA0dbC20c318B] = 1;
        redemptions[0xa9A1bB451Ef2bAcc8377BdF74cEA001278F48347] = 1;
        redemptions[0x4B4a367C6f5018529b3bbe0BeE27C7dae043331A] = 1;
        redemptions[0x9657B94D5d0a7C58CD2c59Ac17969e754C29eC29] = 1;
        redemptions[0x268087220BEF3a1aACC85ea32D140A87e56f494F] = 1;
        redemptions[0xd078df89564Bd99c74B3076a584Eb23068072909] = 1;
        redemptions[0x361084AF8D45E07885b31fCEDCb642b9Cc7B72D7] = 1;
        redemptions[0x43286B397932eb5C7C1Ea06632600A8E3a484c87] = 1;
        redemptions[0x5d54DBAc524Eed9FE4606F65fC0f069E704323a6] = 1;
        redemptions[0x827e692C4AB6e02099D0B4792D931075134876d4] = 1;
        redemptions[0x0A4E4f47b29B5891684c2202bc771aC3a8a35D7a] = 1;
        redemptions[0xB0B7aBccD78a560955eaD86a34eae2F0B6f0199E] = 1;
        redemptions[0xA5019C1D0ceefff54bC89F07dC53126861C76AbA] = 1;
        redemptions[0xf631B11ceC7a24a1300C301e3f079F5C76faaD1B] = 1;
        redemptions[0xe3ACe06FFa96f25FD652D068cD133cBb6B1869Eb] = 1;
        redemptions[0x784b0788F00b7085B2E66aBa316a271C3d10a347] = 1;
        redemptions[0x88bADBA19b9AeC1025125060Df9afFa6Fc0ECBe0] = 1;
        redemptions[0x8A0A570195Fb73a73b550902EEfb221A4E416739] = 1;
        redemptions[0xd7F4A99f72b19d609892694d638C18E2C93De9DC] = 1;
        redemptions[0x3Bd77B00f02C8BcfF586C565E2c5e6B6c5878EC3] = 1;
        redemptions[0xe336647d97414E5613c31b321306708bE29B6E0c] = 1;
        redemptions[0x992931cB309055B105327DEe9EB7afa702442D89] = 1;
        redemptions[0xE366f493c7192a268b8f12A8bEb6F604b29e6C11] = 1;
        redemptions[0xAa66a53e01698873D22d49C69c4829190975cc32] = 1;
        redemptions[0x561d171b3f3691b122B3767CAbd5e86bE76ffdb5] = 1;
        redemptions[0x0A46570dBA491858461E1164B436e9266b3f55b5] = 1;
        redemptions[0x7Dcb39fe010A205f16ee3249F04b24d74C4f44F1] = 1;
        redemptions[0xB4B1F87A6C2C95726ca3aaF9E3a06142D78E3Ac4] = 1;
        redemptions[0x98e9de3364f73824b06fc6531C42b85e67A4A98c] = 1;
        redemptions[0xc345b043023BDeB7b034064A06E36dA51d2FC3d8] = 1;
        redemptions[0xe5845761e7773976055F2559ca54be41F09d9E88] = 1;
        redemptions[0x209af07A4B6b2D15923ffa8C3d41cDAC49BC6c4F] = 1;
        redemptions[0x9A8b253C304b897AF8147143c5D0db4635E8c9C6] = 1;
        redemptions[0x6B254f8a91E255a4FB916501F896E15522799D82] = 1;
        redemptions[0x97f4319bfEFd1394fC33E0721cBe7388C57Aa810] = 1;
        redemptions[0x311747a35Da6c4d3259B8FBfFCe770Be105B7130] = 1;
        redemptions[0x8B73704918df1E5bcfD4d0ea2a04a7B534A06F80] = 1;
        redemptions[0xddEaEC88e4a183F5aCC7d7cFd6f69e300Bb6D455] = 1;
        redemptions[0x40d6D4b08BdEB4cf37f416d0EF2b4211671Ef484] = 1;
        redemptions[0xb432005e1010492fa315b9737881e5E18925204c] = 1;
        redemptions[0x95B46b33b8d7cc0A804D93f5FdDD7efBCB1fEDAC] = 1;
        redemptions[0x241Ef80D41C220B5616A4aB9bA9B9a9Fd7A79a5f] = 1;
        redemptions[0xafc69fD946f565183d9229d75e1953dF179aD959] = 1;
        redemptions[0x38319E74CeFe77B62FeC8E7a2aB81318727f46c7] = 1;
        redemptions[0x9BA74Ba6c4FD92646985AfA5d6A8EDB4F2A38Ba5] = 1;
        redemptions[0x934cBdF4c7163eFac4B8210713c5B7c134E207E6] = 1;
        redemptions[0x3a9F45ac308CcC0a1A48b0f9e2F8Ce859A0039ea] = 1;
        redemptions[0xa42A2271EcBaB3F638595F64c8d96623E334167E] = 1;
        redemptions[0x9Fe686D6fAcBC5AE4433308B26e7c810ac43F3D4] = 1;
        redemptions[0x15296589Df4156703d25475ea9b1039030acFaeb] = 1;
        redemptions[0x38F1DfdcaF2F0d70c29D4AF6a4AA9E920efe8B18] = 1;
        redemptions[0x3140fe113a49d78830E18b64F2398544EE39F8F9] = 1;
        redemptions[0x00AEa56c78ebB54dBf1C52520c20302aAD6B7355] = 1;
        redemptions[0xB810D1238dC5E5D664eBC5d19B0Fd2C28d42968D] = 1;
        redemptions[0x73dFEd4F1269d4Ae7fA951a65b923f887513E18d] = 1;
        redemptions[0x6b401B4B53219Fb4E5Ea71D4a98cd3c75095Ee77] = 1;
        redemptions[0x5B2D192182Cc6e39B047880995B67e179B89F63e] = 1;
        redemptions[0xce97592353ACE045DFD9882c50ceF606327FBE43] = 1;
    }
    
    function tokensOfOwner(address _owner) external view returns(uint256[] memory ) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 index;
            for (index = 0; index < tokenCount; index++) {
                result[index] = tokenOfOwnerByIndex(_owner, index);
            }
            return result;
        }
    }
    
    function calculatePrice() public view returns (uint256) {
        require(hasSaleStarted == true, "Sale hasn't started");
        require(totalSupply() < MAX_DIGIROCKS, "Sale has already ended");

        uint currentSupply = totalSupply();
        if (currentSupply >= 9900) {
            return 960000000000000000;        // 9900-10000: 0.96 ETH
        } else if (currentSupply >= 9500) {
            return 480000000000000000;         // 9500-9900:  0.48 ETH
        } else if (currentSupply >= 7500) {
            return 240000000000000000;         // 7500-9500:  0.24 ETH
        } else if (currentSupply >= 3500) {
            return 120000000000000000;         // 3500-7500:  0.12 ETH
        } else if (currentSupply >= 1500) {
            return 60000000000000000;          // 1500-3500:  0.06 ETH 
        } else if (currentSupply >= 500) {
            return 30000000000000000;          // 500-1500:   0.03 ETH 
        } else {
            return 15000000000000000;          // 0 - 500     0.015 ETH
        }
    }

    function calculatePriceForToken(uint _id) public view returns (uint256) {
        require(_id < MAX_DIGIROCKS, "Sale has already ended");

        if (_id >= 9900) {
            return 960000000000000000;        // 9900-10000: 0.96 ETH
        } else if (_id >= 9500) {
            return 480000000000000000;         // 9500-9900:  0.48 ETH
        } else if (_id >= 7500) {
            return 240000000000000000;         // 7500-9500:  0.24 ETH
        } else if (_id >= 3500) {
            return 120000000000000000;         // 3500-7500:  0.12 ETH
        } else if (_id >= 1500) {
            return 60000000000000000;          // 1500-3500:  0.06 ETH 
        } else if (_id >= 500) {
            return 30000000000000000;          // 500-1500:   0.03 ETH 
        } else {
            return 15000000000000000;          // 0 - 500     0.015 ETH
        }
    }
    
   function adoptDigirock(uint256 numDigirocks) public payable {
        require(numDigirocks > 0 && numDigirocks <= 20, "You can adopt minimum 1, maximum 20 Digirocks");
        require(totalSupply().add(numDigirocks) <= MAX_DIGIROCKS, "Exceeds MAX_DIGIROCKS");
        require(msg.value >= calculatePrice().mul(numDigirocks), "Ether value sent is below the price");

        for (uint i = 0; i < numDigirocks; i++) {
            uint mintIndex = totalSupply();
            _safeMint(msg.sender, mintIndex);
        }
    }
    
    // God Mode
    function setProvenanceHash(string memory _hash) public onlyOwner {
        METADATA_PROVENANCE_HASH = _hash;
    }
    
    function setBaseURI(string memory baseURI) public onlyOwner {
        _setBaseURI(baseURI);
    }
    
    function startSale() public onlyOwner {
        hasSaleStarted = true;
    }

    function pauseSale() public onlyOwner {
        hasSaleStarted = false;
    }
    
    function withdrawAll() public payable onlyOwner {
        require(payable(msg.sender).send(address(this).balance));
    }

    // Freak owners can claim a free rock
    function claimFreeDigirock(uint256 numDigirocks) public {
        require(redemptions[msg.sender] >= numDigirocks, "You can't claim that many");
        require(totalSupply().add(numDigirocks) <= MAX_DIGIROCKS, "Exceeds MAX_DIGIROCKS");
        require(redeemedDigirocks.add(numDigirocks) <= MAX_FREE_DIGIROCKS, "Exceeds MAX_FREE_DIGIROCKS");

        for (uint i = 0; i < numDigirocks; i++) {
            uint mintIndex = totalSupply();
            redemptions[msg.sender] = redemptions[msg.sender].sub(1);
            redeemedDigirocks = redeemedDigirocks.add(1);
            _safeMint(msg.sender, mintIndex);
        }
    }

    // Add the freakowner that can claim the rock
    function addClaimable(address freakowner, uint256 numDigirocks) public onlyOwner {
        require(totalSupply().add(numDigirocks) < MAX_DIGIROCKS, "Exceeds MAX_DIGIROCKS");
        require(redeemedDigirocks.add(numDigirocks) <= MAX_FREE_DIGIROCKS, "Exceeds MAX_FREE_DIGIROCKS");
        redemptions[freakowner] = numDigirocks;
    }
}
// Inspired by Chubbies (chubbies.io)
function claimFreeDigirock(uint256 numDigirocks) public {
    require(redemptions[msg.sender] >= numDigirocks, "You can't claim that many");
    require(totalSupply().add(numDigirocks) <= MAX_DIGIROCKS, "Exceeds MAX_DIGIROCKS");
    require(redeemedDigirocks.add(numDigirocks) <= MAX_FREE_DIGIROCKS, "Exceeds MAX_FREE_DIGIROCKS");

    for (uint i = 0; i < numDigirocks; i++) {
        uint mintIndex = totalSupply();
        redemptions[msg.sender] = redemptions[msg.sender].sub(1);
        redeemedDigirocks = redeemedDigirocks.add(1);
        _safeMint(msg.sender, mintIndex);
    }
}

// Freak owners can claim a free rock