PolymorphismDemo.java

package com.university.bookstore.demo;

import java.util.Arrays;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.university.bookstore.api.MaterialStore;
import com.university.bookstore.impl.MaterialStoreImpl;
import com.university.bookstore.model.AudioBook;
import com.university.bookstore.model.Magazine;
import com.university.bookstore.model.Material;
import com.university.bookstore.model.Media;
import com.university.bookstore.model.PrintedBook;
import com.university.bookstore.model.VideoMaterial;

/**
 * Demonstration class showing polymorphism and OOP best practices.
 * 
 * @author Navid Mohaghegh
 * @version 2.0
 */
public class PolymorphismDemo {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(PolymorphismDemo.class);
    
    public static void main(String[] args) {
        LOGGER.info("=== Material Store Polymorphism Demo ===\n");
        
        MaterialStore store = createSampleStore();
        
        demonstratePolymorphicBehavior(store);
        demonstrateInterfaceSegregation(store);
        demonstrateAbstraction(store);
        demonstrateDynamicBinding(store);
        demonstrateSOLIDPrinciples(store);
        demonstrateMediaVersatility(store);
        demonstrateStreamingVsDownload(store);
    }
    
    private static MaterialStore createSampleStore() {
        MaterialStoreImpl store = new MaterialStoreImpl();
        
        // Traditional print materials
        PrintedBook book = new PrintedBook(
            "9780134685991", "Effective Java", "Joshua Bloch",
            45.99, 2018, 412, "Addison-Wesley", true
        );
        
        Magazine magazine = new Magazine(
            "12345678", "National Geographic", "NatGeo Society",
            6.99, 2024, 3, "Monthly", "Science"
        );
        
        // Audio materials with various qualities
        AudioBook audioBook1 = new AudioBook(
            "9780143038092", "1984", "George Orwell", "Simon Prebble",
            14.99, 2020, 690, "MP3", 850.5,
            Media.MediaQuality.HIGH, "English", true
        );
        
        AudioBook audioBook2 = new AudioBook(
            "9780547928227", "The Hobbit", "J.R.R. Tolkien", "Rob Inglis",
            24.99, 2022, 660, "M4B", 450.0,
            Media.MediaQuality.STANDARD, "English", false
        );
        
        AudioBook audiobook3 = new AudioBook(
            "9781250318398", "Atomic Habits", "James Clear", "James Clear",
            19.99, 2023, 320, "FLAC", 1200.0,
            Media.MediaQuality.ULTRA_HD, "English", true
        );
        
        // Diverse video content
        VideoMaterial documentary = new VideoMaterial(
            "883929665839", "Planet Earth II", "David Attenborough",
            29.99, 2016, 300, "MP4", 4500.0,
            Media.MediaQuality.ULTRA_HD, VideoMaterial.VideoType.DOCUMENTARY,
            "G", Arrays.asList("David Attenborough"), true, "16:9"
        );
        
        VideoMaterial movie = new VideoMaterial(
            "043396544789", "The Matrix", "The Wachowskis",
            19.99, 1999, 136, "MP4", 2800.0,
            Media.MediaQuality.HD, VideoMaterial.VideoType.MOVIE,
            "R", Arrays.asList("Keanu Reeves", "Laurence Fishburne", "Carrie-Anne Moss"),
            true, "2.39:1"
        );
        
        VideoMaterial tutorial = new VideoMaterial(
            "TUT20240001", "Advanced Java Programming", "Tech Education Inc",
            89.99, 2024, 480, "MP4", 3200.0,
            Media.MediaQuality.HD, VideoMaterial.VideoType.TUTORIAL,
            "NR", Arrays.asList("Dr. Sarah Chen"), true, "16:9"
        );
        
        VideoMaterial tvSeries = new VideoMaterial(
            "883929696789", "Breaking Bad - Season 1", "Vince Gilligan",
            39.99, 2008, 420, "MKV", 8500.0,
            Media.MediaQuality.HD, VideoMaterial.VideoType.TV_SERIES,
            "TV-MA", Arrays.asList("Bryan Cranston", "Aaron Paul", "Anna Gunn"),
            true, "16:9"
        );
        
        VideoMaterial shortFilm = new VideoMaterial(
            "SHORT2023001", "The Red Balloon", "Albert Lamorisse",
            9.99, 1956, 34, "AVI", 250.0,
            Media.MediaQuality.STANDARD, VideoMaterial.VideoType.SHORT_FILM,
            "G", Arrays.asList("Pascal Lamorisse"), false, "4:3"
        );
        
        VideoMaterial educationalVideo = new VideoMaterial(
            "EDU2024567", "Quantum Physics Explained", "MIT OpenCourseWare",
            0.00, 2024, 90, "WEBM", 680.0,
            Media.MediaQuality.HIGH, VideoMaterial.VideoType.EDUCATIONAL,
            "NR", Arrays.asList("Prof. Walter Lewin"), true, "16:9"
        );
        
        // Add all materials to store
        store.addMaterial(book);
        store.addMaterial(magazine);
        store.addMaterial(audioBook1);
        store.addMaterial(audioBook2);
        store.addMaterial(audiobook3);
        store.addMaterial(documentary);
        store.addMaterial(movie);
        store.addMaterial(tutorial);
        store.addMaterial(tvSeries);
        store.addMaterial(shortFilm);
        store.addMaterial(educationalVideo);
        
        return store;
    }
    
    private static void demonstratePolymorphicBehavior(MaterialStore store) {
        LOGGER.info("1. POLYMORPHIC BEHAVIOR");
        LOGGER.info("------------------------");
        
        List<Material> materials = store.getAllMaterials();
        
        for (Material material : materials) {
            LOGGER.info("\nMaterial Type: {}", material.getType().getDisplayName());
            LOGGER.info("Title: {}", material.getTitle());
            LOGGER.info("Creator: {}", material.getCreator());
            LOGGER.info("Display Info: {}", material.getDisplayInfo());
            LOGGER.info("Original Price: ${}", String.format("%.2f", material.getPrice()));
            LOGGER.info("Discount Rate: {}%", (material.getDiscountRate() * 100));
            LOGGER.info("Discounted Price: ${}", String.format("%.2f", material.getDiscountedPrice()));
        }
        
        LOGGER.info("\n");
    }
    
    private static void demonstrateInterfaceSegregation(MaterialStore store) {
        LOGGER.info("2. INTERFACE SEGREGATION (Media Interface)");
        LOGGER.info("-------------------------------------------");
        
        List<Media> mediaItems = store.getMediaMaterials();
        
        LOGGER.info("Found {} media items:\n", mediaItems.size());
        
        for (Media media : mediaItems) {
            LOGGER.info("Media: {}", ((Material)media).getTitle());
            LOGGER.info("  {}", media.getPlaybackInfo());
            LOGGER.info("  Download time at 10 Mbps: {} seconds", media.estimateDownloadTime(10));
            LOGGER.info("  Streaming only: {}", media.isStreamingOnly());
        }
        
        LOGGER.info("\n");
    }
    
    private static void demonstrateAbstraction(MaterialStore store) {
        LOGGER.info("3. ABSTRACTION (Abstract Base Class)");
        LOGGER.info("-------------------------------------");
        
        LOGGER.info("Materials grouped by type:\n");
        
        for (Material.MaterialType type : Material.MaterialType.values()) {
            List<Material> ofType = store.getMaterialsByType(type);
            if (!ofType.isEmpty()) {
                LOGGER.info("{}: {} items", type.getDisplayName(), ofType.size());
                for (Material m : ofType) {
                    LOGGER.info("  - {} (${})", m.getTitle(), String.format("%.2f", m.getPrice()));
                }
            }
        }
        
        LOGGER.info("\n");
    }
    
    private static void demonstrateDynamicBinding(MaterialStore store) {
        LOGGER.info("4. DYNAMIC BINDING");
        LOGGER.info("-------------------");
        
        Material cheapest = store.filterMaterials(m -> true).stream()
            .min((a, b) -> Double.compare(a.getPrice(), b.getPrice()))
            .orElse(null);
        
        Material mostExpensive = store.filterMaterials(m -> true).stream()
            .max((a, b) -> Double.compare(a.getPrice(), b.getPrice()))
            .orElse(null);
        
        LOGGER.info("Cheapest item: {}", cheapest.getDisplayInfo());
        LOGGER.info("Most expensive: {}", mostExpensive.getDisplayInfo());
        
        LOGGER.info("\nDynamic method dispatch:");
        processPolymorphically(cheapest);
        processPolymorphically(mostExpensive);
        
        LOGGER.info("\n");
    }
    
    private static void processPolymorphically(Material material) {
        LOGGER.info("  Processing: {}", material.getTitle());
        
        if (material instanceof Media) {
            Media media = (Media) material;
            LOGGER.info("    -> This is media with duration: {} minutes", media.getDuration());
        }
        
        if (material instanceof PrintedBook) {
            PrintedBook book = (PrintedBook) material;
            LOGGER.info("    -> This is a book with {} pages", book.getPages());
        }
        
        if (material instanceof Magazine) {
            Magazine mag = (Magazine) material;
            LOGGER.info("    -> This is a magazine, issue #{}", mag.getIssueNumber());
        }
    }
    
    private static void demonstrateSOLIDPrinciples(MaterialStore store) {
        LOGGER.info("5. SOLID PRINCIPLES IN ACTION");
        LOGGER.info("------------------------------");
        
        MaterialStore.InventoryStats stats = store.getInventoryStats();
        
        LOGGER.info("Inventory Statistics:");
        LOGGER.info("  Total items: {}", stats.getTotalCount());
        LOGGER.info("  Average price: ${}", String.format("%.2f", stats.getAveragePrice()));
        LOGGER.info("  Median price: ${}", String.format("%.2f", stats.getMedianPrice()));
        LOGGER.info("  Unique types: {}", stats.getUniqueTypes());
        LOGGER.info("  Media items: {}", stats.getMediaCount());
        LOGGER.info("  Print items: {}", stats.getPrintCount());
        
        LOGGER.info("\nTotal inventory value: ${}", String.format("%.2f", store.getTotalInventoryValue()));
        LOGGER.info("Total with discounts: ${}", String.format("%.2f", store.getTotalDiscountedValue()));
        
        double savings = store.getTotalInventoryValue() - store.getTotalDiscountedValue();
        LOGGER.info("Total savings: ${}", String.format("%.2f", savings));
        
        LOGGER.info("\nFiltered Results (Lambda Expression):");
        List<Material> affordable = store.filterMaterials(m -> m.getPrice() < 20);
        LOGGER.info("  Items under $20: {}", affordable.size());
        for (Material m : affordable) {
            LOGGER.info("    - {} (${})", m.getTitle(), String.format("%.2f", m.getPrice()));
        }
    }
    
    private static void demonstrateMediaVersatility(MaterialStore store) {
        LOGGER.info("\n6. MEDIA VERSATILITY SHOWCASE");
        LOGGER.info("-------------------------------");
        
        LOGGER.info("\nVideo Content Types:");
        List<Material> videos = store.getMaterialsByType(Material.MaterialType.VIDEO);
        videos.addAll(store.getMaterialsByType(Material.MaterialType.DOCUMENTARY));
        
        for (Material material : videos) {
            if (material instanceof VideoMaterial) {
                VideoMaterial video = (VideoMaterial) material;
                LOGGER.info("\n  {}: {}", video.getVideoType(), video.getTitle());
                LOGGER.info("    Director: {}", video.getDirector());
                LOGGER.info("    Duration: {} minutes", video.getDuration());
                LOGGER.info("    Quality: {}", video.getQuality());
                LOGGER.info("    Format: {}", video.getFormat());
                LOGGER.info("    Rating: {}", video.getRating());
                LOGGER.info("    Aspect Ratio: {}", video.getAspectRatio());
                LOGGER.info("    Subtitles: {}", (video.hasSubtitles() ? "Yes" : "No"));
                LOGGER.info("    Streaming Bandwidth: {} Mbps", video.getStreamingBandwidth());
                
                if (!video.getCast().isEmpty()) {
                    LOGGER.info("    Cast: {}", String.join(", ", 
                        video.getCast().size() > 3 ? 
                        video.getCast().subList(0, 3) : video.getCast()));
                }
            }
        }
        
        LOGGER.info("\n\nAudio Quality Comparison:");
        List<Material> audioMaterials = store.filterMaterials(m -> m instanceof AudioBook);
        
        for (Material material : audioMaterials) {
            AudioBook audio = (AudioBook) material;
            LOGGER.info("\n  {}", audio.getTitle());
            LOGGER.info("    Quality: {}", audio.getQuality());
            LOGGER.info("    Format: {}", audio.getFormat());
            LOGGER.info("    File Size: {} MB", audio.getFileSize());
            LOGGER.info("    Duration: {} minutes", audio.getDuration());
            double estimatedBitrate = (audio.getFileSize() * 8 * 1024) / (audio.getDuration() * 60);
            LOGGER.info("    Estimated Bitrate: {} kbps", String.format("%.0f", estimatedBitrate));
            LOGGER.info("    Download Time (10 Mbps): {} seconds", audio.estimateDownloadTime(10));
        }
        
        LOGGER.info("\n");
    }
    
    private static void demonstrateStreamingVsDownload(MaterialStore store) {
        LOGGER.info("7. STREAMING VS DOWNLOAD ANALYSIS");
        LOGGER.info("-----------------------------------");
        
        List<Media> mediaItems = store.getMediaMaterials();
        
        LOGGER.info("\nStreaming-Only Content (Large Files > 4GB):");
        for (Media media : mediaItems) {
            if (media.isStreamingOnly()) {
                Material material = (Material) media;
                LOGGER.info("  - {} ({} MB)", material.getTitle(), media.getFileSize());
            }
        }
        
        LOGGER.info("\nDownloadable Content:");
        for (Media media : mediaItems) {
            if (!media.isStreamingOnly()) {
                Material material = (Material) media;
                LOGGER.info("  - {} ({} MB, Download time @ 50 Mbps: {}s)", 
                           material.getTitle(), media.getFileSize(), media.estimateDownloadTime(50));
            }
        }
        
        LOGGER.info("\nQuality vs Storage Requirements:");
        LOGGER.info("  Format Distribution:");
        
        int ultraHD = 0, highQ = 0, standard = 0;
        for (Media media : mediaItems) {
            switch (media.getQuality()) {
                case ULTRA_HD: ultraHD++; break;
                case HIGH:
                case HD: highQ++; break;
                default: standard++; break;
            }
        }
        
        LOGGER.info("    Ultra HD: {} items", ultraHD);
        LOGGER.info("    High/HD: {} items", highQ);
        LOGGER.info("    Standard/Low: {} items", standard);
        
        double totalStorage = mediaItems.stream()
            .mapToDouble(Media::getFileSize)
            .sum();
        
        LOGGER.info("\n  Total Storage Required: {} GB", String.format("%.2f", totalStorage / 1024));
        
        LOGGER.info("\n  Bandwidth Requirements by Quality:");
        for (Material material : store.getAllMaterials()) {
            if (material instanceof VideoMaterial) {
                VideoMaterial video = (VideoMaterial) material;
                if (video.getQuality() == Media.MediaQuality.ULTRA_HD || 
                    video.getQuality() == Media.MediaQuality.HD) {
                    LOGGER.info("    {} ({}): {} Mbps", video.getTitle(), video.getQuality(), video.getStreamingBandwidth());
                }
            }
        }
        
        LOGGER.info("\n");
    }
}