// Contract: ERC721
// Target function: function recordFlippening() public
to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
      * @dev Safely transfers `tokenId` token from `from` to `to`.
      *
      * Requirements:
      *
      * - `from` cannot be the zero address.
      * - `to` cannot be the zero address.
      * - `tokenId` token must exist and be owned by `from`.
      * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
      * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
      *
      * Emits a {Transfer} event.
      */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}


/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant alphabet = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

}

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {

    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {

    using Strings for uint256;

    AggregatorV3Interface internal priceFeed;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping (uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping (address => uint256) private _balances;

    // Mapping tokenId to month
    mapping (uint256 => uint8) private _tokenMonths;

    // 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;

    // How many NFTs have been issued for each period;
    mapping (uint8 => uint16) public madePerPeriod;


    // unix time stamps in seconds for each month for the next 50 months
    // startTimes[49] means that you are predicting no flippening in the next 4 years
    // GMT Time
    uint32[] public startTimes = [1619827200, 1622505600, 1625097600, 1627776000, 1630454400, 1633046400,
    1635724800, 1638320400, 1640998800, 1643677200, 1646096400, 1648771200, 1651363200, 1654041600, 1656633600, 1659312000,
    1661990400, 1664582400, 1667260800, 1669856400, 1672534800, 1675213200, 1677632400, 1680307200, 1682899200, 1685577600,
    1688169600, 1690848000, 1693526400, 1696118400, 1698796800, 1701392400, 1704070800, 1706749200, 1709254800, 1711929600,
    1714521600, 1717200000, 1719792000, 1722470400, 1725148800, 1727740800, 1730419200, 1733014800, 1735693200, 1738371600,
    1740790800, 1743465600, 1746057600, 1748736000];


	// prices[x] represents the price of the xth token purchased that month in wei
    uint64[] public prices = [10000000000000000
    , 12500000000000000
    , 15000000000000000
    , 17500000000000000
    , 20000000000000000
    , 25000000000000000
    , 30000000000000000
    , 35000000000000000
    , 40000000000000000
    , 47500000000000000
    , 55000000000000000
    , 62500000000000000
    , 70000000000000000
    , 80000000000000000
    , 90000000000000000
    , 100000000000000000
    , 110000000000000000
    , 122500000000000000
    , 135000000000000000
    , 147500000000000000
    , 160000000000000000
    , 175000000000000000
    , 190000000000000000
    , 205000000000000000
    , 220000000000000000
    , 237500000000000000
    , 255000000000000000
    , 272500000000000000
    , 290000000000000000
    , 310000000000000000
    , 310000000000000000
    ];

   
    uint256 public numTokens = 0;

    // Which NFTs have already claimed their prize
    mapping (uint256 => bool) idsClaimed;

    // How Many NFTs have successfully claimed their prizes
    uint8 tokenClaimCount;

    address payable contractOwner;

    // The unix timestamp of when the flippening has occured
    // and the contract has been called, or 0.
    uint64 public flippeningTime = 0;

    // Amount [in wei] currently payable to winners
    uint256 public payoutBalance = 0;

    // Amount to be paid to devs and artists
    uint256 public artistBalance = 0;

	// Address of gitcoin's multisig wallet
	// gitcoin balance can only be sent to this address.
    address payable gitcoinMatchingMultiSig =  payable (0xde21F729137C5Af1b01d73aF1dC21eFfa2B8a0d6);

    // Amount [in wei] currently payable to gitcoin matching funds
    uint256 public gitcoinBalance = 0;
    
    // historical amount [in wei] already paid to gitcoin matching funds
    uint256 public gitcoinHistorical = 0;

    //price where ETH market cap flips Btc
    // This is how many wei can be bought for 1 btc
    int256 flippeningRatio = 6179713906000000000;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor () {
        _name = "FlippeningNFT";
        _symbol = "FLIP";

        // https://data.chain.link/btc-eth
		// Mainnet chainlink address
		// https://etherscan.io/address/0xdeb288F737066589598e9214E782fa5A8eD689e8
        priceFeed = AggregatorV3Interface(0xdeb288F737066589598e9214E782fa5A8eD689e8);
        contractOwner = payable(msg.sender);
    }


    function getLatestPrice() public view returns (int) {
        (
        uint80 roundID,
        int price,
        uint startedAt,
        uint timeStamp,
        uint80 answeredInRound
        ) = priceFeed.latestRoundData();
        return price;
    }

    /**
     * @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 virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    function monthOf(uint256 tokenId) public view returns (uint8) {
        return _tokenMonths[tokenId];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    /**
     * @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) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0
        ? string(abi.encodePacked(baseURI, tokenId.toString()))
        : '';
    }

    /**
     * @dev Base URI for computing {tokenURI}. Empty by default, can be overriden
     * in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "https://www.ethflippening.com/uri/";
    }

    function createNFT(uint8 period) public payable {

        // Calculate the price and make sure they have sent enough
        require(msg.value >= prices[madePerPeriod[period]], 'You need to send at least the current price');

        // Make sure that the maximum number of tokens have not already been created for that period.
        require(madePerPeriod[period] < 31, 'Max tokens for that period already minted');

        // Tokens can no longer be minted after the flippening has been recorded
        require(flippeningTime == 0, 'Tokens must be created before flippening is recorded');
        
        // Only 50 periods are available
        require(period < 50, 'Only 50 months are available');

        // Create the token
        _mint(msg.sender, numTokens);

        // Update token month
        _tokenMonths[numTokens] = period;

        // Increment
        numTokens ++;

        // Update number per period
        madePerPeriod[period] ++;

        // Handle money sent
        payoutBalance = payoutBalance + msg.value * 45 / 100;
        gitcoinBalance = gitcoinBalance + msg.value * 6 / 100;
        artistBalance = artistBalance + msg.value * 49 / 100;
    }