/*
 * MegaMek - Copyright (C) 2004 Ben Mazur (bmazur@sev.org)
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the Free
 *  Software Foundation; either version 2 of the License, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 *  for more details.
 */

package megamek.client.ui.AWT.util;

import java.awt.Image;
import java.awt.Toolkit;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import megamek.common.util.ItemFile;
import megamek.common.util.ItemFileFactory;

/**
 * This class will produce <code>Image</code> objects from files. If an image
 * file is inside of JAR and ZIP file, then it must save the contents to a
 * temporary file. <p/> Created on January 18, 2004
 * 
 * @author James Damour
 * @version 1
 */
public class ImageFileFactory implements ItemFileFactory {

    /**
     * Accepted image file extentions
     */
    private final static String JPG = "JPG", JPEG = "JPEG", GIF = "GIF", PNG = "PNG"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$

    /**
     * Implement the Singleton pattern.
     */
    private ImageFileFactory() {
    }

    private static ImageFileFactory singleton = null;

    /**
     * Get the Singleton <code>ImageFileFactory</code>.
     * 
     * @return the Singleton <code>ImageFileFactory</code>.
     */
    public static ImageFileFactory getInstance() {
        if (null == singleton)
            singleton = new ImageFileFactory();
        return singleton;
    }

    /**
     * Get the <code>ItemFile</code> for the given <code>File</code>.
     * 
     * @param file - the input <code>File</code> object that will be read to
     *            produce the item. This value must not be <code>null</code>.
     * @return an <code>ItemFile</code> for the given file.
     * @throws <code>IllegalArgumentException</code> if the <code>file</code>
     *             is <code>null</code>.
     */
    public ItemFile getItemFile(final File file)
            throws IllegalArgumentException {

        // Validate the input.
        if (null == file) {
            throw new IllegalArgumentException("A null image file was passed."); //$NON-NLS-1$
        }

        // Construct an anonymous class that gets an Image for the file.
        return new ItemFile() {

            private File itemFile = file; // copy the file entry
            private Image image = null; // cache the Image

            public Object getItem() throws Exception {
                // Cache the image on first use.
                if (null == image) {
                    String name = itemFile.getAbsolutePath();
                    image = Toolkit.getDefaultToolkit().getImage(name);
                }
                // Return a copy of the image.
                return image.getScaledInstance(84, 72, Image.SCALE_FAST);
            } // End getItem()
        };

    }

    /**
     * Get the <code>ItemFile</code> for the given <code>ZipEntry</code> in
     * the <code>ZipFile</code>.
     * 
     * @param zipEntry - the <code>ZipEntry</code> that will be read to
     *            produce the item. This value must not be <code>null</code>.
     * @param zipFile - the <code>ZipFile</code> object that contains the
     *            <code>ZipEntry</code> that will produce the item. This value
     *            must not be <code>null</code>.
     * @return an <code>ItemFile</code> for the given zip file entry.
     * @throws <code>IllegalArgumentException</code> if either the
     *             <code>zipEntry</code> or the <code>zipFile</code> is
     *             <code>null</code>.
     */
    public ItemFile getItemFile(final ZipEntry zipEntry, final ZipFile zipFile)
            throws IllegalArgumentException {

        // Validate the input.
        if (null == zipEntry) {
            throw new IllegalArgumentException("A null ZIP entry was passed."); //$NON-NLS-1$
        }
        if (null == zipFile) {
            throw new IllegalArgumentException("A null ZIP file was passed."); //$NON-NLS-1$
        }

        // Construct an anonymous class that gets an Image for the file.
        return new ItemFile() {

            private ZipEntry itemEntry = zipEntry; // copy the ZipEntry
            private Image image = null; // cache the Image

            public Object getItem() throws Exception {

                // Cache the image on first use.
                if (null == image) {

                    // Get ready to read from the item.
                    InputStream in = new BufferedInputStream(zipFile
                            .getInputStream(itemEntry), (int) itemEntry
                            .getSize());

                    // Make a buffer big enough to hold the item,
                    // read from the ZIP file, and write it to temp.
                    byte[] buffer = new byte[(int) itemEntry.getSize()];
                    in.read(buffer);

                    // Check the last 10 bytes. I've been having
                    // some problems with incomplete image files,
                    // and I want to detect it early and give advice
                    // to players for dealing with the problem.
                    int index = (int) itemEntry.getSize() - 10;
                    while (itemEntry.getSize() > index) {
                        if (0 != buffer[index]) {
                            break;
                        }
                        index++;
                    }
                    if (itemEntry.getSize() <= index) {
                        throw new IOException(
                                "Error reading " + itemEntry.getName() + //$NON-NLS-1$
                                        "\nYou may want to unzip " + //$NON-NLS-1$
                                        zipFile.getName());
                    }

                    // Create the image from the buffer.
                    image = Toolkit.getDefaultToolkit().createImage(buffer);

                } // End get-image

                // Return a copy of the image.
                return image.getScaledInstance(84, 72, Image.SCALE_FAST);

            } // End getItem()
        };

    }

    /**
     * The method that must be implemented by any object that filters filenames
     * (i.e., selects a subset of filenames from a list of filenames). <p/> This
     * definition is copied from <code>java.io.FilenameFilter</code> for
     * completeness.
     * 
     * @param dir - the <code>File</code> object of the directory containing
     *            the named file.
     * @param name - the <code>String</code> name of the file.
     */
    public boolean accept(File dir, String name) {

        // Convert the file name to upper case, and compare it to image
        // file extensions. Yeah, it's a bit of a hack, but whatever.
        String ucName = name.toUpperCase();
        return (ucName.endsWith(JPG) || ucName.endsWith(JPEG)
                || ucName.endsWith(GIF) || ucName.endsWith(PNG));
    }

    /**
     * The method that must be implemented by any object that filters filenames
     * within a <code>ZipFile</code> (i.e., selects a subset of filenames from
     * a list of filenames in a ZIP archive).
     * 
     * @param zipFile - the <code>ZipFile</code> object that contains the
     *            named file's entry.
     * @param name - the <code>String</code> name of the file.
     */
    public boolean accept(ZipFile zipFile, String name) {

        // Convert the file name to upper case, and compare it to image
        // file extensions. Yeah, it's a bit of a hack, but whatever.
        String ucName = name.toUpperCase();
        return (ucName.endsWith(JPG) || ucName.endsWith(JPEG)
                || ucName.endsWith(GIF) || ucName.endsWith(PNG));
    }

}
