// SPDX-License-Identifier: MIT

/*
  


*/

pragma solidity ^0.8.17;


import "erc721a/contracts/extensions/ERC721AQueryable.sol";
import "erc721a/contracts/extensions/ERC721ABurnable.sol";
import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/utils/cryptography/MerkleProof.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';


abstract contract LAPIXZERO {
    function mintMecha(address to, uint[] memory ids) public virtual;
}


contract LapixSukabenja is ERC721AQueryable, ERC721ABurnable, Ownable, ReentrancyGuard {

    event CrateClaimed(uint256 _totalClaimed, address _owner, uint256 _numOfCrates);
    event FuseMecha(address _address, uint[] _tokenIds);

    bytes32 public alphaMerkleRoot;
    bytes32 public laplistMerkleRoot;

    bool public publicMint = false;
    bool public whitelistMint = false;
    bool public fuseEnabled = false;

    string private _baseTokenURI;

    uint public partsPerCrate = 6;
    uint public maxPerAddress = 2;
    uint public maxCrates = 5555;
    uint public cratesMinted = 0;

    uint public alphaPrice = 0.0375 ether;
    uint public laplistPrice = 0.045 ether;
    uint public publicPrice = 0.05 ether;

    LAPIXZERO lapixContract;
    
    constructor(string memory _metadataBaseURL) ERC721A("LapixSukabenja", "LPS") {
        _baseTokenURI = _metadataBaseURL;
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }

    function verify(bytes32[] memory proof, bool alpha) internal view returns (bool) {
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        bool status;

        if (alpha)
            status = MerkleProof.verify(proof, alphaMerkleRoot, leaf);
        else
            status = MerkleProof.verify(proof, laplistMerkleRoot, leaf);

        return status; 
    }

    function _startTokenId() internal view virtual override returns (uint256) {
        return 1;
    }

    function mintCrate(uint256 quantity, bytes32[] memory proof, bool alpha) public payable {

        require(tx.origin == msg.sender, "Cannot mint from a contract");
        require(whitelistMint || publicMint, "Cannot claim now");
        require(_numberMinted(msg.sender) / partsPerCrate + quantity <= maxPerAddress, "Cannot claim these many crates");
        require(quantity > 0, "Invalid crate count");
        require(cratesMinted + quantity <= maxCrates, "Cannot claim these many tokens");
        
        if(whitelistMint){
            
            require(verify(proof, alpha), "Wallet not whitelisted");
            
            if (alpha) {
                require(msg.value >= (alphaPrice * quantity), "Insufficient funds to claim crates");
            }
            else {
                require(msg.value >= (laplistPrice * quantity), "Insufficient funds to claim crates");
            }
        }
        else {
            require(msg.value >= (publicPrice * quantity), "Insufficient funds to claim crates");
        }

        _safeMint(msg.sender, quantity * partsPerCrate);
        cratesMinted = cratesMinted + quantity;

        emit CrateClaimed(cratesMinted, msg.sender, quantity);
    }

    function fuse(uint[] memory tokenIds) public {

        require(fuseEnabled, "Cannot fuse parts");
        require(tokenIds.length == partsPerCrate, "Must have six parts to fuse");

        uint[] memory partCount = new uint[](partsPerCrate);
        
        for (uint i=0; i<tokenIds.length; i++) {

            require(msg.sender == ownerOf(tokenIds[i]), "Must be part owner to fuse");

            uint partType = tokenIds[i] % partsPerCrate;
            partCount[partType] = partCount[partType] + 1;
        }

        for (uint i=0; i<tokenIds.length; i++)
            require(partCount[i] == 1, "Cannot fuse with duplicate parts");
        
        lapixContract.mintMecha(msg.sender, tokenIds);

        for (uint i=0; i<tokenIds.length; i++)
            burn(tokenIds[i]);

        emit FuseMecha(msg.sender, tokenIds);
    }

    function mintParts(address[] memory _addresses) external onlyOwner {
        
        for (uint i=0; i<_addresses.length; i++) {
            address _to = address(_addresses[i]);
            _safeMint(_to, 1);
        }
    }

    function mintCratesToAddress(address _address, uint quantity) external onlyOwner {
        require(cratesMinted + quantity <= maxCrates, "Cannot mint these many crates");
        _safeMint(_address, quantity * partsPerCrate);
    }


    function setBaseURI(string calldata baseURI) external onlyOwner {
        _baseTokenURI = baseURI;
    }

    function setAlphaRoot(bytes32 _root) external onlyOwner {
        alphaMerkleRoot = _root;
    }

    function setLaplistRoot(bytes32 _root) external onlyOwner {
        laplistMerkleRoot = _root;
    }

    function flipPublicMintState() external onlyOwner {
        publicMint = !publicMint;
    }

    function flipWhitelistState() external onlyOwner {
        whitelistMint = !whitelistMint;
    }

    function flipFuseEnabled() external onlyOwner {
        fuseEnabled = !fuseEnabled;
    }

    function setMaxCrates(uint _supply) external onlyOwner {
        maxCrates = _supply;
    }

    function setMaxPerAddress(uint _max) external onlyOwner {
        maxPerAddress = _max;
    }

    function setPrices(uint _alpha, uint _laplist, uint _public) external onlyOwner {
        alphaPrice = _alpha;
        laplistPrice = _laplist;
        publicPrice = _public;
    }

    function setLapixContract(address _address) external onlyOwner {
        lapixContract = LAPIXZERO(_address);
    }


    function withdraw() external onlyOwner {
        payable(msg.sender).transfer(address(this).balance);
    }

}
[{"inputs":[{"internalType":"string","name":"_metadataBaseURL","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"InvalidQueryRange","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fromTokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toTokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"ConsecutiveTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_totalClaimed","type":"uint256"},{"indexed":false,"internalType":"address","name":"_owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"_numOfCrates","type":"uint256"}],"name":"CrateClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_address","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"}],"name":"FuseMecha","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"alphaMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"alphaPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cratesMinted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"explicitOwnershipOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"},{"internalType":"uint24","name":"extraData","type":"uint24"}],"internalType":"struct IERC721A.TokenOwnership","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"explicitOwnershipsOf","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint64","name":"startTimestamp","type":"uint64"},{"internalType":"bool","name":"burned","type":"bool"},{"internalType":"uint24","name":"extraData","type":"uint24"}],"internalType":"struct IERC721A.TokenOwnership[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flipFuseEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flipPublicMintState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"flipWhitelistState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"fuse","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fuseEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"laplistMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"laplistPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxCrates","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxPerAddress","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"quantity","type":"uint256"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"bool","name":"alpha","type":"bool"}],"name":"mintCrate","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"uint256","name":"quantity","type":"uint256"}],"name":"mintCratesToAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_addresses","type":"address[]"}],"name":"mintParts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"partsPerCrate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicMint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"publicPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_root","type":"bytes32"}],"name":"setAlphaRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setLapixContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_root","type":"bytes32"}],"name":"setLaplistRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_supply","type":"uint256"}],"name":"setMaxCrates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_max","type":"uint256"}],"name":"setMaxPerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_alpha","type":"uint256"},{"internalType":"uint256","name":"_laplist","type":"uint256"},{"internalType":"uint256","name":"_public","type":"uint256"}],"name":"setPrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"tokensOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"stop","type":"uint256"}],"name":"tokensOfOwnerIn","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"whitelistMint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]