contract CavemenClub is ERC721Enumerable, PaymentSplitter, Ownable {

    using SafeMath for uint256;

    event TotalSupply(
    	uint256 supply
    );

	// Api interface url
	// Resource of NFT project.
	string _resource;

	// Token minting status
	// Token presale status
	bool public statusMint = true;
	bool public statusPresale = true;

	// Contract price;
	uint256 public tokenPrice;

	// Reference income
	uint8 public tokenIncome;

	// Reference discount
	uint8 public tokenDiscount;

	// Maximum token mint
	uint8 public tokenMaximum;

	// Maximum token supply
	uint16 public tokenSupply;

	// Total reserved token
	uint16 public tokenThreshold;

    // List of addresses that have referral income 
    mapping (address => uint256) public referralAddress;

    // List of addresses that have a number of reserved tokens for presale
    mapping (address => uint256) public presaleAddress;

	/**
	* Initializes the contract setting.
	* Resource: token base URL.
	* Team: team member address.
	* Share: team member shares.
	* Name: Name of NFT project.
	* Symbol: Symbol of NFT project.
	* Income: token referral income.
	* Discount: token referral discount.
	* Price: each token price.
	* Maximum: Maximum mint each transaction 
	* Threshold: Total threshold of tokens 
	* Supply: Total supply of tokens 
	*/
	constructor(string memory resource, address[] memory team, uint256[] memory share, string memory name, string memory symbol, uint8 income, uint8 discount, uint256 price, uint8 maximum, uint16 threshold, uint16 supply) 
		PaymentSplitter(team, share) 
		ERC721(name, symbol) 
	{
		_resource = resource;
	    tokenPrice = price;
	    tokenIncome = income;
	    tokenDiscount = discount;
		tokenMaximum = maximum;
		tokenThreshold = threshold;
		tokenSupply = supply;
	}

    /**
	* Overridden URI
	* Base URI for computing {tokenURI}.
	*/
    function _baseURI() internal view override returns (string memory) {
        return _resource;
    }

    /**
	* Set URI
	* Setting resource value for baseURI usage.
	*/
    function setResource(string memory resource) external onlyOwner {
        _resource = resource;
    }

	/**
	* Set Presale Address
	* Setting reserved presale spots.
	*/
    function setPresaleAddress(address[] memory owners, uint256[] memory amounts) external onlyOwner {
        for(uint256 i; i < owners.length; i++){
            presaleAddress[owners[i]] = amounts[i];
        }
    }

    /**
	* Set mint status
	* Setting token mint status for sales
	*/
    function setMint(bool status) external onlyOwner {
        statusMint = status;
    }

    /**
	* Set presale status
	* Setting token presale status for sales
	*/
    function setPresale(bool status) external onlyOwner {
        statusPresale = status;
    }

    /**
    * Get Price
    * Price calculate by referral
    */
    function getPrice(address payable referral) internal view returns (uint256) {
        if(referral != address(0) && referral != msg.sender){
			return tokenPrice.sub(tokenPrice.mul(tokenDiscount).div(100));
        } else {
        	return tokenPrice;
        }
    }

    /**
    * Token Reserve
    * Mint token without any requirement
    */
    function tokenReserve(uint8 number, address owner) public onlyOwner {
        for (uint8 i = 0; i < number; i++) {
			uint256 identity = totalSupply();
            _safeMint(owner, identity);
        }
    }
    
    /**
    * Token Mint
	* If mint status is true, user has enough ether, number below limit, total supply below limit;
	* Mint token, send share to referral
	* Emit minting supply to browser/graph.
    */
    function tokenMint(uint8 number, address payable referral) external payable {
    	require(statusMint == true || statusPresale == true, "token.presale");
        require(statusMint == true || statusPresale == true && presaleAddress[msg.sender] >= number, "token.address");
    	require(statusMint == true || statusPresale == true, "token.mint");
        require(getPrice(referral).mul(number) <= msg.value, "token.price");

        require(tokenMaximum >= number, "token.maximum");
        require(tokenSupply >= totalSupply().add(tokenThreshold).add(number), "token.supply");

		for (uint8 i = 0; i < number; i++) {
			uint256 identity = totalSupply();
            _safeMint(msg.sender, identity);
        }

        if(referral != address(0) && referral != msg.sender){
        	referral.transfer(tokenPrice.mul(number).mul(tokenIncome).div(100));
        	referralAddress[referral] = referralAddress[referral].add(tokenPrice.mul(number).mul(tokenIncome).div(100));
        }

        if(statusMint == false && statusPresale == true){
        	presaleAddress[msg.sender] = presaleAddress[msg.sender].sub(number);
    	}

    	emit TotalSupply({ supply: totalSupply() });
    }
   
}
function tokenReserve(uint8 number, address owner) public onlyOwner {
    for (uint8 i = 0; i < number; i++) {
int256 identity = totalSupply();
        _safeMint(owner, identity);
    }
}

/**
 * Token Reserve
 * Mint token without any requirement
 */