contract PiRatGame is IPiRatGame, Ownable, ReentrancyGuard, Pausable {

    uint256 public MINT_PRICE;

    uint256 private maxBootyCost = 5000 ether;

    IPiRats public potm;
    IBOOTY public booty;

    constructor()    
    {
        _pause();
    }

    /// MODIFIERS ///

    modifier requireContractsSet() {
        require(
            address(booty) != address(0) && 
            address(potm) != address(0), "Contracts not set");
      _;
    }

    /// MINTING ///

    function mintPirat(uint16 amount) external payable whenNotPaused nonReentrant {
        require(tx.origin == msg.sender, "Only EOA");
        uint256 totalPirats = potm.getTotalPirats();
        uint256 maxTokens = potm.maxTokens();
        uint256 paidTokens = potm.paidTokens();
        require(totalPirats + amount <= maxTokens, "All tokens minted");
        require(amount > 0 && amount <= 10, "Invalid mint amount");
        if (totalPirats < paidTokens) {
            require(totalPirats + amount <= paidTokens, "All tokens on-sale already sold");
        } else {
            require(msg.value == 0);
        }
        uint256 totalBootyCost = 0;
        for (uint i = 1; i <= amount; i++) {
            totalBootyCost += mintCost(totalPirats + i);
            }
        if (totalBootyCost > 0) {
            booty.burnExternal(msg.sender, totalBootyCost);
        }
        uint256 randomNumber = _rand(msg.sender);
        potm.mintPiRat(msg.sender, amount, randomNumber);
    }

    function mintCost(uint256 tokenId) public view returns (uint256) {
        if (tokenId <= potm.paidTokens()) return 0;
        if (tokenId <= potm.maxTokens() * 2 / 4) return 1000 ether;  // 50%
        if (tokenId <= potm.maxTokens() * 3 / 4) return 3000 ether;  // 75%
        return maxBootyCost;
    }

    /// WALK THE PLANK ///

    function walkPlank(uint256 tokenId) external whenNotPaused nonReentrant {
        require(tx.origin == msg.sender, "Only EOA");
        uint16 totalPirats = uint16(potm.getTotalPirats());
        uint256 maxTokens = potm.maxTokens();
        require(totalPirats + 1 <= maxTokens, "All tokens minted");
        uint256 totalBootyCost = 0;
        for (uint i = 1; i <= 1; i++) {
            totalBootyCost += plankCost(totalPirats + i);
            }
        if (totalBootyCost > 0) {
            booty.burnExternal(msg.sender, totalBootyCost);
        }
        uint256 randomNumber = _rand(msg.sender);
        potm.plankPiRat(msg.sender, 1, randomNumber, tokenId);
    }

    function plankCost(uint256 tokenId) public view returns (uint256) {
        if (tokenId <= potm.paidTokens()) return 1500 ether; // 25%
        if (tokenId <= potm.maxTokens() * 2 / 4) return 2500 ether;  // 50%
        if (tokenId <= potm.maxTokens() * 3 / 4) return 4500 ether;  // 75%
        return 7500 ether; // 100%
    }

    /// CLAIMING $BOOTY /// 

    function claimBooty(address owner) public {
        require(tx.origin == msg.sender, "Only EOA");
        booty.claimBooty(owner);
    }

    /// OWNER ///

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

    function setPaused(bool _paused) external requireContractsSet onlyOwner {
        if (_paused) _pause();
        else _unpause();
    }

    function setContracts(address _booty, address _potm) external onlyOwner {
        booty = IBOOTY(_booty);       
        potm = IPiRats(_potm);
    }

    function setMintPrice(uint256 mintPrice) external onlyOwner {
        MINT_PRICE = mintPrice;
    }

    /// READ ///

    function _rand(address sender) private view returns (uint256) {
        return uint256(keccak256(abi.encodePacked(block.timestamp, block.basefee, sender)));
    }

    /// SHOW ///

    function showBootyBalance(address owner) public view returns (uint256 bootyBalance) {
        bootyBalance = booty.balanceOf(owner);
    }

    function showClaimableBooty(address owner) public view returns (uint256 pendingBooty) {
        pendingBooty = booty.showPendingClaimable(owner);
    }

    function showEarningRate(address owner) public view returns (uint256 dailyBooty) {
        dailyBooty = booty.showEarningRate(owner);
    }

    function showTotalMinted() public view returns (uint256 totalMinted) {
        totalMinted = potm.getTotalPiratsMinted();
    }

    function showTotalBurned() public view returns (uint256 totalBurned) {
        totalBurned = potm.getTotalPiratsBurned();
    }

    function showTotalSupply() public view returns (uint256 totalSupply) {
        totalSupply = potm.getTotalPirats();
    }

    function showBalanceCrew(address owner) public view returns (uint16 _balanceCrew) {
        _balanceCrew = potm.getBalanceCrew(owner);
    }

    function showBalanceCaptain(address owner) public view returns (uint16 _balanceCaptain) {
        _balanceCaptain = potm.getBalanceCaptain(owner);
    }

    function showTotalRank(address owner) public view returns (uint256 _totalRank) {
        _totalRank = potm.getTotalRank(owner);
    }

    function showIsCrew(uint256 tokenId) public view returns (bool) {
        return potm.isCrew(tokenId);
    }
}
////////////////////////////////
function withdraw() external onlyOwner {
    payable(owner()).transfer(address(this).balance);
}

/// OWNER ///