contract TheBorderColliesPack is ERC721, Ownable{
    using SafeMath for uint256;

    uint256 public constant MAX_ASSETS = 10000;
    uint256 public constant ASSET_TO_AIRDROP = 50;
    uint256 public constant MAX_ASSETS_BULK_MINTING = 25;
    string public constant PROVENANCE_HASH = "1b4084b6ae84f37eeecff4ac837e79f69d105d9f1e030903f410f47d465cea23";

    uint256 public assetPrice = 60000000000000000; //0.06 ETH
    uint256 public airdropAssets = 0;
    bool public saleEnabled = false;

    constructor() ERC721("The Border Collies Pack", "BCOLLIE") {}

    /**
     * Owner-only functions
     */

    function airdrop(address _to, uint256 numberOfTokens) public onlyOwner {
        require(airdropAssets.add(numberOfTokens) <= ASSET_TO_AIRDROP, "No more asset to airdrop!");
        uint256 supply = totalSupply();
        uint256 i;
        for (i = 0; i < numberOfTokens; i=i.add(1)) {
            _safeMint(_to, supply.add(i));
        }
        airdropAssets = airdropAssets.add(numberOfTokens);
    }

    function donation(address payable _to, uint256 amount) public onlyOwner {
        (bool success, ) = _to.call{value: amount}("");
        require(success, "Transfer failed.");
    }

    function changePrice(uint256 _newPrice) public onlyOwner {
        assetPrice = _newPrice;
    }

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

    function saleStartStop() public onlyOwner {
        saleEnabled = !saleEnabled;
    }

    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        (bool success, ) = msg.sender.call{value: balance}("");
        require(success, "Transfer failed.");
    }

    /**
    * Public functions
    */

    function mintAsset(uint256 numberOfTokens) public payable {
        require(saleEnabled, "Sale must be active to mint.");
        require(numberOfTokens <= MAX_ASSETS_BULK_MINTING, "Maximum number of tokens for bulk minting exceeded.");
        require(totalSupply().add(numberOfTokens) <= MAX_ASSETS.sub(ASSET_TO_AIRDROP), "All available assets have been minted!");
        require(assetPrice.mul(numberOfTokens) <= msg.value, "Ether value sent is wrong.");

        uint256 supply = totalSupply();
        uint256 i;
        for (i = 0; i < numberOfTokens; i = i.add(1)) {
            if (totalSupply() < MAX_ASSETS) {
                _safeMint(msg.sender, supply.add(i));
            }
        }
    }

    function tokensOfOwner(address _owner) external view returns(uint256[] memory) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            for (uint256 index; index < tokenCount; index = index.add(1)) {
                result[index] = tokenOfOwnerByIndex(_owner, index);
            }
            return result;
        }
    }

}
function mintAsset(uint256 numberOfTokens) public payable {
        require(saleEnabled, "Sale must be active to mint.");
        require(numberOfTokens <= MAX_ASSETS_BULK_MINTING, "Maximum number of tokens for bulk minting exceeded.");
        require(totalSupply().add(numberOfTokens) <= MAX_ASSETS.sub(ASSET_TO_AIRDROP), "All available assets have been minted!");
        require(assetPrice.mul(numberOfTokens) <= msg.value, "Ether value sent is wrong.");

        uint256 supply = totalSupply();
        uint256 i;
        for (i = 0; i < numberOfTokens; i = i.add(1)) {
            if (totalSupply() < MAX_ASSETS) {
                _safeMint(msg.sender, supply.add(i));
            }
        }
    }
/**
 * Public functions
 */