contract BalloonCo is ERC721, Ownable {

    using SafeMath for uint256;

    string public BALLOONCO_PROVENANCE = ""; // 
    
    string public LICENSE_TEXT = "Copyright 2021 BALLOONCO";
    
    bool licenseLocked = false;

    uint256 public constant balloonCoPrice = 77000000000000000; // 0.077 ETH

    uint256 public discountNumerator = 100;
    uint256 public discountDenominator = 100;
    uint256 public testDiscount = 1;

    uint public constant maxBalloonCoPurchase = 25;

    uint256 public constant MAX_BALLOONCOS = 3333;

    bool public saleIsActive = false;
    bool public preSaleIsActive = false;
    
    mapping(uint => string) public balloonCoNames;
    
    // Reserve 125 Figurines for team - Giveaways/Prizes etc
    uint public balloonCoReserve = 125;
    
    event balloonCoNameChange(address _by, uint _tokenId, string _name);
    
    event licenseisLocked(string _licenseText);

    constructor() ERC721("BalloonCo", "BCO") { }
    
    function withdraw() public onlyOwner {
        uint balance = address(this).balance;
        msg.sender.transfer(balance);
    }
    
    function reserveBalloonCos(address _to, uint256 _reserveAmount) public onlyOwner {        
        uint supply = totalSupply();
        require(_reserveAmount > 0 && _reserveAmount <= balloonCoReserve, "Not enough reserve left for team");
        for (uint i = 0; i < _reserveAmount; i++) {
            _safeMint(_to, supply + i);
        }
        balloonCoReserve = balloonCoReserve.sub(_reserveAmount);
    }


    function setProvenanceHash(string memory provenanceHash) public onlyOwner {
        BALLOONCO_PROVENANCE = provenanceHash;
    }

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


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

    function flipPreSaleState() public onlyOwner {
        preSaleIsActive = !preSaleIsActive;
    }    
    
    
    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;
        }
    }
    
    // Returns the license for tokens
    function tokenLicense(uint _id) public view returns(string memory) {
        require(_id < totalSupply(), "CHOOSE A BALLOONCO FIGURINE WITHIN RANGE");
        return LICENSE_TEXT;
    }
    
    // Locks the license to prevent further changes 
    function lockLicense() public onlyOwner {
        licenseLocked =  true;
        emit licenseisLocked(LICENSE_TEXT);
    }
    
    // Change the license
    function changeLicense(string memory _license) public onlyOwner {
        require(licenseLocked == false, "License already locked");
        LICENSE_TEXT = _license;
    }
    
    
    function mintBalloonCo(uint numberOfTokens) public payable {
        if (numberOfTokens >=10 && numberOfTokens<20 ){
            discountNumerator = 90;
        }
        else if (numberOfTokens >=20){
            discountNumerator = 85;
        }
        else{
            discountNumerator = 100;
        }
        require(saleIsActive || preSaleIsActive, "Sale must be active to mint BalloonCo Figurines");
        require(numberOfTokens > 0 && numberOfTokens <= maxBalloonCoPurchase, "Can only mint 25 tokens at a time");
        require(totalSupply().add(numberOfTokens) <= MAX_BALLOONCOS, "Purchase would exceed max supply of BalloonCo. Figurines");
        require(msg.value >= balloonCoPrice.mul(numberOfTokens.mul(discountNumerator)).div(discountDenominator), "Ether value sent is not correct");
        testDiscount = balloonCoPrice.mul(numberOfTokens.mul(discountNumerator)).div(discountDenominator);
        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_BALLOONCOS) {
                _safeMint(msg.sender, mintIndex);
            }
        }

    }
    
     
    function changeBalloonCoName(uint _tokenId, string memory _name) public {
        require(ownerOf(_tokenId) == msg.sender, "Hey, your wallet doesn't own this BalloonCo Figurine!");
        require(sha256(bytes(_name)) != sha256(bytes(balloonCoNames[_tokenId])), "New name is same as the current one");
        balloonCoNames[_tokenId] = _name;
        
        emit balloonCoNameChange(msg.sender, _tokenId, _name);
        
    }
    
    function viewBalloonCoName(uint _tokenId) public view returns( string memory ){
        require( _tokenId < totalSupply(), "Choose a BalloonCo. Figurine within range" );
        return balloonCoNames[_tokenId];
    }
    
    
    function balloonCoNamesOfOwner(address _owner) external view returns(string[] memory ) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            return new string[](0);
        } else {
            string[] memory result = new string[](tokenCount);
            uint256 index;
            for (index = 0; index < tokenCount; index++) {
                result[index] = balloonCoNames[ tokenOfOwnerByIndex(_owner, index) ] ;
            }
            return result;
        }
    }
    
}
function tokenLicense(uint _id) public view returns(string memory) {
        require(_id < totalSupply(), "CHOOSE A BALLOONCO FIGURINE WITHIN RANGE");
        return LICENSE_TEXT;
    }
// Returns the license for tokens