// Contract: Token
// Target function: function getUserInfo( address target, uint256 amount, bytes32[] calldata merkleProof ) external view returns (uint256[USER_INFO_MAX] memory)
 }
}


//import "operator-filter-registry/src/DefaultOperatorFilterer.sol";
/**
 * @title  DefaultOperatorFilterer
 * @notice Inherits from OperatorFilterer and automatically subscribes to the default OpenSea subscription.
 */
abstract contract DefaultOperatorFilterer is OperatorFilterer {
    address constant DEFAULT_SUBSCRIPTION = address(0x3cc6CddA760b79bAfa08dF41ECFA224f810dCeB6);

    constructor() OperatorFilterer(DEFAULT_SUBSCRIPTION, true) {}
}


//-----------------------------------
// Token(ERC721)
//-----------------------------------
contract Token is Ownable, ERC721, ReentrancyGuard, ERC2981, DefaultOperatorFilterer {
    //--------------------------------------
    // constant
    //--------------------------------------
    string constant private TOKEN_NAME = "NISHIMOTO IS THE MOUTH -PROFITS TO THE PROPHET-";
    string constant private TOKEN_SYMBOL = "NITM";
    address constant private OWNER_ADDRESS = 0x40a9AE87b3486d38b1eAe58D8d8AF23169796a87;

    uint96 constant private ROYALTY_FEE_NUMERATOR = 1000;    // 1000/10000 = 10%
    uint256 constant private TOKEN_ID_OFS = 1;
    uint256 constant private BLOCK_SEC_MARGIN = 30;

    // enum
    uint256 constant private INFO_SALE_SUSPENDED = 0;
    uint256 constant private INFO_SALE_START = 1;
    uint256 constant private INFO_SALE_END = 2;
    uint256 constant private INFO_SALE_PRICE = 3;
    uint256 constant private INFO_SALE_WHITELISTED = 4;
    uint256 constant private INFO_SALE_USER_MINTED = 5;
    uint256 constant private INFO_SALE_USER_MINTABLE = 6;
    uint256 constant private INFO_MAX = 7;
    uint256 constant private USER_INFO_SALE_TYPE = INFO_MAX;
    uint256 constant private USER_INFO_TOTAL_SUPPLY = INFO_MAX + 1;
    uint256 constant private USER_INFO_TOKEN_MAX = INFO_MAX + 2;
    uint256 constant private USER_INFO_MINT_LIMIT = INFO_MAX + 3;
    uint256 constant private USER_INFO_MAX = INFO_MAX + 4;

    //--------------------------------------
    // storage
    //--------------------------------------
    address private _manager;

    string private _base_uri_for_hidden;
    string private _base_uri_for_revealed;
    uint256 private _token_max;
    uint256 private _token_reserved;
    uint256 private _token_mint_limit;
    uint256 private _total_supply;

    // PRIVATE(whitelist)
    bool private _PRIVATE_SALE_is_suspended;
    uint256 private _PRIVATE_SALE_start;
    uint256 private _PRIVATE_SALE_end;
    uint256 private _PRIVATE_SALE_price;
    bytes32 private _PRIVATE_SALE_merkle_root;
    mapping( address => uint256 ) private _PRIVATE_SALE_map_user_minted;

    // PUBLIC
    bool private _PUBLIC_SALE_is_suspended;
    uint256 private _PUBLIC_SALE_start;
    uint256 private _PUBLIC_SALE_end;
    uint256 private _PUBLIC_SALE_price;
    uint256 private _PUBLIC_SALE_mintable;
    mapping( address => uint256 ) private _PUBLIC_SALE_map_user_minted;

    //--------------------------------------------------------
    // [modifier] onlyOwnerOrManager
    //--------------------------------------------------------
    modifier onlyOwnerOrManager() {
        require( msg.sender == owner() || msg.sender == manager(), "caller is not the owner neither manager" );
        _;
    }

    //--------------------------------------------------------
    // constructor
    //--------------------------------------------------------
    constructor() Ownable() ERC721( TOKEN_NAME, TOKEN_SYMBOL ) {
        if( msg.sender != OWNER_ADDRESS ){
            transferOwnership( OWNER_ADDRESS );
        }

        _setDefaultRoyalty( OWNER_ADDRESS, ROYALTY_FEE_NUMERATOR );

        _manager = msg.sender;

        //-----------------------
        // mainnet
        //-----------------------
        _base_uri_for_hidden = "ipfs://Qmdc63a6qBr61jrJQy7HygJiU11wUpuGqSMd89X9j2Lo5L/";
        _token_max = 300;
        _token_mint_limit = 1;

        //***********************
        // PRIVATE(whitelist)
        //***********************
        _PRIVATE_SALE_start = 1674727200;               // 2023-01-26 19:00:00(JST)
        _PRIVATE_SALE_end   = 1674734340;               // 2023-01-26 20:59:00(JST)
        _PRIVATE_SALE_price = 50000000000000000;        // 0.05 ETH
        _PRIVATE_SALE_merkle_root = 0xaea8314bd76e90aedeae1680cede1f639911a4f220aa0608acc9a5f11e296f59;
        
        //~~~~~~~~~~~~~~~~~~~~~~~
        // PUBLIC
        //~~~~~~~~~~~~~~~~~~~~~~~
        _PUBLIC_SALE_start = 1674734400;                // 2023-01-26 21:00:00(JST)
        _PUBLIC_SALE_end   = 1674820740;                // 2023-01-27 20:59:00(JST)
        _PUBLIC_SALE_price = 50000000000000000;         // 0.05 ETH
        _PUBLIC_SALE_mintable = 0;                      // no limit
    }

    //=======================================================================
    // [public/override] 
    //=======================================================================
    function supportsInterface(bytes4 interfaceId) public view override( ERC721, ERC2981 ) returns (bool) {
        return super.supportsInterface(interfaceId);
    }

    //=======================================================================
    // [external/onlyOwnerOrManager] for ERC2981
    //=======================================================================
    function setDefaultRoyalty( address receiver, uint96 feeNumerator ) external onlyOwnerOrManager { _setDefaultRoyalty( receiver, feeNumerator ); }
    function deleteDefaultRoyalty() external onlyOwnerOrManager { _deleteDefaultRoyalty(); }
    function setTokenRoyalty( uint256 tokenId, address receiver, uint96 feeNumerator ) external onlyOwnerOrManager { _setTokenRoyalty( tokenId, receiver, feeNumerator ); }
    function resetTokenRoyalty( uint256 tokenId ) external onlyOwnerOrManager { _resetTokenRoyalty( tokenId ); }

    //=======================================================================
    // [public/override/onlyAllowedOperatorApproval] for OperatorFilter
    //=======================================================================
    function setApprovalForAll(address operator, bool approved) public override onlyAllowedOperatorApproval(operator) { super.setApprovalForAll(operator, approved); }
    function approve(address operator, uint256 tokenId) public override onlyAllowedOperatorApproval(operator) { super.approve(operator, tokenId); }
    function transferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) { super.transferFrom(from, to, tokenId); }
    function safeTransferFrom(address from, address to, uint256 tokenId) public override onlyAllowedOperator(from) { super.safeTransferFrom(from, to, tokenId); }
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public override onlyAllowedOperator(from) { super.safeTransferFrom(from, to, tokenId, data); }

    //--------------------------------------------------------
    // [public] manager
    //--------------------------------------------------------
    function manager() public view returns (address) {
        return( _manager );
    }

    //--------------------------------------------------------
    // [external/onlyOwner] setManager
    //--------------------------------------------------------
    function setManager( address target ) external onlyOwner {
        _manager = target;
    }

    //--------------------------------------------------------
    // [external] get
    //--------------------------------------------------------
    function baseUriForHidden() external view returns (string memory) { return( _base_uri_for_hidden ); }
    function baseUriForRevealed() external view returns (string memory) { return( _base_uri_for_revealed ); }
    function tokenMax() external view returns (uint256) { return( _token_max ); }
    function tokenReserved() external view returns (uint256) { return( _token_reserved ); }
    function tokenMintLimit() external view returns (uint256) { return( _token_mint_limit ); }    
    function totalSupply() external view returns (uint256) { return( _total_supply ); }

    //--------------------------------------------------------
    // [external/onlyOwnerOrManager] set
    //--------------------------------------------------------
    function setBaseUriForHidden( string calldata uri ) external onlyOwnerOrManager { _base_uri_for_hidden = uri; }
    function setBaseUriForRevealed( string calldata uri ) external onlyOwnerOrManager { _base_uri_for_revealed = uri; }
    function setTokenMax( uint256 max ) external onlyOwnerOrManager { _token_max = max; }
    function setTokenReserved( uint256 reserved ) external onlyOwnerOrManager { _token_reserved = reserved; }
    function setTokenMintLimit( uint256 limit ) external onlyOwnerOrManager { _token_mint_limit = limit; }

    //--------------------------------------------------------
    // [public/override] tokenURI
    //--------------------------------------------------------
    function tokenURI( uint256 tokenId ) public view override returns (string memory) {
        require( _exists( tokenId ), "nonexistent token" );

        if( bytes(_base_uri_for_revealed).length > 0 ){
            return( string( abi.encodePacked( _base_uri_for_revealed, Strings.toString( tokenId ) ) ) );            
        }

        return( string( abi.encodePacked( _base_uri_for_hidden, Strings.toString( tokenId ) ) ) );
    }

    //********************************************************
    // [public] getInfo - PRIVATE(whitelist)
    //********************************************************
    function PRIVATE_SALE_getInfo( address target, uint256 amount, bytes32[] calldata merkleProof ) public view returns (uint256[INFO_MAX] memory) {
        uint256[INFO_MAX] memory arrRet;

        if( _PRIVATE_SALE_is_suspended ){ arrRet[INFO_SALE_SUSPENDED] = 1; }
        arrRet[INFO_SALE_START] = _PRIVATE_SALE_start;
        arrRet[INFO_SALE_END] = _PRIVATE_SALE_end;
        arrRet[INFO_SALE_PRICE] = _PRIVATE_SALE_price;
        if( _checkWhitelisted( target, amount, merkleProof ) ){
            arrRet[INFO_SALE_WHITELISTED] = 1;
            arrRet[INFO_SALE_USER_MINTABLE] = amount;
        }
        arrRet[INFO_SALE_USER_MINTED] = _PRIVATE_SALE_map_user_minted[target];

        return( arrRet );
    }

    //********************************************************
    // [external/onlyOwnerOrManager] write - PRIVATE(whitelist)
    //********************************************************
    function PRIVATE_SALE_suspend( bool flag ) external onlyOwnerOrManager { _PRIVATE_SALE_is_suspended = flag; }
    function PRIVATE_SALE_setStartEnd( uint256 start, uint256 end ) external onlyOwnerOrManager { _PRIVATE_SALE_start = start; _PRIVATE_SALE_end = end; }
    function PRIVATE_SALE_setPrice( uint256 price ) external onlyOwnerOrManager { _PRIVATE_SALE_price = price; }
    function PRIVATE_SALE_setMerkleRoot( bytes32 root ) external onlyOwnerOrManager { _PRIVATE_SALE_merkle_root = root; }

    //********************************************************
    // [external/payable/nonReentrant] mint - PRIVATE(whitelist)
    //********************************************************
    function PRIVATE_SALE_mint( uint256 num, uint256 amount, bytes32[] calldata merkleProof ) external payable nonReentrant {
        uint256[INFO_MAX] memory arrInfo = PRIVATE_SALE_getInfo( msg.sender, amount, merkleProof );

        require( arrInfo[INFO_SALE_SUSPENDED] == 0, "PRIVATE SALE: suspended" );
        require( arrInfo[INFO_SALE_START] == 0 || arrInfo[INFO_SALE_START] <= (block.timestamp+BLOCK_SEC_MARGIN), "PRIVATE SALE: not opend" );
        require( arrInfo[INFO_SALE_END] == 0 || (arrInfo[INFO_SALE_END]+BLOCK_SEC_MARGIN) > block.timestamp, "PRIVATE SALE: finished" );
        require( arrInfo[INFO_SALE_WHITELISTED] == 1, "PRIVATE SALE: not whitelisted" );
        require( arrInfo[INFO_SALE_USER_MINTABLE] >= (arrInfo[INFO_SALE_USER_MINTED]+num), "PRIVATE SALE: reached the limit" );

        _checkPayment( msg.sender, arrInfo[INFO_SALE_PRICE]*num, msg.value );

        _mintTokens( msg.sender, num );
        _PRIVATE_SALE_map_user_minted[msg.sender] += num;
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // [public] getInfo - PUBLIC
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    function PUBLIC_SALE_getInfo( address target ) public view returns (uint256[INFO_MAX] memory) {
        uint256[INFO_MAX] memory arrRet;

        if( _PUBLIC_SALE_is_suspended ){ arrRet[INFO_SALE_SUSPENDED] = 1; }
        arrRet[INFO_SALE_START] = _PUBLIC_SALE_start;
        arrRet[INFO_SALE_END] = _PUBLIC_SALE_end;
        arrRet[INFO_SALE_PRICE] = _PUBLIC_SALE_price;
        arrRet[INFO_SALE_WHITELISTED] = 1;
        arrRet[INFO_SALE_USER_MINTED] = _PUBLIC_SALE_map_user_minted[target];
        arrRet[INFO_SALE_USER_MINTABLE] = _PUBLIC_SALE_mintable;

        return( arrRet );
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // [external/onlyOwnerOrManager] write - PUBLIC
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    function PUBLIC_SALE_suspend( bool flag ) external onlyOwnerOrManager { _PUBLIC_SALE_is_suspended = flag; }
    function PUBLIC_SALE_setStartEnd( uint256 start, uint256 end ) external onlyOwnerOrManager { _PUBLIC_SALE_start = start; _PUBLIC_SALE_end = end; }
    function PUBLIC_SALE_setPrice( uint256 price ) external onlyOwnerOrManager { _PUBLIC_SALE_price = price; }
    function PUBLIC_SALE_setMintable( uint256 mintable ) external onlyOwnerOrManager { _PUBLIC_SALE_mintable = mintable; }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // [external/payable/nonReentrant] mint - PUBLIC
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    function PUBLIC_SALE_mint( uint256 num ) external payable nonReentrant {
        uint256[INFO_MAX] memory arrInfo = PUBLIC_SALE_getInfo( msg.sender );

        require( arrInfo[INFO_SALE_SUSPENDED] == 0, "PUBLIC SALE: suspended" );
        require( arrInfo[INFO_SALE_START] == 0 || arrInfo[INFO_SALE_START] <= (block.timestamp+BLOCK_SEC_MARGIN), "PUBLIC SALE: not opend" );
        require( arrInfo[INFO_SALE_END] == 0 || (arrInfo[INFO_SALE_END]+BLOCK_SEC_MARGIN) > block.timestamp, "PUBLIC SALE: finished" );
        require( arrInfo[INFO_SALE_USER_MINTABLE] == 0 || arrInfo[INFO_SALE_USER_MINTABLE] >= (arrInfo[INFO_SALE_USER_MINTED]+num), "PUBLIC SALE: reached the limit" );

        _checkPayment( msg.sender, arrInfo[INFO_SALE_PRICE]*num, msg.value );

        _mintTokens( msg.sender, num );
        _PUBLIC_SALE_map_user_minted[msg.sender] += num;
    }

    //--------------------------------------------------------
    // [internal] _checkWhitelisted
    //--------------------------------------------------------
    function _checkWhitelisted( address target, uint256 amount, bytes32[] calldata merkleProof ) internal view returns (bool) {
        bytes32 node = keccak256( abi.encodePacked( target, amount ) );
        return( MerkleProof.verify( merkleProof, _PRIVATE_SALE_merkle_root, node ) );
    }

    //--------------------------------------------------------
    // [internal] _checkPayment
    //--------------------------------------------------------
    function _checkPayment( address msgSender, uint256 price, uint256 payment ) internal {
        require( price <= payment, "insufficient value" );

        // refund if overpaymented
        if( price < payment ){
            uint256 change = payment - price;
            address payable target = payable( msgSender );
            target.transfer( change );
        }
    }

    //--------------------------------------------------------
    // [internal] _mintTokens
    //--------------------------------------------------------
    function _mintTokens( address to, uint256 num ) internal {
        require( _total_supply >= _token_reserved, "reservation not finished" );
        require( (_total_supply+num) <= _token_max, "exceeded the supply range" );
        require( _token_mint_limit == 0 || _token_mint_limit >= num, "reached mint limitation" );

        for( uint256 i=0; i<num; i++ ){
            _mint( to, _total_supply+(TOKEN_ID_OFS+i) );
        }
        _total_supply += num;
    }

    //--------------------------------------------------------
    // [external/onlyOwnerOrManager] reserveTokens
    //--------------------------------------------------------
    function reserveTokens( uint256 num ) external onlyOwnerOrManager {
        require( (_total_supply+num) <= _token_reserved, "exceeded the reservation range" );

        for( uint256 i=0; i<num; i++ ){
            _mint( owner(), _total_supply+(TOKEN_ID_OFS+i) );
        }
        _total_supply += num;
    }