// Contract: Token
// Target function: function mintByMinter( uint256 tokenId, address owner ) external override

 * fee is specified in basis points by default.
 *
 * IMPORTANT: ERC-2981 only specifies a way to signal royalty information and does not enforce its payment. See
 * https://eips.ethereum.org/EIPS/eip-2981#optional-royalty-payments[Rationale] in the EIP. Marketplaces are expected to
 * voluntarily pay royalties together with sales, but note that this standard is not yet widely supported.
 *
 * _Available since v4.5._
 */
abstract contract ERC2981 is IERC2981, ERC165 {
    struct RoyaltyInfo {
        address receiver;
        uint96 royaltyFraction;
    }

    RoyaltyInfo private _defaultRoyaltyInfo;
    mapping(uint256 => RoyaltyInfo) private _tokenRoyaltyInfo;

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
        return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @inheritdoc IERC2981
     */
    function royaltyInfo(uint256 _tokenId, uint256 _salePrice) public view virtual override returns (address, uint256) {
        RoyaltyInfo memory royalty = _tokenRoyaltyInfo[_tokenId];

        if (royalty.receiver == address(0)) {
            royalty = _defaultRoyaltyInfo;
        }

        uint256 royaltyAmount = (_salePrice * royalty.royaltyFraction) / _feeDenominator();

        return (royalty.receiver, royaltyAmount);
    }

    /**
     * @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
     * fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
     * override.
     */
    function _feeDenominator() internal pure virtual returns (uint96) {
        return 10000;
    }

    /**
     * @dev Sets the royalty information that all ids in this contract will default to.
     *
     * Requirements:
     *
     * - `receiver` cannot be the zero address.
     * - `feeNumerator` cannot be greater than the fee denominator.
     */
    function _setDefaultRoyalty(address receiver, uint96 feeNumerator) internal virtual {
        require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
        require(receiver != address(0), "ERC2981: invalid receiver");

        _defaultRoyaltyInfo = RoyaltyInfo(receiver, feeNumerator);
    }

    /**
     * @dev Removes default royalty information.
     */
    function _deleteDefaultRoyalty() internal virtual {
        delete _defaultRoyaltyInfo;
    }

    /**
     * @dev Sets the royalty information for a specific token id, overriding the global default.
     *
     * Requirements:
     *
     * - `receiver` cannot be the zero address.
     * - `feeNumerator` cannot be greater than the fee denominator.
     */
    function _setTokenRoyalty(
        uint256 tokenId,
        address receiver,
        uint96 feeNumerator
    ) internal virtual {
        require(feeNumerator <= _feeDenominator(), "ERC2981: royalty fee will exceed salePrice");
        require(receiver != address(0), "ERC2981: Invalid parameters");

        _tokenRoyaltyInfo[tokenId] = RoyaltyInfo(receiver, feeNumerator);
    }

    /**
     * @dev Resets royalty information for the token id back to the global default.
     */
    function _resetTokenRoyalty(uint256 tokenId) internal virtual {
        delete _tokenRoyaltyInfo[tokenId];
    }
}


//import {IOperatorFilterRegistry} from "./IOperatorFilterRegistry.sol";
interface IOperatorFilterRegistry {
    function isOperatorAllowed(address registrant, address operator) external view returns (bool);
    function register(address registrant) external;
    function registerAndSubscribe(address registrant, address subscription) external;
    function registerAndCopyEntries(address registrant, address registrantToCopy) external;
    function unregister(address addr) external;
    function updateOperator(address registrant, address operator, bool filtered) external;
    function updateOperators(address registrant, address[] calldata operators, bool filtered) external;
    function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external;
    function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external;
    function subscribe(address registrant, address registrantToSubscribe) external;
    function unsubscribe(address registrant, bool copyExistingEntries) external;
    function subscriptionOf(address addr) external returns (address registrant);
    function subscribers(address registrant) external returns (address[] memory);
    function subscriberAt(address registrant, uint256 index) external returns (address);
    function copyEntriesOf(address registrant, address registrantToCopy) external;
    function isOperatorFiltered(address registrant, address operator) external returns (bool);
    function isCodeHashOfFiltered(address registrant, address operatorWithCode) external returns (bool);
    function isCodeHashFiltered(address registrant, bytes32 codeHash) external returns (bool);
    function filteredOperators(address addr) external returns (address[] memory);
    function filteredCodeHashes(address addr) external returns (bytes32[] memory);
    function filteredOperatorAt(address registrant, uint256 index) external returns (address);
    function filteredCodeHashAt(address registrant, uint256 index) external returns (bytes32);
    function isRegistered(address addr) external returns (bool);
    function codeHashOf(address addr) external returns (bytes32);
}


//import {OperatorFilterer} from "./OperatorFilterer.sol";
/**
 * @title  OperatorFilterer
 * @notice Abstract contract whose constructor automatically registers and optionally subscribes to or copies another
 *         registrant's entries in the OperatorFilterRegistry.
 * @dev    This smart contract is meant to be inherited by token contracts so they can use the following:
 *         - `onlyAllowedOperator` modifier for `transferFrom` and `safeTransferFrom` methods.
 *         - `onlyAllowedOperatorApproval` modifier for `approve` and `setApprovalForAll` methods.
 */
abstract contract OperatorFilterer {
    error OperatorNotAllowed(address operator);

    IOperatorFilterRegistry public constant OPERATOR_FILTER_REGISTRY =
        IOperatorFilterRegistry(0x000000000000AAeB6D7670E522A718067333cd4E);

    constructor(address subscriptionOrRegistrantToCopy, bool subscribe) {
        // If an inheriting token contract is deployed to a network without the registry deployed, the modifier
        // will not revert, but the contract will need to be registered with the registry once it is deployed in
        // order for the modifier to filter addresses.
        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
            if (subscribe) {
                OPERATOR_FILTER_REGISTRY.registerAndSubscribe(address(this), subscriptionOrRegistrantToCopy);
            } else {
                if (subscriptionOrRegistrantToCopy != address(0)) {
                    OPERATOR_FILTER_REGISTRY.registerAndCopyEntries(address(this), subscriptionOrRegistrantToCopy);
                } else {
                    OPERATOR_FILTER_REGISTRY.register(address(this));
                }
            }
        }
    }

    modifier onlyAllowedOperator(address from) virtual {
        // Allow spending tokens from addresses with balance
        // Note that this still allows listings and marketplaces with escrow to transfer tokens if transferred
        // from an EOA.
        if (from != msg.sender) {
            _checkFilterOperator(msg.sender);
        }
        _;
    }

    modifier onlyAllowedOperatorApproval(address operator) virtual {
        _checkFilterOperator(operator);
        _;
    }

    function _checkFilterOperator(address operator) internal view virtual {
        // Check registry code length to facilitate testing in environments without a deployed registry.
        if (address(OPERATOR_FILTER_REGISTRY).code.length > 0) {
            if (!OPERATOR_FILTER_REGISTRY.isOperatorAllowed(address(this), operator)) {
                revert OperatorNotAllowed(operator);
            }
        }
    }
}


//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) {}
}


//import "./Common/IBlockMintable.sol";
//--------------------------------------------
// BlockMintable intterface
//--------------------------------------------
interface IBlockMintable {
    //----------------
    // read
    //----------------
    function getBlockTokenIdFrom( uint256 blockId ) external view returns (uint256);
    function getBlockTokenNum( uint256 blockId ) external view returns (uint256);
    function getBlockMintable( uint256 blockId ) external view returns (address);

    //----------------
    // write
    //----------------
    function mintByMinter( uint256 tokenId, address owner ) external;
}


//------------------------------------------------------------
// Token(ERC721)
//------------------------------------------------------------
contract Token is Ownable, ERC721, ERC2981, IBlockMintable, DefaultOperatorFilterer {
    //--------------------------------------------------------
    // 定数
    //--------------------------------------------------------
    string constant private TOKEN_NAME = "MRDC";
    string constant private TOKEN_SYMBOL = "MRDC";
    address constant private OWNER_ADDRESS = 0xE524Fd8B4C262767e8577acE6343b7540Ee9B243;
    uint96 constant private ROYALTY_FEE_NUMERATOR = 1000;    // 1000/10000 = 10%

    //--------------------------------------------------------
    // 管理
    //--------------------------------------------------------
    address private _manager;

    string[] private _arr_block_base_uri;
    uint256[] private _arr_block_token_id_from;
    uint256[] private _arr_block_token_num;
    address[] private _arr_block_mintable;

    //--------------------------------------------------------
    // [modifier] onlyOwnerOrManager
    //--------------------------------------------------------
    modifier onlyOwnerOrManager() {
        require( msg.sender == owner() || msg.sender == manager(), "caller is not the owner neither manager" );
        _;
    }

    //--------------------------------------------------------
    // コンストラクタ
    //--------------------------------------------------------
    constructor() Ownable() ERC721( TOKEN_NAME, TOKEN_SYMBOL ) {
        transferOwnership( OWNER_ADDRESS );

        _setDefaultRoyalty( OWNER_ADDRESS, ROYALTY_FEE_NUMERATOR );

        _manager = msg.sender;
    }

    //=======================================================================
    // [public/override] supportsInterface
    //=======================================================================
    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/onlyAllowedOperator&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 getBlockNum() external view returns (uint256) { return( _arr_block_base_uri.length ); }
    function getBlockBaseUri( uint256 blockId ) external view returns (string memory) { return( _arr_block_base_uri[blockId] ); }
    function getBlockTokenIdFrom( uint256 blockId ) external override view returns (uint256) { return( _arr_block_token_id_from[blockId] ); }
    function getBlockTokenNum( uint256 blockId ) external override view returns (uint256) { return( _arr_block_token_num[blockId] ); }
    function getBlockMintable( uint256 blockId ) external override view returns (address) { return( _arr_block_mintable[blockId] ); }

    //--------------------------------------------------------
    // [external/onlyOwnerOrManager] registBlock
    //--------------------------------------------------------
    function registBlock( uint256 blockId, string calldata baseUri, uint256 tokenIdFrom, uint256 tokenNum ) external onlyOwnerOrManager {
        require( _arr_block_base_uri.length == blockId, "invalid blockId" );

        // 追加
        _arr_block_base_uri.push( baseUri );
        _arr_block_token_id_from.push( tokenIdFrom );
        _arr_block_token_num.push( tokenNum );
        _arr_block_mintable.push( address(0x0) );
    }

    //--------------------------------------------------------
    // [external/onlyOwnerOrManager] unregistBlock
    //--------------------------------------------------------
    function unregistBlock( uint256 blockId ) external onlyOwnerOrManager {
        require( blockId < _arr_block_base_uri.length, "invalid blockId" );

        // 前詰め
        for( uint256 i=blockId; i<_arr_block_base_uri.length-1; i++ ){
            _arr_block_base_uri[i] = _arr_block_base_uri[i+1];
            _arr_block_token_id_from[i] = _arr_block_token_id_from[i+1];
            _arr_block_token_num[i] = _arr_block_token_num[i+1];
            _arr_block_mintable[i] = _arr_block_mintable[i+1];
        }

        // 削除
        _arr_block_base_uri.pop();
        _arr_block_token_id_from.pop();
        _arr_block_token_num.pop();
        _arr_block_mintable.pop();
    }

    //--------------------------------------------------------
    // [external/onlyOwnerOrManager] setBlockInfo
    //--------------------------------------------------------
    function setBlockInfo( uint256 blockId, string calldata baseUri, uint256 tokenIdFrom, uint256 tokenNum ) external onlyOwnerOrManager {
        require( blockId < _arr_block_base_uri.length, "invalid blockId" );

        _arr_block_base_uri[blockId] = baseUri;
        _arr_block_token_id_from[blockId] = tokenIdFrom;
        _arr_block_token_num[blockId] = tokenNum;
    }

    //--------------------------------------------------------
    // [external/onlyOwnerOrManager] setMintable
    //--------------------------------------------------------
    function setMintable( uint256 blockId, address mintable ) external onlyOwnerOrManager {
        require( blockId < _arr_block_base_uri.length, "invalid blockId" );

        _arr_block_mintable[blockId] = mintable;
    }

    //--------------------------------------------------------
    // [external] checkBlocks（問題のあるブロックIDを返す）
    //--------------------------------------------------------
    function checkBlocks() external view returns (uint256) {
        // 基本情報の確認
        for( uint256 blockId=0; blockId<_arr_block_base_uri.length; blockId++ ){
            // base_uriが無効なのはおかしい
            if( bytes(_arr_block_base_uri[blockId]).length <= 0 ){ return( blockId ); }

            // token_numが無効なのはおかしい
            if( _arr_block_token_num[blockId] <= 0 ){ return( blockId ); }

            // mintableが無効なのはおかしい
            if( _arr_block_mintable[blockId] == address(0x0) ){ return( blockId ); }
        }

        // tokenIdの領域の重複確認
        for( uint256 blockId=0; blockId<_arr_block_base_uri.length-1; blockId++ ){
            uint256 from0 = _arr_block_token_id_from[blockId];
            uint256 to0 = _arr_block_token_id_from[blockId] + _arr_block_token_num[blockId] - 1;
            for( uint256 i=blockId+1; i<_arr_block_base_uri.length; i++ ){
                uint256 from = _arr_block_token_id_from[i];
                uint256 to = _arr_block_token_id_from[i] + _arr_block_token_num[i] - 1;

                // 交差していないか？
                if( from >= from0 && from <= to0 ){ return( blockId ); }
                if( to >= from0 && to <= to0 ){ return( blockId ); }
                if( from0 >= from && from0 <= to ){ return( blockId ); }
                if( to0 >= from && to0 <= to ){ return( blockId ); }
            }
        }

        // ここまできたら問題のあるブロックはない
        return( _arr_block_base_uri.length );
    }