/*
 * Plotter.java
 *
 * Created on January 22, 2002, 3:08 PM
 */

package hep.aida.ref.plotter;

import hep.aida.*;
import hep.aida.ITitleStyle;
import hep.aida.ref.plotter.TitleStyle;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.ArrayList;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import org.freehep.application.PropertyUtilities;
import org.freehep.swing.Headless;
import org.freehep.util.export.ExportFileType;

/**
 * A dummy implementation of an AIDA Plotter, useful in batch jobs where no
 * graphical output is required.
 * @author tonyj
 * @version $Id: DummyPlotter.java,v 1.7 2005/03/15 15:19:11 turri Exp $
 */
public class DummyPlotter implements IPlotter {
    
    private ArrayList parameters  = new ArrayList();
    private ArrayList parOptions  = new ArrayList();
    private ArrayList parValues   = new ArrayList();
    private ArrayList parDefaults = new ArrayList();

    private ITitleStyle titleStyle = new TitleStyle();
    private static final String[] emptyArray = new String[0];
    private List regions = new ArrayList();
    private int currentRegion = 0;
    
    static final int DEFAULT_WIDTH = 600;
    static final int DEFAULT_HEIGHT = 600;
    
    private boolean warned = false;
    
    protected DummyPlotter() {
        initializeParameters();
    }
    DummyPlotter(String name) {
        initializeParameters();
        createRegion();
    }
    
    private void initializeParameters() {
        addParameter("plotterWidth",String.valueOf(DEFAULT_WIDTH));
        addParameter("plotterHeight",String.valueOf(DEFAULT_HEIGHT));
    }
    
    protected IPlotterRegion justCreateRegion(double x, double y, double width, double height) {
        return new DummyPlotterRegion(x,y,width,height);
    }
    public void show() {
        System.err.println("Warning: Dummy plotter used, no plot will appear");
    }
    public void hide() {
    }
    public void refresh() {
    }
    public void interact() {
    }
    public IPlotterRegion next() {
        return (IPlotterRegion) regions.get(++currentRegion);
    }
    public IPlotterRegion createRegion(double x) {
        return createRegion(x,0,1-x,1);
    }
    public IPlotterRegion createRegion() {
        return createRegion(0,0,1,1);
    }
    public IPlotterRegion createRegion(double x, double y) {
        return createRegion(x,y,1-x,1-y);
    }
    public IPlotterRegion createRegion(double x, double y, double w) {
        return createRegion(x,y,w,1-y);
    }
    
    public IPlotterRegion createRegion(double x, double y, double w, double h) {
        if (x<0 || x>1) throw new IllegalArgumentException("x");
        if (y<0 || y>1) throw new IllegalArgumentException("y");
        if (w<0 || x+w>1) throw new IllegalArgumentException("w");
        if (h<0 || y+h>1) throw new IllegalArgumentException("h");
        IPlotterRegion region = justCreateRegion(x,y,w,h);
        regions.add(region);
        return region;
    }
    public IPlotterRegion currentRegion() {
        return (IPlotterRegion) regions.get(currentRegion);
    }
    public void createRegions() {
        createRegions(1,1,0);
    }
    public void createRegions(int columns) {
        createRegions(columns,1,0);
    }
    public void createRegions(int columns, int rows) {
        createRegions(columns,rows,0);
    }
    public void createRegions(int columns, int rows, int start) {
        if (columns <= 0) throw new IllegalArgumentException("columns");
        if (rows <= 0) throw new IllegalArgumentException("rows");
        if (start < 0 || start >= rows*columns) throw new IllegalArgumentException("start");
        
        // Does this destroy current regions?
        destroyRegions();
        
        double width = 1./columns;
        double height = 1./rows;
        
        for (int i=0; i<columns; i++) {
            for (int j=0; j<rows; j++) {
                regions.add(justCreateRegion(i*width,j*height,width,height));
            }
        }
        
        currentRegion = start;
    }
    public void setCurrentRegionNumber(int value) {
        if (value < 0 || value >= regions.size()) throw new IllegalArgumentException();
        currentRegion = value;
    }
    public int currentRegionNumber() {
        return currentRegion;
    }
    public IPlotterRegion region(int index) {
        if (index < 0 || index >= regions.size()) throw new IllegalArgumentException();
        return (IPlotterRegion) regions.get(index);
    }
    
    public void destroyRegions() {
        regions.clear();
    }
    
    public void clearRegions() {
        for (Iterator i = regions.iterator(); i.hasNext(); ) {
            ((IPlotterRegion) i.next()).clear();
        }
    }
    
    public String[] availableParameterOptions(String parameterName) {
        int parIndex = parameterIndex( parameterName );
        Object obj = parOptions.get(parIndex);
        return obj == null ? emptyArray : (String[]) obj;
    }
    
    public String[] availableParameters() {
        int size = parameters.size();
        String[] pars = new String[ size ];
        for ( int i = 0; i < size; i++ )
            pars[i] = (String) parameters.get(i);
        return pars;
    }
    
    public String parameterValue(String parameterName) {
        int parIndex = parameterIndex( parameterName );
        return (String) parValues.get(parIndex);
    }
    
    private int parameterIndex( String parameterName ) {
        for ( int i = 0; i < parameters.size(); i++ )
            if ( ( (String) parameters.get(i) ).toLowerCase().equals( parameterName.toLowerCase() ) )
                return i;
        throw new IllegalArgumentException("Parameter "+parameterName+" is not a plotter parameter.");
    }
        
    public void setParameter(String name) {
        setParameter(name,null);
    }
    
    public void setParameter(String name, String value) {
        int parIndex = parameterIndex( name );
        if ( value == null )
            parValues.set(parIndex,parDefaults.get(parIndex));
        else
            parValues.set(parIndex,value);        
    }
    
    public int numberOfRegions() {
        return regions.size();
    }
    
    public hep.aida.ITitleStyle titleStyle() {
        return titleStyle;
    }
    
    public void setTitle(String str) {
    }
        
    public void setTitleStyle(hep.aida.ITitleStyle iTitleStyle) {
        this.titleStyle = iTitleStyle;
    }
    
    public void writeToFile(String file) throws IOException {
        writeToFile(file, null);
    }
    
    public void writeToFile(String file, String type) throws IOException {
        System.err.println("Warning: Dummy plotter used, no file will be written");
    }
    
    public void writeToFile(String fileName, String fileType, Properties props) throws IOException {
        if (fileType == null) {
            int pos = fileName.lastIndexOf('.');
            if (pos > 0) fileType = fileName.substring(pos+1);
        }
        OutputStream out = new BufferedOutputStream(new FileOutputStream(fileName));
        writeToFile(out,fileType,props, true);
    }
    
    void writeToFile(OutputStream out, String type, Properties props) throws IOException {
        writeToFile(out, type, props, false);
    }

    void writeToFile(OutputStream out, String type, Properties props, boolean closeOut) throws IOException {
        int delay = 0;
        if ( isShowing() ) {
            delay = 1500;
            if ( ! warned ) {
                warned = true;
                System.out.println("There might be a synchronization problem when the plotter is being shown and it is written to file.\n" +
                "Please refer to the JAIDA release notes for more details: http://java.freehep.org/jaida.\n" +
                "To avoid this problem don't invoke the show() method on the Plotter when writing to file.");
            }
        }
        WriteToFile writeToFile = new WriteToFile( panel(), isShowing(), out, type, props, closeOut );
        
        if ( isShowing() ) {
            WriteToFileActionListener actionListener = new WriteToFileActionListener(writeToFile, isShowing());
        
            Timer timer = new Timer(delay,actionListener);
            timer.setRepeats(false);
            timer.start();
        } else {
            writeToFile.run();
        }
    }
    
    public boolean isShowing() {
        return false;
    }
    
    public JPanel panel() {
        return null;
    }
    
    protected int plotterWidth() {
        return Integer.parseInt(parameterValue("plotterWidth"));
    }
    
    protected int plotterHeight() {
        return Integer.parseInt(parameterValue("plotterHeight"));
    }

    public static void invokeOnSwingThread(Runnable run) {
        try {
            if (SwingUtilities.isEventDispatchThread()) run.run();
            else SwingUtilities.invokeAndWait(run);
        }
        catch (java.lang.reflect.InvocationTargetException x) {
            x.printStackTrace();
        }
        catch (InterruptedException x) {
            x.printStackTrace();
        }
    }
    
    private class WriteToFileActionListener implements ActionListener {
        
        private WriteToFile writeToFile;
        private boolean isShowing;
        
        WriteToFileActionListener( WriteToFile writeToFile, boolean isShowing ) {
            this.writeToFile = writeToFile;
            this.isShowing = isShowing;
        }
        
        public void actionPerformed(ActionEvent e) {
            if ( isShowing )
                invokeOnSwingThread(writeToFile);
            else
                writeToFile.run();
        }
    }
    
    private class WriteToFile implements Runnable {
        
        private JPanel panel;
        private boolean isShowing;
        private OutputStream out;
        private String fileType;
        private Properties props;
        private boolean closeOut;
        
        WriteToFile(JPanel panel, boolean showing, OutputStream out, String type, Properties props, boolean closeOut) {
            this.panel = panel;
            isShowing = showing;
            this.out = out;
            fileType = type;
            this.props = props;
        }
        
        public void run() {
            Headless headLess = null;
            if (!isShowing) {
                // OK, this actually has to do something!
                headLess = new Headless(panel);
                int width = props == null ? DEFAULT_WIDTH : PropertyUtilities.getInteger(props,"plotWidth",DEFAULT_WIDTH);
                int height = props == null ? DEFAULT_HEIGHT : PropertyUtilities.getInteger(props,"plotHeight",DEFAULT_HEIGHT);
                panel.setPreferredSize(new Dimension(width,height));
                headLess.pack();
                headLess.setVisible(true);
            }
            
            try {
                List types = ExportFileType.getExportFileTypes(fileType);
                if (types == null || types.size() == 0) throw new IllegalArgumentException("Unsupported file type: "+fileType);
                ExportFileType fType = (ExportFileType) types.get(0);
                try {
                    fType.exportToFile(out, panel, null, props, "AIDA");
                } catch (IOException e) {
                    throw new RuntimeException(e.getMessage());
                }
            } finally {
                if (headLess != null) headLess.dispose();
            }
            if ( closeOut )
                try {
                    out.close();
                } catch (IOException ioe) {
                    throw new RuntimeException(ioe.getMessage());
                }
        }
    }
    
    protected void addParameter( String parameterName ) {
        addParameter( parameterName, null, null );
    }

    protected void addParameter( String parameterName, String defaultValue ) {
        addParameter( parameterName, null, defaultValue );
    }

    protected void addParameter( String parameterName, String[] options ) {
        addParameter( parameterName, options, null );
    }

    protected void addParameter( String parameterName, String[] options, String defaultValue ) {
        try {
            parameterIndex(parameterName);
            throw new IllegalArgumentException("Parameter "+parameterName+" already belongs to this BaseStyle");
        } catch (IllegalArgumentException iae) {
            parameters.add(parameterName);
            parOptions.add(options);
            parDefaults.add(defaultValue);
            parValues.add(defaultValue);
        }
    }
}