// Contract: LootInfinitySquad
// Target function: function createSquad(uint256 squadTokenId, uint256 lootTokenId) public payable nonReentrant
 list of the given address
     */
    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
        uint256 length = ERC721.balanceOf(to);
        _ownedTokens[to][length] = tokenId;
        _ownedTokensIndex[tokenId] = length;
    }

    /**
     * @dev Private function to add a token to this extension's token tracking data structures.
     * @param tokenId uint256 ID of the token to be added to the tokens list
     */
    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
        _allTokensIndex[tokenId] = _allTokens.length;
        _allTokens.push(tokenId);
    }

    /**
     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
     * @param from address representing the previous owner of the given token ID
     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
     */
    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
        uint256 tokenIndex = _ownedTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary
        if (tokenIndex != lastTokenIndex) {
            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];

            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
        }

        // This also deletes the contents at the last position of the array
        delete _ownedTokensIndex[tokenId];
        delete _ownedTokens[from][lastTokenIndex];
    }

    /**
     * @dev Private function to remove a token from this extension's token tracking data structures.
     * This has O(1) time complexity, but alters the order of the _allTokens array.
     * @param tokenId uint256 ID of the token to be removed from the tokens list
     */
    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
        // then delete the last slot (swap and pop).

        uint256 lastTokenIndex = _allTokens.length - 1;
        uint256 tokenIndex = _allTokensIndex[tokenId];

        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
        uint256 lastTokenId = _allTokens[lastTokenIndex];

        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index

        // This also deletes the contents at the last position of the array
        delete _allTokensIndex[tokenId];
        _allTokens.pop();
    }
}

/// [MIT License]
/// @title Base64
/// @notice Provides a function for encoding some bytes in base64
/// @author Brecht Devos <brecht@loopring.org>
library Base64 {
    bytes internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /// @notice Encodes some bytes to the base64 representation
    function encode(bytes memory data) internal pure returns (string memory) {
        uint256 len = data.length;
        if (len == 0) return "";

        // multiply by 4/3 rounded up
        uint256 encodedLen = 4 * ((len + 2) / 3);

        // Add some extra buffer at the end
        bytes memory result = new bytes(encodedLen + 32);

        bytes memory table = TABLE;

        assembly {
            let tablePtr := add(table, 1)
            let resultPtr := add(result, 32)

            for {
                let i := 0
            } lt(i, len) {

            } {
                i := add(i, 3)
                let input := and(mload(add(data, i)), 0xffffff)

                let out := mload(add(tablePtr, and(shr(18, input), 0x3F)))
                out := shl(8, out)
                out := add(out, and(mload(add(tablePtr, and(shr(12, input), 0x3F))), 0xFF))
                out := shl(8, out)
                out := add(out, and(mload(add(tablePtr, and(shr(6, input), 0x3F))), 0xFF))
                out := shl(8, out)
                out := add(out, and(mload(add(tablePtr, and(input, 0x3F))), 0xFF))
                out := shl(224, out)

                mstore(resultPtr, out)

                resultPtr := add(resultPtr, 4)
            }

            switch mod(len, 3)
            case 1 {
                mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
            }
            case 2 {
                mstore(sub(resultPtr, 1), shl(248, 0x3d))
            }

            mstore(result, encodedLen)
        }

        return string(result);
    }
}

interface LootInterface {
    function getWeapon(uint256 tokenId) external view returns (string memory);
    function getChest(uint256 tokenId) external view returns (string memory);
    function getHead(uint256 tokenId) external view returns (string memory);
    function getWaist(uint256 tokenId) external view returns (string memory);
    function getFoot(uint256 tokenId) external view returns (string memory);
    function getHand(uint256 tokenId) external view returns (string memory);
    function getNeck(uint256 tokenId) external view returns (string memory);
    function getRing(uint256 tokenId) external view returns (string memory);
}

contract SquadComponents {
    LootInterface constant loot = LootInterface(0xFF9C1b15B16263C61d017ee9F65C50e4AE0113D7); //mainnet

    constructor() {}

    function random(string memory input) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(input)));
    }

    function computeLootIds(
        uint256 tokenId,
        uint256 squadStatus,
        uint256 coLootId,
        uint256 xoLootId,
        address owner
    ) internal pure returns (uint256, uint256) {
        if (squadStatus != 2 || tokenId > 4000) {
            coLootId = random(string(abi.encodePacked(abi.encodePacked(owner), toString(tokenId))));
            xoLootId = random(string(abi.encodePacked(abi.encodePacked(owner), toString(tokenId + 10000))));
        }

        return (coLootId, xoLootId);
    }

    function getWeapon(
        uint256 tokenId,
        uint256 squadStatus,
        uint256 coLootId,
        uint256 xoLootId,
        address owner
    ) public view returns (string memory) {
        (coLootId, xoLootId) = computeLootIds(tokenId, squadStatus, coLootId, xoLootId, owner);

        string memory output = string(abi.encodePacked(
            loot.getWeapon(coLootId),
            ", ",
            loot.getWeapon(xoLootId)
        ));

        return output;
    }

    function getChest(
        uint256 tokenId,
        uint256 squadStatus,
        uint256 coLootId,
        uint256 xoLootId,
        address owner
    ) public view returns (string memory) {
        (coLootId, xoLootId) = computeLootIds(tokenId, squadStatus, coLootId, xoLootId, owner);

        string memory output = string(abi.encodePacked(
            loot.getChest(coLootId),
            ", ",
            loot.getChest(xoLootId)
        ));

        return output;
    }

    function getHead(
        uint256 tokenId,
        uint256 squadStatus,
        uint256 coLootId,
        uint256 xoLootId,
        address owner
    ) public view returns (string memory) {
        (coLootId, xoLootId) = computeLootIds(tokenId, squadStatus, coLootId, xoLootId, owner);

        string memory output = string(abi.encodePacked(
            loot.getHead(coLootId),
            ", ",
            loot.getHead(xoLootId)
        ));

        return output;
    }

    function getWaist(
        uint256 tokenId,
        uint256 squadStatus,
        uint256 coLootId,
        uint256 xoLootId,
        address owner
    ) public view returns (string memory) {
        (coLootId, xoLootId) = computeLootIds(tokenId, squadStatus, coLootId, xoLootId, owner);

        string memory output = string(abi.encodePacked(
            loot.getWaist(coLootId),
            ", ",
            loot.getWaist(xoLootId)
        ));

        return output;
    }

    function getFoot(
        uint256 tokenId,
        uint256 squadStatus,
        uint256 coLootId,
        uint256 xoLootId,
        address owner
    ) public view returns (string memory) {
        (coLootId, xoLootId) = computeLootIds(tokenId, squadStatus, coLootId, xoLootId, owner);

        string memory output = string(abi.encodePacked(
            loot.getFoot(coLootId),
            ", ",
            loot.getFoot(xoLootId)
        ));

        return output;
    }

    function getHand(
        uint256 tokenId,
        uint256 squadStatus,
        uint256 coLootId,
        uint256 xoLootId,
        address owner
    ) public view returns (string memory) {
        (coLootId, xoLootId) = computeLootIds(tokenId, squadStatus, coLootId, xoLootId, owner);

        string memory output = string(abi.encodePacked(
            loot.getHand(coLootId),
            ", ",
            loot.getHand(xoLootId)
        ));

        return output;
    }

    function getNeck(
        uint256 tokenId,
        uint256 squadStatus,
        uint256 coLootId,
        uint256 xoLootId,
        address owner
    ) public view returns (string memory) {
        (coLootId, xoLootId) = computeLootIds(tokenId, squadStatus, coLootId, xoLootId, owner);

        string memory output = string(abi.encodePacked(
            loot.getNeck(coLootId),
            ", ",
            loot.getNeck(xoLootId)
        ));

        return output;
    }

    function getRing(
        uint256 tokenId,
        uint256 squadStatus,
        uint256 coLootId,
        uint256 xoLootId,
        address owner
    ) public view returns (string memory) {
        (coLootId, xoLootId) = computeLootIds(tokenId, squadStatus, coLootId, xoLootId, owner);

        string memory output = string(abi.encodePacked(
            loot.getRing(coLootId),
            ", ",
            loot.getRing(xoLootId)
        ));

        return output;
    }

    function tokenURI(
        uint256 tokenId,
        uint256 squadStatus,
        uint256 coLootId,
        uint256 xoLootId,
        address owner
    ) public view returns (string memory) {
        string[17] memory parts;
        parts[0] = '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.base { fill: white; font-family: serif; font-size: 14px; }</style><rect width="100%" height="100%" fill="black" /><text x="10" y="20" class="base">';

        parts[1] = getWeapon(tokenId, squadStatus, coLootId, xoLootId, owner);

        parts[2] = '</text><text x="10" y="40" class="base">';

        parts[3] = getChest(tokenId, squadStatus, coLootId, xoLootId, owner);

        parts[4] = '</text><text x="10" y="60" class="base">';

        parts[5] = getHead(tokenId, squadStatus, coLootId, xoLootId, owner);

        parts[6] = '</text><text x="10" y="80" class="base">';

        parts[7] = getWaist(tokenId, squadStatus, coLootId, xoLootId, owner);

        parts[8] = '</text><text x="10" y="100" class="base">';

        parts[9] = getFoot(tokenId, squadStatus, coLootId, xoLootId, owner);

        parts[10] = '</text><text x="10" y="120" class="base">';

        parts[11] = getHand(tokenId, squadStatus, coLootId, xoLootId, owner);

        parts[12] = '</text><text x="10" y="140" class="base">';

        parts[13] = getNeck(tokenId, squadStatus, coLootId, xoLootId, owner);

        parts[14] = '</text><text x="10" y="160" class="base">';

        parts[15] = getRing(tokenId, squadStatus, coLootId, xoLootId, owner);

        parts[16] = '</text></svg>';

        string memory output = string(abi.encodePacked(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parts[7], parts[8]));
        output = string(abi.encodePacked(output, parts[9], parts[10], parts[11], parts[12], parts[13], parts[14], parts[15], parts[16]));

        string memory json = Base64.encode(bytes(string(abi.encodePacked('{"name": "Bag #', toString(tokenId), '", "description": "Loot is randomized adventurer gear generated and stored on chain. Stats, images, and other functionality are intentionally omitted for others to interpret. Feel free to use Loot in any way you want.", "image": "data:image/svg+xml;base64,', Base64.encode(bytes(output)), '"}'))));
        output = string(abi.encodePacked('data:application/json;base64,', json));

        return output;
    }

    function toString(uint256 value) internal pure returns (string memory) {
    // Inspired by OraclizeAPI's implementation - MIT license
    // 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);
    }
}

contract LootInfinitySquad is ERC721Enumerable, ReentrancyGuard, Ownable {
    IERC721 constant loot = IERC721(0xFF9C1b15B16263C61d017ee9F65C50e4AE0113D7); //mainnet
    SquadComponents constant components = SquadComponents(0xc803Bf4Cae80ad4DCF7668a98ee2887cE638ffb9); //mainnet
    
    uint256 public floor = 3*(10**16); // 0.03 ETH
    uint256 private feeBalance = 0;

    struct Squad {
        uint256 status; //0=avaialbe, 1=created, 2=minted
        uint256 coLootTokenId;
        uint256 xoLootTokenId;
    }
    mapping(uint256 => Squad) public squads;
    mapping(uint256 => uint256) public lootTokenIdToSquadTokenId;

    mapping(uint256 => uint256) public incentives;
    mapping(uint256 => address) private creators;