contract WorldofGolem is ERC721A, Ownable {
    using Strings for uint256;

    // Constant variables
    // ------------------------------------------------------------------------
    uint256 public constant MAX_PRESALE_SUPPLY = 500; // Total amount of WorldofGolem available during presale
    uint256 public constant MAX_SUPPLY = MAX_PRESALE_SUPPLY + 9500; // Total amount of WorldofGolem

    uint256 public constant MAX_PER_WALLET_PRESALE = 20; // Max amount of WorldofGolem per wallet during whitelist period

    uint256 public constant MAX_PER_TX = 20; // Max amount of WorldofGolem per transaction
    uint256 public constant MAX_PER_WALLET_PUBLIC = 20; // Max amount of WorldofGolem per wallet during public sale

    uint256 public PRICE = 0.065 ether; // Price of a WorldofGolem
    uint256 public PRESALE_PRICE = 0.045 ether; // Presale Price of a WorldofGolem

    // Team addresses -
    // ------------------------------------------------------------------------
    address private constant _a1 = 0x2F6a40D83fa905A6e1b10D02eFba2b187F31d045;
    address private constant _a2 = 0x54697ba9bCe35deAb1dBcDb8E904Dd8ceA695C3D;
    address private constant _a3 = 0xf7f1ED914EdF6eAC82736b5d384De6EE19D6f429;
    address private constant _a4 = 0x415dFa37d8Df33a617676d44E32E8d95AA8F631D;

    
    // State variables
    // ------------------------------------------------------------------------
    bool public isPresaleActive = false;
    bool public isPublicSaleActive = false;


    // Presale mappings
    // ------------------------------------------------------------------------
    mapping(address => uint256) private _presaleClaimed;
    mapping(address => uint256) private _publicSaleClaimed;

    function countClaimedPresale(address addr) external view returns (uint256) {
        require(addr != address(0), "Null Address");
        return _presaleClaimed[addr];
    }

    function countClaimedPublicSale(address addr) external view returns (uint256) {
        require(addr != address(0), "Null Address");
        return _publicSaleClaimed[addr];
    }

    // URI variables
    // ------------------------------------------------------------------------
    string private _contractURI;
    string private _baseTokenURI;

    // Merkle Root Hash
    // ------------------------------------------------------------------------
    bytes32 private _merkleRoot;

    // Events
    // ------------------------------------------------------------------------
    event BaseTokenURIChanged(string baseTokenURI);
    event ContractURIChanged(string contractURI);

    // Modifiers
    // ------------------------------------------------------------------------
    modifier onlyPresale() {
        require(isPresaleActive, "Presale is not active");
        _;
    }

    modifier onlyPublicSale() {
        require(isPublicSaleActive, "Public sale is not active");
        _;
    }

    // Modifier to ensure that the call is coming from an externally owned account, not a contract
    modifier onlyEOA() {
        require(tx.origin == msg.sender, "Contract caller must be externally owned account");
        _;
    }

    modifier onlyValidQuantity(uint256 quantity) {
        require(quantity > 0, "Quantity cannot be zero"); // require that the transaction quantity > 0
        require(quantity <= MAX_PER_TX, "Quantity exceeds max quantity per transaction"); // require that the transaction quantity is less than the max per transaction
        _;
    }

    // Constructor
    // ------------------------------------------------------------------------
    constructor() ERC721A("WorldofGolem", "WOG", 20) {}

    // Presale functions
    // ------------------------------------------------------------------------

    // check if a merkle proof and the sender is eligible for presale
    function isEligibleForPresaleMerkle(bytes32[] calldata _merkleProof) external view returns (bool) {
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender)); // hash the address of the sender
        return MerkleProof.verify(_merkleProof, _merkleRoot, leaf); // verify that the sender's address is valid for the merkle proof   
    }

    function togglePresaleStatus() external onlyOwner {
        isPresaleActive = !isPresaleActive;
    }

    function togglePublicSaleStatus() external onlyOwner {
        isPublicSaleActive = !isPublicSaleActive;
    }

    // Mint functions
    // ------------------------------------------------------------------------

    // Presale Mint using merkle proof
    function presaleGolem(bytes32[] calldata _merkleProof, uint256 quantity) external payable onlyPresale onlyValidQuantity(quantity) {
        // require that the buyer is eligible for presale using the merkle proof 
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        require(MerkleProof.verify(_merkleProof, _merkleRoot, leaf), "Wallet is not eligible for presale");
        
        require(totalSupply() < MAX_PRESALE_SUPPLY, "Golem presale sold out");      
        require(totalSupply() + quantity <= MAX_PRESALE_SUPPLY, "Quantity exceeds maximum presale supply");

        require(_presaleClaimed[msg.sender] < MAX_PER_WALLET_PRESALE, "Wallet has already claimed presale limit");
        require(_presaleClaimed[msg.sender] + quantity <= MAX_PER_WALLET_PRESALE, "Quantity exceeds max quantity per wallet during presale"); // require that the buyer has not claimed their presale limit

        require(PRESALE_PRICE * quantity == msg.value, "Invalid ETH amount provided");

        _presaleClaimed[msg.sender] += quantity;
        _safeMint(msg.sender, quantity); // using ERC721A we can mint multiple tokens using _safeMint
    }

    // Public Sale mint
    function publicSaleGolem(uint256 quantity) external payable onlyPublicSale onlyValidQuantity(quantity) {
        
        require(totalSupply() < MAX_SUPPLY, "Golem sold out");
        require(totalSupply() + quantity <= MAX_SUPPLY, "Quantity exceeds maximum supply");

        require(_publicSaleClaimed[msg.sender] < MAX_PER_WALLET_PUBLIC, "Wallet has already claimed public sale limit");
        require(_publicSaleClaimed[msg.sender] + quantity <= MAX_PER_WALLET_PUBLIC, "Quantity exceeds max quantity per wallet during public sale");
        
        require(PRICE * quantity == msg.value, "Invalid ETH amount provided");

        _publicSaleClaimed[msg.sender] += quantity;
        _safeMint(msg.sender, quantity); // using ERC721A we can mint multiple tokens using _safeMint
    }


    //Owner mint
    function giveawayGolem(address to, uint256 quantity) external onlyOwner {
        require(totalSupply() + quantity <= MAX_SUPPLY, "WOG: Minting would exceed max supply");
        require(quantity > 0, "WOG: Must mint at least one token");
        _publicSaleClaimed[to] += quantity;
        _safeMint(to, quantity);
    }

    // Contract URI Functions
    // ------------------------------------------------------------------------

    // Set the contract URI - must set to a URL which can return a JSON of metadata for the contract
    function setContractURI(string calldata URI) external onlyOwner {
        _contractURI = URI;
        emit ContractURIChanged(URI);
    }

    // Return the contract URI
    function contractURI() public view returns (string memory) {
        return _contractURI;
    }

    // Base URI Functions
    // ------------------------------------------------------------------------
    
    // set the base token URI
    function setBaseTokenURI(string calldata URI) external onlyOwner {
        _baseTokenURI = URI;
        emit BaseTokenURIChanged(URI);
    }
    
    // override the _baseURI() method in the ERC721 contract 
    // the tokenURI() method below will call the tokenURI() method in the ERC721A contract, which will need the _baseURI set above
    function _baseURI() internal view override returns (string memory) {
        return _baseTokenURI;
    }

    function baseTokenURI() public view returns (string memory) {
        return _baseTokenURI;
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        return super.tokenURI(tokenId);
    }

    // allow the owner of the contract to set the Merkle Root
    function setMerkleRoot(bytes32 rootHash) external onlyOwner {
        _merkleRoot = rootHash;
    }
 
    // Withdrawal functions -- NEED TO UPDATE WITH INFORMATION FOR WITHDRAWALS
    // ------------------------------------------------------------------------
    
    function withdrawAll() external onlyOwner {
        uint _a1amount = address(this).balance * 45/100;
        uint _a2amount = address(this).balance * 45/100;
        uint _a3amount = address(this).balance * 5/100;
        uint _a4amount = address(this).balance * 5/100;
       
        require(payable(_a1).send(_a1amount), "Failed to send to a1");
        require(payable(_a2).send(_a2amount), "Failed to send to a2");
        require(payable(_a3).send(_a3amount), "Failed to send to a3");
        require(payable(_a4).send(_a4amount), "Failed to send to a3");
    }   
        
}
function setBaseTokenURI(string calldata URI) external onlyOwner {
        _baseTokenURI = URI;
        emit BaseTokenURIChanged(URI);
    }
// Base URI Functions
// ------------------------------------------------------------------------
// set the base token URI