/*
 *    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.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * ScriptingPanel.java
 * Copyright (C) 2009 University of Waikato, Hamilton, New Zealand
 */

package weka.gui.scripting;

import weka.core.Tee;
import weka.gui.PropertyDialog;
import weka.gui.ReaderToTextPane;
import weka.gui.scripting.event.TitleUpdatedEvent;
import weka.gui.scripting.event.TitleUpdatedListener;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.io.File;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.util.HashSet;
import java.util.Iterator;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JTextPane;

/**
 * Abstract ancestor for scripting panels.
 * 
 * @author  fracpete (fracpete at waikato dot ac dot nz)
 * @version $Revision: 5142 $
 */
public abstract class ScriptingPanel
  extends JPanel
  implements TitleUpdatedListener {

  /** for serialization. */
  private static final long serialVersionUID = 7593091442691911406L;

  /** The new output stream for System.out. */
  protected PipedOutputStream m_POO;

  /** The new output stream for System.err. */
  protected PipedOutputStream m_POE;

  /** The thread that sends output from m_POO to the output box. */
  protected ReaderToTextPane m_OutRedirector;

  /** The thread that sends output from m_POE to the output box. */
  protected ReaderToTextPane m_ErrRedirector;
  
  /** whether debug mode is on. */
  protected boolean m_Debug;
  
  /** the listeners for the changes in the title. */
  protected HashSet<TitleUpdatedListener> m_TitleUpdatedListeners;

  /**
   * Default constructor.
   */
  public ScriptingPanel() {
    super();
    
    initialize();
    initGUI();
    initFinish();
  }
  
  /**
   * For initializing member variables.
   */
  protected void initialize() {
    m_POO            = new PipedOutputStream();
    m_POE            = new PipedOutputStream();
    m_Debug          = false;
    m_TitleUpdatedListeners = new HashSet<TitleUpdatedListener>();
  }
  
  /**
   * Sets up the GUI after initializing the members.
   * The JTextArea returned via <code>getOutputArea()</code> must be setup here.
   * 
   * @see	#initialize()
   * @see	#getOutput()
   */
  protected void initGUI() {
  }
  
  /**
   * Finishes up after initializing members and setting up the GUI.
   * Redirects stdout and stderr using <code>getOutputArea()</code>.
   * 
   * @see	#initialize()
   * @see	#initGUI()
   * @see	#getOutput()
   */
  protected void initFinish() {
    // Redirect System.out to the text area
    try {
      PipedInputStream pio = new PipedInputStream(m_POO);
      Tee teeOut = new Tee(System.out);
      System.setOut(teeOut);
      teeOut.add(new PrintStream(m_POO));
      Reader reader = new InputStreamReader(pio);
      m_OutRedirector = new ReaderToTextPane(reader, getOutput(), Color.BLACK);
      m_OutRedirector.start();
    }
    catch (Exception e) {
      System.err.println("Error redirecting stdout");
      e.printStackTrace();
      m_OutRedirector = null;
    }

    // Redirect System.err to the text area
    try {
      PipedInputStream pie = new PipedInputStream(m_POE);
      Tee teeErr = new Tee(System.err);
      System.setErr(teeErr);
      teeErr.add(new PrintStream(m_POE));
      Reader reader = new InputStreamReader(pie);
      m_ErrRedirector = new ReaderToTextPane(reader, getOutput(), Color.RED);
      m_ErrRedirector.start();
    }
    catch (Exception e) {
      System.err.println("Error redirecting stderr");
      e.printStackTrace();
      m_ErrRedirector = null;
    }

    addTitleUpdatedListener(this);
  }
  
  /**
   * Returns an icon to be used in a frame.
   * 
   * @return		the icon
   */
  public abstract ImageIcon getIcon();
  
  /**
   * Returns the current title for the frame/dialog.
   * 
   * @return		the title
   */
  public abstract String getTitle();
  
  /**
   * Returns the text area that is used for displaying output on stdout
   * and stderr.
   * 
   * @return		the JTextArea
   */
  public abstract JTextPane getOutput();
  
  /**
   * Returns the menu bar to to be displayed in the frame.
   * 
   * @return		the menu bar, null if not applicable
   */
  public abstract JMenuBar getMenuBar();
  
  /**
   * Turns on/off debugging mode.
   * 
   * @param value	if true, debug mode is turned on
   */
  public void setDebug(boolean value) {
    m_Debug = value;
  }
  
  /**
   * Returns whether debugging mode is on.
   * 
   * @return		true if debug mode is turned on
   */
  public boolean getDebug() {
    return m_Debug;
  }
  
  /**
   * Adds the listener to the internal list.
   * 
   * @param l		the listener to add
   */
  public void addTitleUpdatedListener(TitleUpdatedListener l) {
    m_TitleUpdatedListeners.add(l);
  }
  
  /**
   * Removes the listener from the internal list.
   * 
   * @param l		the listener to remove
   */
  public void removeTitleUpdatedListener(TitleUpdatedListener l) {
    m_TitleUpdatedListeners.remove(l);
  }
  
  /**
   * Sends the event to all listeners for title updates.
   * 
   * @param e		the event to send
   */
  protected void notifyTitleUpdatedListeners(TitleUpdatedEvent e) {
    Iterator<TitleUpdatedListener>	iter;
    
    iter = m_TitleUpdatedListeners.iterator();
    while (iter.hasNext())
      iter.next().titleUpdated(e);
  }
  
  /**
   * Gets called when the title of the frame/dialog needs updating.
   * 
   * @param event	the event that got sent
   */
  public void titleUpdated(TitleUpdatedEvent event) {
    if (PropertyDialog.getParentDialog(ScriptingPanel.this) != null)
      PropertyDialog.getParentDialog(ScriptingPanel.this).setTitle(getTitle());
    else if (PropertyDialog.getParentFrame(ScriptingPanel.this) != null)
      PropertyDialog.getParentFrame(ScriptingPanel.this).setTitle(getTitle());
  }
  
  /**
   * Displays the panel in a frame.
   * 
   * @param panel	the panel to display
   * @param args	currently ignored commandline parameters
   */
  public static void showPanel(ScriptingPanel panel, String[] args) {
    showPanel(panel, args, 800, 600);
  }
  
  /**
   * Displays the panel in a frame.
   * 
   * @param panel	the panel to display
   * @param args	currently ignored commandline parameters
   * @param width	the width of the frame
   * @param height	the height of the frame
   */
  public static void showPanel(ScriptingPanel panel, String[] args, int width, int height) {
    try {
      JFrame frame = new JFrame();
      frame.getContentPane().setLayout(new BorderLayout());
      frame.getContentPane().add(panel, BorderLayout.CENTER);
      frame.setJMenuBar(panel.getMenuBar());
      frame.setSize(new Dimension(width, height));
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setTitle(panel.getTitle());
      frame.setIconImage(panel.getIcon().getImage());
      frame.setLocationRelativeTo(null);
      if ((args.length > 0) && (panel instanceof FileScriptingPanel))
	((FileScriptingPanel) panel).open(new File(args[0]));
      frame.setVisible(true);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}
