/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Simulate.java
 * Input/output tool: superclass for simulation-output formats that display their results in a waveform window.
 * Written by Steven M. Rubin, Sun Microsystems.
 *
 * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
 *
 * Electric(tm) 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 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.tool.io.input;

import com.sun.electric.database.Environment;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.UserInterfaceExec;
import com.sun.electric.tool.io.FileType;
import com.sun.electric.tool.io.input.verilog.VerilogOut;
import com.sun.electric.tool.simulation.SimulationTool;
import com.sun.electric.tool.simulation.Stimuli;
import com.sun.electric.tool.user.waveform.WaveformWindow;
import com.sun.electric.util.TextUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

import javax.swing.SwingUtilities;

/**
 * This class reads simulation output files and plots them.
 */
public final class SimulationData {

	private SimulationData() {}

    private static final String[] known_extensions = new String[]
        { 

          // Steve has specifically requested that tr0/ac0 files have top
          // priority (Bug #2815)
          "tr0", "ac0", 
          
          "vcd", "raw", "dump", "spo",

          // it's important that "out" and "txt" appear last because
          // they sometimes are present yet do not contain simulation
          // data (although in such situations the user should not
          // be using "plot from guessed" anyways)
          "out", "txt" };

    public static boolean isKnownSimulationFormatExtension(String extension) {
        return getInputForExtension(extension)!=null;
    }

	/**
	 * Based on the provided cell, make an educated guess
	 * about which simulation file the user has in mind, and plot
	 * that.  If WaveformWindow is null, one will be created.
	 */
	public static void plotGuessed(Cell cell, WaveformWindow ww) {
        if (cell==null) return;
        String[] paths = new String[] {
            FileType.SPICE.getGroupPath(),
            TextUtils.getFilePath(cell.getLibrary().getLibFile())
        };
        for (String path : paths)
            for (String ext : known_extensions)
                if (new File(path, cell.getName()+'.'+ext).exists()) {
                    plot(cell, TextUtils.makeURLToFile(new File(path, cell.getName()+'.'+ext).getPath()), ww);
                    return;
                }
        System.out.println("unable to guess any simulation file with a known extension; the following directories were checked: ");
        for(String path : paths)
            System.out.println("  " + path);
	}

	/**
	 * Plot the simulation data for cell found at url in ww.
     * If cell is null, no cell will be associated with the simulation data (crossprobing disabled).
     * If ww is null, a waveform window will be created.
	 */
	public static void plot(Cell cell, URL url, WaveformWindow ww) {
        new ReadSimulationOutput(cell, url, ww).start();
    }

    private static Input<Stimuli> getInputForExtension(String extension) {
        if (extension.indexOf('.') != -1)
            extension = extension.substring(extension.lastIndexOf('.')+1);
        if (extension.equals("dump") || extension.equals("vcd")) return new VerilogOut();
        if (extension.equals("txt")) return new PSpiceOut();
        if (extension.equals("raw")) return new RawSpiceOut();
        if (extension.equals("spo")) return new SpiceOut();
        if (extension.equals("out")) return new EpicOut();
        if (extension.startsWith("tr") || extension.startsWith("sw") || extension.startsWith("ic") ||
            extension.startsWith("ac") || extension.startsWith("mt") || extension.startsWith("pa"))
            	return new HSpiceOut();
        return null;
    }

	/**
	 * Class to read simulation output in a new thread.
	 */
	private static class ReadSimulationOutput extends Thread {
		private Input<Stimuli> is;
		private URL fileURL;
		private Cell cell;
		private WaveformWindow ww;
        private Stimuli sd;
        private final Environment launcherEnvironment;
        private final UserInterfaceExec userInterface;
        private String netDelimeter;

		private ReadSimulationOutput(Cell cell, URL fileURL, WaveformWindow ww) {
			this.fileURL = fileURL;
			this.cell = cell;
			this.ww = ww;
            this.is = getInputForExtension(fileURL.getPath());
            this.netDelimeter = SimulationTool.getSpiceExtractedNetDelimiter();
            if (this.is==null) throw new RuntimeException("unable to detect type");

            launcherEnvironment = Environment.getThreadEnvironment();
            userInterface = new UserInterfaceExec();
		}

		public void run() {
            if (is == null) return;
            if (Thread.currentThread() == this) {
                Environment.setThreadEnvironment(launcherEnvironment);
                Job.setUserInterface(userInterface);
            }
			try {
                sd = new Stimuli();
                sd.setNetDelimiter(netDelimeter);
                sd.setCell(cell);
                is.processInput(fileURL, cell, sd);
                if (sd == null) return;
                sd.setFileURL(fileURL);
                final Stimuli sdx = sd;
                assert cell.getDatabase() == EDatabase.clientDatabase();
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        if (ww == null)
                            WaveformWindow.showSimulationDataInNewWindow(sdx);
                        else
                            WaveformWindow.refreshSimulationData(sdx, ReadSimulationOutput.this.ww);
                    }});
			} catch (IOException e) {
				System.out.println("Error reading: " + fileURL + " (" + e.getMessage() + ")");
			}
		}
	}

	public static Stimuli processInput(Cell cell, URL url) {
        ReadSimulationOutput job = new ReadSimulationOutput(cell, url, null);
        job.run();
        return job.sd;
    }

	public static Stimuli processInput(Cell cell, URL url, String netDelimeter) {
 		Input<Stimuli> is = getInputForExtension(url.getPath());
        if (is==null) throw new RuntimeException("unable to detect type");
        Stimuli sd = new Stimuli();
        sd.setNetDelimiter(netDelimeter);
        sd.setCell(cell);
        try {
            is.processInput(url, cell, sd);
        } catch (IOException e) {
			System.out.println("End of file reached while reading " + url);
            return null;
        }
        sd.setFileURL(url);
        return sd;
    }
}
