package com.cube.examples;

import com.cube.index.*;
import com.cube.storage.LSMStorageEngine;

import java.util.*;

/**
 * Runnable examples demonstrating Cubic Index System
 */
public class CubicIndexExamples {
    
    public static void main(String[] args) throws Exception {
        System.out.println("=== Cubic Index System Examples ===\n");
        
        example1_CubicNumbers();
        example2_BasicIndexing();
        example3_SideDistribution();
        example4_MultiLevelIndex();
        example5_PrefixAndRangeSearch();
        example6_IntegratedStorage();
        
        System.out.println("\n=== All cubic index examples completed! ===");
    }
    
    /**
     * Example 1: Understanding Cubic Numbers
     */
    public static void example1_CubicNumbers() {
        System.out.println("Example 1: Cubic Numbers (N³×6)");
        System.out.println("----------------------------------");
        
        System.out.println("Cubic Index Progression:");
        for (int n = 1; n <= 10; n++) {
            long index = CubicIndexNode.calculateCubicIndex(n);
            System.out.printf("  Level %d: %d³×6 = %d%n", n, n, index);
        }
        
        System.out.println("\nNotice the exponential growth:");
        System.out.println("  Level 1→2: 6→48 (8x increase)");
        System.out.println("  Level 2→3: 48→162 (3.4x increase)");
        System.out.println("  Level 5→6: 750→1296 (1.7x increase)");
        
        System.out.println();
    }
    
    /**
     * Example 2: Basic Indexing Operations
     */
    public static void example2_BasicIndexing() {
        System.out.println("Example 2: Basic Cubic Indexing");
        System.out.println("--------------------------------");
        
        // Create a cubic index node at level 3
        CubicIndexNode node = new CubicIndexNode(3);
        
        System.out.println("Created node at Level 3:");
        System.out.println("  Index Value: " + node.getIndexValue());
        System.out.println("  Formula: 3³×6 = " + (3*3*3*6));
        
        // Add data
        System.out.println("\nAdding data...");
        node.put("user:alice", "Alice Johnson".getBytes());
        node.put("user:bob", "Bob Smith".getBytes());
        node.put("product:laptop", "Dell XPS 13".getBytes());
        
        System.out.println("✓ Added 3 items");
        System.out.println("  Total keys: " + node.getTotalSize());
        
        // Retrieve data
        System.out.println("\nRetrieving data...");
        String alice = new String(node.get("user:alice"));
        System.out.println("  user:alice → " + alice);
        
        // Show statistics
        Map<String, Object> stats = node.getStats();
        System.out.println("\nNode Statistics:");
        System.out.println("  " + stats);
        
        System.out.println();
    }
    
    /**
     * Example 3: 6-Sided Distribution
     */
    public static void example3_SideDistribution() {
        System.out.println("Example 3: 6-Sided Data Distribution");
        System.out.println("-------------------------------------");
        
        CubicIndexNode node = new CubicIndexNode(2);
        
        System.out.println("The 6 sides of a cubic node:");
        for (CubicIndexNode.Side side : CubicIndexNode.Side.values()) {
            System.out.println("  " + side.name() + " (index: " + side.getIndex() + ")");
        }
        
        // Add data and see distribution
        System.out.println("\nAdding 60 keys...");
        for (int i = 0; i < 60; i++) {
            node.put("key-" + i, ("value-" + i).getBytes());
        }
        
        System.out.println("\nDistribution across sides:");
        for (CubicIndexNode.Side side : CubicIndexNode.Side.values()) {
            int count = node.getSide(side).size();
            System.out.printf("  %-6s: %2d keys ", side.name(), count);
            System.out.println("█".repeat(count / 2));
        }
        
        System.out.println("\nTotal: " + node.getTotalSize() + " keys");
        System.out.println("Average per side: " + (node.getTotalSize() / 6.0));
        
        System.out.println();
    }
    
    /**
     * Example 4: Multi-Level Index
     */
    public static void example4_MultiLevelIndex() {
        System.out.println("Example 4: Multi-Level Cubic Index");
        System.out.println("-----------------------------------");
        
        CubicIndexTree tree = new CubicIndexTree(5, 20, true);
        
        System.out.println("Created tree with 5 levels:");
        System.out.println("  Level 1: Capacity = 6");
        System.out.println("  Level 2: Capacity = 48");
        System.out.println("  Level 3: Capacity = 162");
        System.out.println("  Level 4: Capacity = 384");
        System.out.println("  Level 5: Capacity = 750");
        
        // Add data
        System.out.println("\nAdding data across levels...");
        String[] datasets = {
            "users", "products", "orders", "sessions", "events"
        };
        
        for (String dataset : datasets) {
            for (int i = 0; i < 20; i++) {
                String key = dataset + ":" + i;
                tree.put(key, ("data-" + i).getBytes());
            }
        }
        
        System.out.println("✓ Added " + tree.getTotalSize() + " keys");
        
        // Show distribution
        System.out.println("\nDistribution across levels:");
        for (int level = 1; level <= tree.getLevelCount(); level++) {
            CubicIndexNode node = tree.getLevel(level);
            if (node != null) {
                System.out.printf("  Level %d: %3d keys%n", level, node.getTotalSize());
            }
        }
        
        System.out.println();
    }
    
    /**
     * Example 5: Prefix and Range Search
     */
    public static void example5_PrefixAndRangeSearch() {
        System.out.println("Example 5: Advanced Search Operations");
        System.out.println("--------------------------------------");
        
        CubicIndexTree tree = new CubicIndexTree();
        
        // Add hierarchical data
        System.out.println("Adding hierarchical data...");
        tree.put("user:1:name", "Alice".getBytes());
        tree.put("user:1:email", "alice@example.com".getBytes());
        tree.put("user:1:age", "30".getBytes());
        tree.put("user:2:name", "Bob".getBytes());
        tree.put("user:2:email", "bob@example.com".getBytes());
        tree.put("product:laptop:1", "Dell XPS".getBytes());
        tree.put("product:laptop:2", "MacBook Pro".getBytes());
        tree.put("product:mouse:1", "Logitech MX".getBytes());
        
        // Prefix search
        System.out.println("\nPrefix Search 'user:1':");
        List<String> user1 = tree.searchPrefix("user:1");
        for (String key : user1) {
            System.out.println("  → " + key);
        }
        
        System.out.println("\nPrefix Search 'product:laptop':");
        List<String> laptops = tree.searchPrefix("product:laptop");
        for (String key : laptops) {
            System.out.println("  → " + key);
        }
        
        // Add sequential keys for range search
        System.out.println("\nAdding sequential keys...");
        for (int i = 0; i < 20; i++) {
            tree.put(String.format("seq:%03d", i), ("data-" + i).getBytes());
        }
        
        System.out.println("\nRange Search 'seq:005' to 'seq:010':");
        List<String> range = tree.searchRange("seq:005", "seq:010");
        for (String key : range) {
            System.out.println("  → " + key);
        }
        
        System.out.println();
    }
    
    /**
     * Example 6: Integrated Storage with Cubic Index
     */
    public static void example6_IntegratedStorage() throws Exception {
        System.out.println("Example 6: Cubic-Indexed Storage Engine");
        System.out.println("----------------------------------------");
        
        // Create backing storage
        LSMStorageEngine lsmStorage = new LSMStorageEngine("/tmp/cube-indexed-example");
        
        // Wrap with cubic index
        CubicIndexedStorage storage = new CubicIndexedStorage(lsmStorage);
        
        System.out.println("Created cubic-indexed storage");
        System.out.println("  Backing: LSM Storage Engine");
        System.out.println("  Index: Cubic Index Tree");
        
        // Write data
        System.out.println("\nWriting data...");
        for (int i = 0; i < 100; i++) {
            String key = "item:" + i;
            String value = "Item " + i + " - " + UUID.randomUUID();
            storage.put(key, value.getBytes());
        }
        
        System.out.println("✓ Wrote 100 items");
        
        // Read with index acceleration
        System.out.println("\nReading data (using cubic index)...");
        long startTime = System.nanoTime();
        
        byte[] value = storage.get("item:42");
        
        long endTime = System.nanoTime();
        double timeMs = (endTime - startTime) / 1_000_000.0;
        
        System.out.println("✓ Retrieved: " + new String(value).substring(0, 20) + "...");
        System.out.println("  Time: " + String.format("%.3f", timeMs) + " ms");
        
        // Prefix search
        System.out.println("\nPrefix search for 'item:1'...");
        Iterator<String> results = storage.scan("item:1");
        int count = 0;
        while (results.hasNext() && count < 5) {
            System.out.println("  → " + results.next());
            count++;
        }
        System.out.println("  (showing first " + count + " results)");
        
        // Show statistics
        System.out.println("\nIndex Statistics:");
        Map<String, Object> indexStats = storage.getIndexStats();
        System.out.println("  Total Levels: " + indexStats.get("totalLevels"));
        System.out.println("  Total Keys: " + indexStats.get("totalKeys"));
        
        @SuppressWarnings("unchecked")
        Map<String, Integer> sideDist = (Map<String, Integer>) indexStats.get("sideDistribution");
        System.out.println("\n  Side Distribution:");
        for (Map.Entry<String, Integer> entry : sideDist.entrySet()) {
            System.out.printf("    %-6s: %d keys%n", entry.getKey(), entry.getValue());
        }
        
        // Visual structure
        System.out.println("\nCubic Index Structure:");
        storage.printIndexStructure();
        
        // Cleanup
        storage.close();
        
        System.out.println();
    }
}
