VideoMaterial.java

package com.university.bookstore.model;

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

/**
 * Represents video content (movies, documentaries, educational videos).
 * Implements both Material and Media interfaces to demonstrate multiple inheritance.
 * 
 * @author Navid Mohaghegh
 * @version 2.0
 * @since 2024-09-15
 */
public class VideoMaterial extends Material implements Media {
    
    private final String director;
    private final int duration;
    private final String format;
    private final double fileSize;
    private final MediaQuality quality;
    private final VideoType videoType;
    private final String rating;
    private final List<String> cast;
    private final boolean subtitlesAvailable;
    private final String aspectRatio;
    
    /**
     * Types of video content.
     */
    public enum VideoType {
        MOVIE("Movie"),
        DOCUMENTARY("Documentary"),
        EDUCATIONAL("Educational"),
        TV_SERIES("TV Series"),
        SHORT_FILM("Short Film"),
        TUTORIAL("Tutorial");
        
        private final String label;
        
        VideoType(String label) {
            this.label = label;
        }
        
        @Override
        public String toString() {
            return label;
        }
    }
    
    /**
     * Creates a new VideoMaterial.
     * 
     * @param id unique identifier (UPC/EAN code)
     * @param title video title
     * @param director director or producer
     * @param price price in dollars
     * @param year release year
     * @param duration duration in minutes
     * @param format video format (MP4, AVI, MKV, etc.)
     * @param fileSize size in megabytes
     * @param quality video quality
     * @param videoType type of video
     * @param rating content rating (G, PG, PG-13, R, etc.)
     * @param cast list of main cast members
     * @param subtitlesAvailable whether subtitles are included
     * @param aspectRatio aspect ratio (16:9, 4:3, etc.)
     */
    public VideoMaterial(String id, String title, String director, double price,
                        int year, int duration, String format, double fileSize,
                        MediaQuality quality, VideoType videoType, String rating,
                        List<String> cast, boolean subtitlesAvailable, String aspectRatio) {
        super(id, title, price, year, 
              videoType == VideoType.DOCUMENTARY ? MaterialType.DOCUMENTARY : MaterialType.VIDEO);
        this.director = validateStringField(director, "Director");
        this.duration = validateDuration(duration);
        this.format = validateStringField(format, "Format");
        this.fileSize = validateFileSize(fileSize);
        this.quality = quality != null ? quality : MediaQuality.HD;
        this.videoType = videoType != null ? videoType : VideoType.MOVIE;
        this.rating = rating != null ? rating : "NR";
        this.cast = cast != null ? cast : Arrays.asList();
        this.subtitlesAvailable = subtitlesAvailable;
        this.aspectRatio = aspectRatio != null ? aspectRatio : "16:9";
    }
    
    private int validateDuration(int duration) {
        if (duration <= 0) {
            throw new IllegalArgumentException(
                "Duration must be positive. Provided: " + duration);
        }
        return duration;
    }
    
    private double validateFileSize(double fileSize) {
        if (fileSize <= 0) {
            throw new IllegalArgumentException(
                "File size must be positive. Provided: " + fileSize);
        }
        return fileSize;
    }
    
    @Override
    public String getCreator() {
        return director;
    }
    
    @Override
    public String getDisplayInfo() {
        return String.format("%s (%d) - Directed by %s, %s, %d min, Rated %s, $%.2f",
            title, year, director, videoType, duration, rating, price);
    }
    
    @Override
    public int getDuration() {
        return duration;
    }
    
    @Override
    public String getFormat() {
        return format;
    }
    
    @Override
    public double getFileSize() {
        return fileSize;
    }
    
    @Override
    public boolean isStreamingOnly() {
        return fileSize > 4096;
    }
    
    @Override
    public MediaQuality getQuality() {
        return quality;
    }
    
    @Override
    public double getDiscountRate() {
        int currentYear = java.time.Year.now().getValue();
        if (year < currentYear - 5) {
            return 0.30;
        } else if (year < currentYear - 2) {
            return 0.15;
        }
        return 0.0;
    }
    
    /**
     * Checks if this is a feature-length video.
     * 
     * @return true if duration >= 60 minutes
     */
    public boolean isFeatureLength() {
        return duration >= 60;
    }
    
    /**
     * Gets bandwidth requirement for streaming in Mbps.
     * 
     * @return required bandwidth
     */
    public double getStreamingBandwidth() {
        switch (quality) {
            case LOW: return 1.5;
            case STANDARD: return 3.0;
            case HIGH: return 5.0;
            case HD: return 8.0;
            case ULTRA_HD: return 25.0;
            default: return 5.0;
        }
    }
    
    /**
     * Gets storage space required with compression.
     * 
     * @param compressionRatio compression ratio (0.5 = 50% compression)
     * @return compressed size in MB
     */
    public double getCompressedSize(double compressionRatio) {
        if (compressionRatio <= 0 || compressionRatio > 1) {
            throw new IllegalArgumentException("Compression ratio must be between 0 and 1");
        }
        return fileSize * compressionRatio;
    }
    
    public String getDirector() {
        return director;
    }
    
    public VideoType getVideoType() {
        return videoType;
    }
    
    public String getRating() {
        return rating;
    }
    
    public List<String> getCast() {
        return cast;
    }
    
    public boolean hasSubtitles() {
        return subtitlesAvailable;
    }
    
    public String getAspectRatio() {
        return aspectRatio;
    }
    
    @Override
    public String toString() {
        return String.format("VideoMaterial[ID=%s, Title='%s', Director='%s', Type=%s, Duration=%dmin, Quality=%s, Rating=%s, Price=$%.2f]",
            id, title, director, videoType, duration, quality, rating, price);
    }
}