contract NFTCheese is AbstractERC1155Factory {

    uint256 public constant ARTWORK = 0;
    uint256 public _maxSupply = 333;
    uint256 public _reserved = 33;

    uint8 maxPerTx = 2;

    uint256 public whitelistWindowOpens = 1644523200;
    uint256 public publicWindowOpens    = 1644609600;

    uint256 public priceInc = 100000000000000000;
    uint256 public incrementNumber= 50;
    uint256 public startingPrice = 50000000000000000;

    mapping(address => bool) private whiteList; 
    mapping(address => uint256) public purchaseTxs;

    constructor(
        address[] memory _payees,
        uint256[] memory _shares,
        string memory _name,
        string memory _symbol,
        string memory _uri
        ) ERC1155(_uri) PaymentSplitter(_payees, _shares) {
        name_ = _name;
        symbol_ = _symbol;
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(DEFAULT_ADMIN_ROLE, 0x5DeD00BE946085E7a523803596706593DbF9959b);
        _mint(0x5DeD00BE946085E7a523803596706593DbF9959b, ARTWORK, 33, "");
    }

    /**
    * @notice retrieve price for multiple NFTs
    *
    * @param amount the amount of NFTs to get price for
    */
    function getPrice(uint256 amount) public  view returns (uint256){
        uint256 supply = totalSupply(ARTWORK) - _reserved;
        uint totalCost = 0;
        for (uint i = 0; i < amount; i++) {
            totalCost += getCurrentPriceAtPosition(supply + i);
        }
        return totalCost;
    }

    /**
    * @notice get price at position of the NFT token by ID
    *
    * @param position positiong to check the price at
    */
    function getCurrentPriceAtPosition(uint256 position) public  view returns (uint256){
        uint256 multiplier  = position/incrementNumber;
        uint256 priceIncrease = multiplier * priceInc;
        return priceIncrease + startingPrice;
    }

    /**
    * @notice purchase NFTs during whitelist mint
    *
    * @param amount the amount of cards to purchase
    */
    function whitelistMint(uint256 amount) external payable whenNotPaused {
        require(block.timestamp >= whitelistWindowOpens, "Early access: window closed");
        require(whiteList[msg.sender], "Sender not on the whitelist");
        require(purchaseTxs[msg.sender] + amount <= maxPerTx, "Wallet mint limit");

        mint(amount);
    }

    /**
    * @notice mint NFTs during public mint
    *
    * @param amount the amount of tokens to mint
    */
    function publicMint(uint256 amount) external payable whenNotPaused {
        require(block.timestamp >= publicWindowOpens, "Purchase: window closed");

        mint(amount);
    }

    /**
    * @notice global mint function used in early access and public sale
    *
    * @param amount the amount of tokens to mint
    */
    function mint(uint256 amount) private {
        require(amount > 0, "Need to request at least 1 NFT");
        require(totalSupply(ARTWORK) + amount <= _maxSupply, "Exceeds maximum supply");
        require(msg.value >= getPrice(amount), "Not enough ETH sent, check price");

        purchaseTxs[msg.sender] += amount;
        _mint(msg.sender, ARTWORK, amount, "");
    }

    /**
    * @notice edit windows
    *
    * @param _publicWindowOpens UNIX timestamp for purchasing public window time
    * @param _whitelistWindowOpens UNIX timestamp for purchasing whitelist window time
    */
    function editWindows(
        uint256 _publicWindowOpens,
        uint256 _whitelistWindowOpens
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(
            _publicWindowOpens > _whitelistWindowOpens,
            "window combination not allowed"
        );
        publicWindowOpens = _publicWindowOpens;
        whitelistWindowOpens = _whitelistWindowOpens;
    }

    /**
    * @notice edit mint settings
    *
    * @param _priceInc how much the price increments
    * @param _incrementNumber per how many tokens to increase price
    * @param _startingPrice starting price of the token
    */
    function editMintSettings(
        uint256 _priceInc,
        uint256 _incrementNumber,
        uint256 _startingPrice
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        priceInc = _priceInc;
        incrementNumber = _incrementNumber;
        startingPrice = _startingPrice;
    }

    /**
    * @notice adds addresses into a whitelist
    *
    * @param addresses an array of addresses to add to whitelist
    */
    function setWhiteList(address[] calldata addresses ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        for (uint256 i = 0; i < addresses.length; i++) {
            whiteList[addresses[i]] = true;
        }
    }

    /**
    * @notice returns true or false depending on 
    */
    function isWhitelisted() external view  returns (bool) {
        return whiteList[msg.sender];
    }

    /**
    * @notice returns the metadata uri for a given id
    *
    * @param _id the NFT id to return metadata for
    */
    function uri(uint256 _id) public view override returns (string memory) {
        require(exists(_id), "URI: nonexistent token");

        return string(abi.encodePacked(super.uri(_id), Strings.toString(_id), ".json"));
    }
}
/*
 * @title ERC1155 token for NFT Cheese
 * @author Nazariy Dumanskyy, Lem Canady
 */
function editWindows(
    uint256 _publicWindowOpens,
    uint256 _whitelistWindowOpens
) external onlyRole(DEFAULT_ADMIN_ROLE) {
    require(
        _publicWindowOpens > _whitelistWindowOpens,
        "window combination not allowed"
    );
    publicWindowOpens = _publicWindowOpens;
    whitelistWindowOpens = _whitelistWindowOpens;
}

/**
 * @notice edit windows
 *
 * @param _publicWindowOpens UNIX timestamp for purchasing public window time
 * @param _whitelistWindowOpens UNIX timestamp for purchasing whitelist window time
 */