// Contract: TheSpaceLab
// Target function: function approve(address to, uint256 tokenId) public override
's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0) return 0;
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b > 0, "SafeMath: modulo by zero");
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

contract TheSpaceLab is Context, Ownable, ERC165, IERC721, IERC721Metadata {
    OwnersMapping.itmap data; //
    using SafeMath for uint256;


    mapping(uint256 => uint256) private priceMap; //
    mapping(uint256 => uint256) private amountMap; //
    uint256 public immutable maxPerTx; //
    uint256 public  maxNumber; //
    string private _baseURIS;
    address private balanAddr;

    function baseURI() public view virtual returns (string memory) {
        return _baseURIS;
    }

    function _setBaseURI(string memory baseURI_) internal virtual {
        _baseURIS = baseURI_;
    }
    //data
    function insert(address key, uint typ, uint dis) internal returns (bool)
    {
        return OwnersMapping.insert(data, key, typ, dis);
    }

    function isLimit(address key) public view returns (bool)
    {
        return OwnersMapping.isLimit(data, key);
    }

    function isLimitAdd(address key, uint256 amount) public view returns (bool)
    {
        return OwnersMapping.isLimitAdd(data, key, amount);
    }

    function getType(address key) public view returns (uint)
    {
        return OwnersMapping.getType(data, key);
    }


    function getDis(address key) public view returns (uint)
    {
        return OwnersMapping.getDis(data, key);
    }

    function add(address key, uint256 tokenId) internal returns (bool)
    {
        return OwnersMapping.add(data, key, tokenId);
    }

    function del(address key, uint256 tokenId) internal returns (bool)
    {
        return OwnersMapping.del(data, key, tokenId);
    }

    function contains(address key) public view returns (bool)
    {
        return OwnersMapping.contains(data, key);
    }


    using Address for address;
    using Strings for uint256;

    // Compiler will pack this into a single 256bit word.
    struct TokenOwnership {
        // The address of the owner.
        address addr;
        // Keeps track of the start time of ownership with minimal overhead for tokenomics.
        uint64 startTimestamp;
        // Whether the token has been burned.
        bool burned;
    }

    // Compiler will pack this into a single 256bit word.
    struct AddressData {
        // Realistically, 2**64-1 is more than enough.
        uint64 balance;
        // Keeps track of mint count with minimal overhead for tokenomics.
        uint64 numberMinted;
        // Keeps track of burn count with minimal overhead for tokenomics.
        uint64 numberBurned;
        // For miscellaneous variable(s) pertaining to the address
        // (e.g. number of whitelist mint slots used).
        // If there are multiple variables, please pack them into a uint64.
        uint64 aux;
    }

    // The tokenId of the next token to be minted.
    uint256 internal _currentIndex;

    // The number of tokens burned.
    uint256 internal _burnCounter;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    bool public isSaleActive;

    //
    uint256 reduceTime;
    //
    uint256 pubSaledAmount;
    //
    uint256 priDropAmount;


    uint256 beginSaleTime;
    // Mapping from token ID to ownership details
    // An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.
    mapping(uint256 => TokenOwnership) internal _ownerships;

    // Mapping owner address to address data
    mapping(address => AddressData) private _addressData;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    constructor() {
        _name = "The Space Lab";
        _symbol = "The Space Lab";
        maxNumber = 3300;
        //
        maxPerTx = 10;
        //
        priceMap[1] = 38 * 10 ** 16;
        //
        priceMap[2] = 2 * 10 ** 17;
        //
        reduceTime = 3 * 24 * 60 * 60;
        balanAddr = 0x22050459E58a5F267d7c97C82542f932b6232b55;
        //
        //
        _currentIndex = _startTokenId();
    }

    function getnow() public view returns (uint256) {
        return block.timestamp;
    }

    function getSalePrice() public view returns (uint256) {
        if(!isSaleActive){
            return priceMap[1];
        }
        if (block.timestamp>= (beginSaleTime + reduceTime) || (priceMap[1] - (block.timestamp-beginSaleTime) / 3600 * 25 * 10 ** 14)<=priceMap[2]) {
            return priceMap[2];
        }
        //return priceMap[1] - (reduceTime - now) / 3600 * 4 * 10 ** 16;
        return priceMap[1] - (block.timestamp- beginSaleTime) / 3600 * 25 * 10 ** 14;
    }

    // return pubSaleAmoint priDropAmount price
    function getBaseMsg() public view returns(uint256,uint256,uint256){
        return (pubSaledAmount,priDropAmount,getSalePrice());
    }

    //
    function setPrice(uint256 rate) public onlyOwner {
        //require(msg.sender == owneraddr, "not owner");
        priceMap[1] = rate * 10 ** 16;
    }
    
    //
    function setNumber(uint256 maxnumber) public onlyOwner {
        // require(msg.sender == owneraddr, "not owner");
        maxNumber = maxnumber;
    }
    //pay  pay
    function mintPay(uint256 numberOfTokens) public payable {
        require(isSaleActive, "Sale is not active");
        require(pubSaledAmount+numberOfTokens<=3000,"already sold out");
        //
        require(numberOfTokens <= maxPerTx, "No more than 10 tokens per transaction");
        //
        require(totalSupply().add(numberOfTokens) <= maxNumber, "Purchase would exceed max supply of Martial");
        //
        uint256 price = getSalePrice();

        require(price.mul(numberOfTokens) <= msg.value, "Ether value is not correct");
        payable(balanAddr).transfer(msg.value);

        _safeMint(_msgSender(), numberOfTokens);
        pubSaledAmount += numberOfTokens;
    }

    //
    function mintOnly(address[] memory addList, uint256[] memory numberOfTokensList) public onlyOwner {
        for (uint i = 0; i < addList.length; i++) {//
            address newOwner = addList[i];
            //
            uint256 numberOfTokens = numberOfTokensList[i];
            //
            if (numberOfTokens > maxPerTx) {//
                continue;
            } else {
                if((priDropAmount+numberOfTokens)>=300){
                    return;
                }
                _safeMint(newOwner, numberOfTokens);
                priDropAmount+=numberOfTokens;
            }
        }
    }



    // eixt  true  nonexit  false
    function isExitA(uint256 tokenId) public view returns (bool) {
        if (_exists(tokenId)) {
            return true;
        } else {
            return false;
        }
    }


    // baseuri
    //
    function setBaseURI(string memory baseURI) public onlyOwner {
        // require(msg.sender == owneraddr, "not owner");
        _setBaseURI(baseURI);
    }

    //
    function flipSaleState() public onlyOwner {
        //  require(msg.sender == owneraddr, "not owner");
        isSaleActive = !isSaleActive;
    }

    //
    function resetBeginTime() public onlyOwner {
        beginSaleTime = block.timestamp;
    }


    /**
     * To change the starting tokenId, please override this function.
     */
    function _startTokenId() internal view virtual returns (uint256) {
        return 1;
    }

    /**
     * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
     */
    function totalSupply() public view returns (uint256) {
        // Counter underflow is impossible as _burnCounter cannot be incremented
        // more than _currentIndex - _startTokenId() times
    unchecked {
        return _currentIndex - _burnCounter - _startTokenId();
    }
    }

    /**
     * Returns the total amount of tokens minted in the contract.
     */
    function _totalMinted() internal view returns (uint256) {
        // Counter underflow is impossible as _currentIndex does not decrement,
        // and it is initialized to _startTokenId()
    unchecked {
        return _currentIndex - _startTokenId();
    }
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
        interfaceId == type(IERC721).interfaceId ||
        interfaceId == type(IERC721Metadata).interfaceId ||
        super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view override returns (uint256) {
        if (owner == address(0)) revert BalanceQueryForZeroAddress();
        return uint256(_addressData[owner].balance);
    }

    /**
     * Returns the number of tokens minted by `owner`.
     */
    function _numberMinted(address owner) internal view returns (uint256) {
        return uint256(_addressData[owner].numberMinted);
    }

    /**
     * Returns the number of tokens burned by or on behalf of `owner`.
     */
    function _numberBurned(address owner) internal view returns (uint256) {
        return uint256(_addressData[owner].numberBurned);
    }

    /**
     * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
     */
    function _getAux(address owner) internal view returns (uint64) {
        return _addressData[owner].aux;
    }

    /**
     * Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
     * If there are multiple variables, please pack them into a uint64.
     */
    function _setAux(address owner, uint64 aux) internal {
        _addressData[owner].aux = aux;
    }

    /**
     * Gas spent here starts off proportional to the maximum mint batch size.
     * It gradually moves to O(1) as tokens get transferred around in the collection over time.
     */
    function _ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) {
        uint256 curr = tokenId;

    unchecked {
        if (_startTokenId() <= curr && curr < _currentIndex) {
            TokenOwnership memory ownership = _ownerships[curr];
            if (!ownership.burned) {
                if (ownership.addr != address(0)) {
                    return ownership;
                }
                // Invariant:
                // There will always be an ownership that has an address and is not burned
                // before an ownership that does not have an address and is not burned.
                // Hence, curr will not underflow.
                while (true) {
                    curr--;
                    ownership = _ownerships[curr];
                    if (ownership.addr != address(0)) {
                        return ownership;
                    }
                }
            }
        }
    }
        revert OwnerQueryForNonexistentToken();
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view override returns (address) {
        return _ownershipOf(tokenId).addr;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        if (!_exists(tokenId)) revert URIQueryForNonexistentToken();

        string memory baseURI = _baseURI();
        return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, tokenId.toString(), ".json")) : '';
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overriden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return _baseURIS;
    }