# EBook.java

    package com.university.bookstore.model;

    import java.util.Objects;
    import java.util.Set;
    import com.fasterxml.jackson.annotation.JsonCreator;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

    /**
     * Represents an electronic book (e-book) in the bookstore system.
     * Extends Material and implements Media interface to demonstrate
     * multiple inheritance through interfaces.
     *
     * <p>EBooks support various file formats, DRM protection, and
     * provide reading time estimation based on word count.</p>
     *
     * @author Navid Mohaghegh
     * @version 2.0
     * @since 2024-09-15
     */
    @JsonIgnoreProperties(ignoreUnknown = true)
    public class EBook extends Material implements Media {

        private static final Set<String> VALID_FORMATS = Set.of("PDF", "EPUB", "MOBI");
        private static final int WORDS_PER_MINUTE = 250;
        private static final double DRM_FREE_DISCOUNT = 0.15;

        private final String author;
        private final String fileFormat;
        private final double fileSize; // in MB
        private final boolean drmEnabled;
        private final int wordCount;
        private final MediaQuality quality;

        /**
         * Constructs a new EBook with validation.
         *
         * @param id unique identifier
         * @param title the book title
         * @param author the author name
         * @param price the price in dollars
         * @param year the publication year
         * @param fileFormat the file format (PDF, EPUB, or MOBI)
         * @param fileSize the file size in MB
         * @param drmEnabled whether DRM is enabled
         * @param wordCount the number of words in the book
         * @param quality the media quality
         * @throws IllegalArgumentException if validation fails
         */
        @JsonCreator
        public EBook(@JsonProperty("id") String id,
                     @JsonProperty("title") String title,
                     @JsonProperty("author") String author,
                     @JsonProperty("price") double price,
                     @JsonProperty("year") int year,
                     @JsonProperty("fileFormat") String fileFormat,
                     @JsonProperty("fileSize") double fileSize,
                     @JsonProperty("drmEnabled") boolean drmEnabled,
                     @JsonProperty("wordCount") int wordCount,
                     @JsonProperty("quality") MediaQuality quality) {
            super(id, title, price, year, MaterialType.E_BOOK);
            this.author = validateStringField(author, "Author");
            this.fileFormat = validateFileFormat(fileFormat);
            this.fileSize = validateFileSize(fileSize);
            this.drmEnabled = drmEnabled;
            this.wordCount = validateWordCount(wordCount);
            this.quality = Objects.requireNonNull(quality, "Media quality cannot be null");
        }

        /**
         * Validates that the file format is supported.
         *
         * @param format the file format to validate
         * @return the validated format
         * @throws IllegalArgumentException if format is not supported
         */
        private String validateFileFormat(String format) {
            if (format == null) {
                throw new NullPointerException("File format cannot be null");
            }
            String upperFormat = format.trim().toUpperCase();
            if (!VALID_FORMATS.contains(upperFormat)) {
                throw new IllegalArgumentException(
                    String.format("Unsupported file format: %s. Supported formats: %s",
                        format, VALID_FORMATS));
            }
            return upperFormat;
        }

        /**
         * Validates that the file size is positive.
         *
         * @param size the file size to validate
         * @return the validated size
         * @throws IllegalArgumentException if size is not positive
         */
        private double validateFileSize(double size) {
            if (size <= 0.0) {
                throw new IllegalArgumentException(
                    String.format("File size must be positive. Provided: %.2f MB", size));
            }
            if (Double.isNaN(size) || Double.isInfinite(size)) {
                throw new IllegalArgumentException(
                    String.format("File size must be a valid number. Provided: %.2f MB", size));
            }
            return size;
        }

        /**
         * Validates that the word count is non-negative.
         *
         * @param count the word count to validate
         * @return the validated count
         * @throws IllegalArgumentException if count is negative
         */
        private int validateWordCount(int count) {
            if (count < 0) {
                throw new IllegalArgumentException(
                    String.format("Word count cannot be negative. Provided: %d", count));
            }
            return count;
        }

        @Override
        public String getCreator() {
            return author;
        }

        /**
         * Gets the author of the e-book.
         * @return the author
         */
        public String getAuthor() {
            return author;
        }

        @Override
        public String getDisplayInfo() {
            return String.format("EBook: %s by %s (%s, %.1f MB, %s, %d words)",
                getTitle(), author, fileFormat, fileSize,
                drmEnabled ? "DRM" : "DRM-free", wordCount);
        }

        @Override
        public double getDiscountRate() {
            // DRM-free books get a 15% discount
            return drmEnabled ? 0.0 : DRM_FREE_DISCOUNT;
        }

        /**
         * Calculates estimated reading time in minutes based on average reading speed.
         *
         * @return estimated reading time in minutes
         */
        public int getReadingTimeMinutes() {
            return wordCount / WORDS_PER_MINUTE;
        }

        /**
         * Gets the file format of the ebook.
         *
         * @return the file format
         */
        public String getFileFormat() {
            return fileFormat;
        }

        /**
         * Gets the file size in MB.
         *
         * @return the file size
         */
        public double getFileSize() {
            return fileSize;
        }

        /**
         * Checks if DRM is enabled.
         *
         * @return true if DRM is enabled
         */
        public boolean isDrmEnabled() {
            return drmEnabled;
        }

        /**
         * Gets the word count of the book.
         *
         * @return the word count
         */
        public int getWordCount() {
            return wordCount;
        }

        @Override
        public MediaQuality getQuality() {
            return quality;
        }

        public String getDescription() {
            return String.format("Digital %s format book by %s. %s, %.1f MB. Estimated reading time: %d minutes.",
                fileFormat, author,
                drmEnabled ? "DRM protected" : "DRM-free",
                fileSize,
                getReadingTimeMinutes());
        }

        // Media interface implementation
        @Override
        public int getDuration() {
            return getReadingTimeMinutes();
        }

        @Override
        public String getFormat() {
            return fileFormat;
        }

        @Override
        public boolean isStreamingOnly() {
            return false; // E-books are downloadable, not streaming
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (!(obj instanceof EBook)) return false;
            if (!super.equals(obj)) return false;

            EBook other = (EBook) obj;
            return Double.compare(other.fileSize, fileSize) == 0 &&
                   drmEnabled == other.drmEnabled &&
                   wordCount == other.wordCount &&
                   Objects.equals(author, other.author) &&
                   Objects.equals(fileFormat, other.fileFormat) &&
                   quality == other.quality;
        }

        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), author, fileFormat, fileSize,
                               drmEnabled, wordCount, quality);
        }

        @Override
        public String toString() {
            return String.format("EBook[ID=%s, Title='%s', Author='%s', Price=$%.2f, Year=%d, " +
                               "Format=%s, Size=%.1fMB, DRM=%s, Words=%d, Quality=%s]",
                getId(), getTitle(), author, getPrice(), getYear(),
                fileFormat, fileSize, drmEnabled, wordCount, quality);
        }
    }