contract Workwares is AbstractERC1155 {

    uint256 constant MAX_SUPPLY_WHITE = 323;
    uint256 constant MAX_SUPPLY_GOLD = 10;
    uint256 constant MAX_RESERVED = 5;
    uint256 public presalePrice = 0.1 ether;
    uint256 public mintPrice = 0.28 ether;
    uint256 public presaleActive = 0;
    uint256 public saleActive = 0;
    uint256 public numWhiteMints = 0;
    uint256 public numGoldMints = 0;

    // change to hosted folder
    string baseURI = "http://bayc1.mintworkwares.io/metadata/";

    mapping(address => uint256) public numMints;
    mapping(address => uint256) public whitelist;
    mapping(address => uint256) public numReserved;

    constructor() ERC1155(baseURI) {
        name_ = "Workwares";
        symbol_ = "BAYC1";
    }

    function setPrice(uint256 _price) external onlyOwner {
        require(_price > 0, "Price cannot be 0");
        mintPrice = _price;
    }

    function toggleSaleState(uint256 state) external onlyOwner {
        saleActive = state;
    }

    function togglePresaleState(uint256 state) external onlyOwner {
        presaleActive = state;
    }

    function reserveTokens(uint256 amount) external onlyOwner {
        require(numReserved[msg.sender] + amount <= MAX_RESERVED, "Would exceed max reserved tokens");
        numReserved[msg.sender] += amount;
        numWhiteMints += amount;
        _mint(msg.sender, 0, amount, "");
    }

    function withdraw() public payable onlyOwner {
        uint256 balance = address(this).balance;

        address acct1 = 0x011C2799d043D2D384Ac19aA3cF0567021Cd9Fa4;
        address studio = 0x388CcBf8c1A37F444DcFF6eDE0014DfA85BeDC1B;

        uint256 acct1_share = balance / 100 * 85;
        uint256 studio_share = balance / 100 * 15;

        // transfer eth to addresses
        payable(studio).transfer(studio_share);
        payable(acct1).transfer(acct1_share);
    }

    function addWhitelist(address[] calldata addresses) external onlyOwner {
        for (uint256 i = 0; i < addresses.length; i++) {
            whitelist[addresses[i]] = 1;
        }
    }

    // presale mint
    function presalePurchase() external payable {
        require(presaleActive == 1, "Presale closed");
        require(whitelist[msg.sender] == 1, "Address not on whitelist");
        require(msg.value == presalePrice, "Incorrect payment");

        uint256 nonce = 0;
        uint256 mintIndex = uint(keccak256(abi.encodePacked(block.timestamp, msg.sender, nonce))) % 30;
        whitelist[msg.sender] = 0;
        numMints[msg.sender]++;
        
        if (mintIndex == 0 && numGoldMints < MAX_SUPPLY_GOLD) {
            numGoldMints++;
            _mint(msg.sender, 1, 1, "");            
        } else {
            numWhiteMints++;
            _mint(msg.sender, 0, 1, "");            
        }
            
    }

    // public mint
    function purchase() external payable {
        require(saleActive == 1, "Sale currently closed");
        require(numMints[msg.sender] < 10 , "max tx amount exceeded");
        require(numWhiteMints < MAX_SUPPLY_WHITE || numGoldMints < MAX_SUPPLY_GOLD, "Transaction would exceed maximum supply");
        require(msg.value == mintPrice, "Incorrect payment");

        if (balanceOf(msg.sender, 1) >= 1 && numWhiteMints == MAX_SUPPLY_WHITE) {
            revert("Not enough tokens remaining to fulfill transaction");
        }

        uint256 nonce = 0;
        uint256 mintIndex = uint(keccak256(abi.encodePacked(block.timestamp, msg.sender, nonce))) % 30;

        numMints[msg.sender]++;

        if (mintIndex == 0 && balanceOf(msg.sender, 1) == 0 && numGoldMints < MAX_SUPPLY_GOLD) {
            // mint gold
            numGoldMints++;
            _mint(msg.sender, 1, 1, "");        
        } else if (mintIndex != 0 && numWhiteMints == MAX_SUPPLY_WHITE) {
            numGoldMints++;
            _mint(msg.sender, 1, 1, "");
        } else {
            // mint white
            numWhiteMints++;
            _mint(msg.sender, 0, 1, "");
        }
        
    }

    function redeemToken(uint256 id) external {
        require(balanceOf(msg.sender, id) > 0, "RedeemToken: must have a token to redeem");
        _burn(msg.sender, id, 1);
        _mint(msg.sender, id+2, 1, "");
    }

    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)));
    }
    
    // burn function
    function burn(
        address account,
        uint256 id,
        uint256 value
    ) public virtual override {
        require(account == _msgSender() || isApprovedForAll(account, _msgSender()),
            "ERC1155: caller is not owner nor approved"
        );

        _burn(account, id, value);
    }

}
function presalePurchase() external payable {
    require(presaleActive == 1, "Presale closed");
    require(whitelist[msg.sender] == 1, "Address not on whitelist");
    require(msg.value == presalePrice, "Incorrect payment");

    uint256 nonce = 0;
    uint256 mintIndex = uint(keccak256(abi.encodePacked(block.timestamp, msg.sender, nonce))) % 30;
    whitelist[msg.sender] = 0;
    numMints[msg.sender]++;
        
    if (mintIndex == 0 && numGoldMints < MAX_SUPPLY_GOLD) {
        numGoldMints++;
        _mint(msg.sender, 1, 1, "");            
    } else {
        numWhiteMints++;
        _mint(msg.sender, 0, 1, "");            
    }
            
}

// presale mint