package com.srbenoit.math.grapher;

import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import com.srbenoit.log.LoggedObject;
import com.srbenoit.ui.UIUtilities;

/**
 * A graphing tool that renders the graph of a function.
 */
public class Grapher extends LoggedObject {

    /** PNG file format */
    private static final String PNG = "PNG";

    /** JPEG file format */
    private static final String JPEG = "JPEG";

    /** GIF file format */
    private static final String GIF = "GIF";

    /** BMP file format */
    private static final String BMP = "BMP";

    /** WBMP file format */
    private static final String WBMP = "WBMP";

    /** the image that displays the graph */
    private final transient GraphImage image;

    /**
     * Create a <code>Grapher</code> to render a function's graph.
     *
     * @param  xSize  the width of graphs to generate
     * @param  ySize  the height of graphs to generate
     */
    public Grapher(final int xSize, final int ySize) {

        super();

        this.image = new GraphImage(xSize, ySize);
    }

    /**
     * Gets the <code>GraphImage</code> that this grapher draws to.
     *
     * @return  the <code>GraphImage</code>
     */
    public GraphImage getGraphImage() {

        return this.image;
    }

    /**
     * Renders the graph.
     *
     * @param  functions  the functions to be graphed
     */
    public void graph(final Graphable... functions) {

        this.image.graph(functions);
    }

    /**
     * Exports the image of the graph to a file.
     *
     * @param   file  the file to which to export
     * @throws  IOException  if there is an error writing the file
     */
    public void export(final File file) throws IOException {

        String fname;
        int pos;
        String ext;
        String format;
        File target;

        fname = file.getName();
        pos = fname.lastIndexOf('.');

        if (pos == -1) {
            format = JPEG;
            target = new File(file.getParent(), file.getName() + ".jpg");
        } else {
            ext = fname.substring(pos + 1).toUpperCase();

            if (("JPG".equals(ext)) || (JPEG.equals(ext))) {
                format = JPEG;
                target = file;
            } else if (PNG.equals(ext)) {
                format = PNG;
                target = file;
            } else if (GIF.equals(ext)) {
                format = GIF;
                target = file;
            } else if (BMP.equals(ext)) {
                format = BMP;
                target = file;
            } else if (WBMP.equals(ext)) {
                format = WBMP;
                target = file;
            } else {
                format = JPEG;
                target = new File(file.getParent(), file.getName() + ".jpg");
            }
        }

        ImageIO.write(this.image.getImage(), format, target);
    }

    /**
     * Builds a <code>JFrame</code> and displays the grapher in that frame.
     *
     * @param  title  the frame title
     */
    public void showInFrame(final String title) {

        FrameBuilder builder;

        builder = new FrameBuilder(this, title);
        SwingUtilities.invokeLater(builder);
    }

    /**
     * Class to construct a frame and display the grapher as its content pane from within the AWT
     * event thread.
     */
    private static class FrameBuilder implements Runnable {

        /** the <code>Grapher</code> whose panel is to be shown in the frame */
        private final Grapher grapher;

        /** the frame title */
        private final String frameTitle;

        /**
         * Constructs a new <code>FrameBuilder</code>.
         *
         * @param  grapherToShow  the <code>Grapher</code> whose panel is to be shown in the frame
         * @param  title          the frame title
         */
        protected FrameBuilder(final Grapher grapherToShow, final String title) {

            this.grapher = grapherToShow;
            this.frameTitle = title;
        }

        /**
         * Builds the frame and installs the <code>Grapher</code>'s panel is its content pane.
         */
        public void run() {

            JFrame frame;
            JLabel label;

            frame = new JFrame(this.frameTitle);
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            label = new JLabel(new ImageIcon(this.grapher.getGraphImage().getImage()));
            frame.setContentPane(label);
            frame.pack();

            UIUtilities.positionFrame(frame, 0.5, 0.5);

            frame.setVisible(true);
        }
    }
}
