// Contract: Vendor
// Target function: function PRIVATE_getInfo( address target, uint256 amount, bytes32[] calldata merkleProof, uint256 amountMax, bytes32[] calldata merkleProofMax ) public view returns (uint256[INFO_MAX] memory)
 unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}


//import "./Common/IMintable.sol";
//--------------------------------------------
// Mintable intterface
//--------------------------------------------
interface IMintable {
    //----------------
    // write
    //----------------
    function mintByMinter( address to, uint256 tokenId ) external;
}


//------------------------------------------------------------
// Vendor
//------------------------------------------------------------
contract Vendor is Ownable, ReentrancyGuard {
    //--------------------------------------------------------
    // constants
    //--------------------------------------------------------
    address constant private OWNER_ADDRESS = 0xc78b8E9f12EDbc74A708F9B5A0472B33b3B286ce;
    address constant private TOKEN_ADDRESS = 0x0BdA9d7185A9885eCb1770d4793389bE5DA2e576;

    address constant private CREATOR_ADDRESS_0 = 0xc78b8E9f12EDbc74A708F9B5A0472B33b3B286ce;
    uint256 constant private CREATOR_FEE_WEIGHT_0 = 875;    // 87.5%

    address constant private CREATOR_ADDRESS_1 = 0xFe2875DcACD1D92Ca755C0a3DEF4a8debd970643;
    uint256 constant private CREATOR_FEE_WEIGHT_1 = 125;    // 12.5%

    uint256 constant private COLOR_NUM = 10;
    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_USER_MAX_IF_WHITELISTED = 4;
    uint256 constant private INFO_SALE_USER_MINTABLE = 5;
    uint256 constant private INFO_SALE_USER_MINTED = 6;
    uint256 constant private INFO_SALE_USER_LIMIT = 7;
    uint256 constant private INFO_MAX = 8;

    uint256 constant private USER_INFO_SALE_TYPE = INFO_MAX;
    uint256 constant private USER_INFO_USER_MAX = INFO_MAX + 1;
    uint256 constant private USER_INFO_USER_MINTED = INFO_MAX + 2;
    uint256 constant private USER_INFO_TOKEN_MAX = INFO_MAX + 3;
    uint256 constant private USER_INFO_TOTAL_MINTED = INFO_MAX + 4;
    uint256 constant private USER_INFO_TOKEN_MAX_PER_COLOR = INFO_MAX + 5;
    uint256 constant private USER_INFO_COLOR_MINTED = INFO_MAX + 6;
    uint256 constant private USER_INFO_MAX = USER_INFO_COLOR_MINTED + COLOR_NUM;

    //--------------------------------------------------------
    // storage
    //--------------------------------------------------------
    address private _manager;

    IMintable private _token;
    uint256 private _token_id_ofs;
    uint256 private _token_max;
    uint256 private _token_max_per_color;
    uint256 private _token_max_per_user;
    bytes32 private _token_max_per_user_merkle_root;
    uint256 private _token_reserved;
    uint256[COLOR_NUM] private _arr_token_reserved;

    uint256 private _total_minted;
    uint256[COLOR_NUM] private _arr_color_minted;

    //*** PRIVATE(whitelist) ***
    bool private _PRIVATE_is_suspended;
    uint256 private _PRIVATE_start;
    uint256 private _PRIVATE_end;
    uint256 private _PRIVATE_price;
    bytes32 private _PRIVATE_merkle_root;
    mapping( address => uint256 ) private _PRIVATE_map_user_minted;

    //~~~ PARTNER(whitelist) ~~~
    bool private _PARTNER_is_suspended;
    uint256 private _PARTNER_start;
    uint256 private _PARTNER_end;
    uint256 private _PARTNER_price;
    bytes32 private _PARTNER_merkle_root;
    mapping( address => uint256 ) private _PARTNER_map_user_minted;

    //=== PUBLIC ===
    bool private _PUBLIC_is_suspended;
    uint256 private _PUBLIC_start;
    uint256 private _PUBLIC_end;
    uint256 private _PUBLIC_price;
    mapping( address => uint256 ) private _PUBLIC_map_user_minted;

    //+++ CREATOR +++
    address[] private _arr_creator;
    uint256[] private _arr_creator_fee_weight;

    //--------------------------------------------------------
    // [modifier] onlyOwnerOrManager
    //--------------------------------------------------------
    modifier onlyOwnerOrManager() {
        require( msg.sender == owner() || msg.sender == manager(), "caller is not the owner neither manager" );
        _;
    }

    //--------------------------------------------------------
    // constructor
    //--------------------------------------------------------
    constructor() Ownable( OWNER_ADDRESS ) {
        _manager = msg.sender;

        _token = IMintable(TOKEN_ADDRESS);

        _arr_creator.push( CREATOR_ADDRESS_0 );
        _arr_creator_fee_weight.push( CREATOR_FEE_WEIGHT_0 );

        _arr_creator.push( CREATOR_ADDRESS_1 );
        _arr_creator_fee_weight.push( CREATOR_FEE_WEIGHT_1 );

        //-----------------------
        // setting
        //-----------------------
        _token_id_ofs = 1;
        _token_max = 5000;
        _token_max_per_color = 500;
        _token_max_per_user = 5;
        _token_max_per_user_merkle_root = 0x3e22812f090c3a2e3417d065aabd382839f0f3d8f9ee6895a0f588d20eeeedf8;
        for( uint256 i=0; i<COLOR_NUM; i++ ){
            _arr_token_reserved[i] = 41;
            _token_reserved += _arr_token_reserved[i];
        }

        //***********************
        // PRIVATE(whitelist)
        //***********************
        _PRIVATE_start = 1677042000;               // 2023-02-22 14:00:00(JST)
        _PRIVATE_end   = 1677214800;               // 2023-02-24 14:00:00(JST)
        _PRIVATE_price = 70000000000000000;        // 0.07 ETH
        _PRIVATE_merkle_root = 0xbca758ea3d259685add3babb2992029e0eceea0add5fa292b5485b98b5e5d528;
        
        //~~~~~~~~~~~~~~~~~~~~~~~
        // PARTNER(whitelist)
        //~~~~~~~~~~~~~~~~~~~~~~~
        _PARTNER_start = 1677214800;               // 2023-02-24 14:00:00(JST)
        _PARTNER_end   = 1677301200;               // 2023-02-25 14:00:00(JST)
        _PARTNER_price = 70000000000000000;        // 0.07 ETH
        _PARTNER_merkle_root = 0x5f10da6422d0a2ed2a29460dcea08d42790d53cf339e6c347f48e6d109da6ed1;

        //=======================
        // PUBLIC
        //=======================
        _PUBLIC_start = 1677301200;                 // 2023-02-25 14:00:00(JST)
        _PUBLIC_end   = 1677387600;                 // 2023-02-26 14:00:00(JST)
        _PUBLIC_price = 70000000000000000;          // 0.07 ETH
    }

    //--------------------------------------------------------
    // [public] manager
    //--------------------------------------------------------
    function manager() public view returns (address) {
        return( _manager );
    }

    //--------------------------------------------------------
    // [external/onlyOwner] setManager
    //--------------------------------------------------------
    function setManager( address target ) external onlyOwner {
        _manager = target;
    }

    //--------------------------------------------------------
    // [external] get
    //--------------------------------------------------------
    function token() external view returns (address) { return( address(_token) ); }
    function tokenIdOfs() external view returns (uint256) { return( _token_id_ofs ); }
    function tokenMax() external view returns (uint256) { return( _token_max ); }
    function tokenMaxPerColor() external view returns (uint256) { return( _token_max_per_color ); }
    function tokenMaxPerUser() external view returns (uint256) { return( _token_max_per_user ); }
    function tokenMaxPerUserMerkleRoot() external view returns (bytes32) { return( _token_max_per_user_merkle_root ); }
    function tokenReserved() external view returns (uint256) { return( _token_reserved ); }
    function tokenReservedAt( uint256 colorId ) external view returns (uint256) { return( _arr_token_reserved[colorId] ); }

    function totalMinted() external view returns (uint256) { return( _total_minted ); }
    function colorMintedAt( uint256 colorId ) external view returns (uint256) { return( _arr_color_minted[colorId] ); }
    function userMinted( address target ) external view returns (uint256) { return( _getUserMinted( target ) ); }

    //--------------------------------------------------------
    // [external/onlyOwnerOrManager] set
    //--------------------------------------------------------
    function setToken( address target ) external onlyOwnerOrManager { _token = IMintable(target); }
    function setTokenIdOfs( uint256 ofs ) external onlyOwnerOrManager { _token_id_ofs = ofs; }
    function setTokenMax( uint256 max ) external onlyOwnerOrManager { _token_max = max; }
    function setTokenMaxPerColor( uint256 max ) external onlyOwnerOrManager { _token_max_per_color = max; }
    function setTokenMaxPerUser( uint256 max ) external onlyOwnerOrManager { _token_max_per_user = max; }
    function setTokenMaxPerUserMerkleRoot( bytes32 root ) external onlyOwnerOrManager { _token_max_per_user_merkle_root = root; }
    function setTokenReserved( uint256[COLOR_NUM] calldata arrReserved ) external onlyOwnerOrManager {
        _token_reserved = 0;
        for( uint256 i=0; i<COLOR_NUM; i++ ){
            require( arrReserved[i] <= _token_max_per_color && arrReserved[i] >= _arr_color_minted[i], "invalid arrReserved" );
            _arr_token_reserved[i] = arrReserved[i];
            _token_reserved += _arr_token_reserved[i];
        }

        require( _token_reserved <= _token_max, "invalid arrReserved total" );
    }