package nxm.sys.libg;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import javax.swing.JPanel;

import nxm.sys.inc.InternalUseOnly;
import nxm.sys.lib.Shell;

/** A class to run a graphical Midas Panel. This class is implemented using
    swing components while {@link MPanel} uses AWT. <br>
    <br>
    Additionally (since NeXtMidas 1.9.2) this class has the ability to act as a
    {@link MidasFrame} which is used to enable the creation of swing-based Java
    bean components (see {@link MidasBean}).

    NOTE: MJPanel does NOT currently support Java Serialization, even though it extends classes that support it.

    @see MWindow
    @see MPanel
    @see MidasBean

    @author  Jeff Schoen
 */
public class MJPanel extends JPanel implements MidasFrame {
  /** NOTE: this should be changed if the structure/fields of this class changes. */
  private static final long serialVersionUID = 2012021080311L; // for Serializable interface

  /** Message given when user tries to serialize an MJPanel object */
  @InternalUseOnly
  public static final String UNSERIALIZABLE_MJPANEL_OBJECT_MESSAGE =
      "nxm.sys.libg.MJPanel does NOT currently support Java Serialization";

  /** the MWindow associated with this panel */
  public transient MWindow MW;
  /** the title associated with this panel */
  private String title;
  /** Indicates if a refresh is needed */
  private boolean needrefresh=false;

  /** Instantiate a new MJPanel with a null layout. */
  public MJPanel() {
    this((String)null);
  }

  /** Instantiate a new MJPanel with the given registry name.
      @param name the registry name for this panel
   */
  public MJPanel (String name) {
    super();
    setLayout(null);
    setOpaque(true);
    setTitle(name);
  }

  /** Sets the title */
  void setTitle (String t) {  title = t; }  // package private only used in libg

  /** Gets the title */
  String getTitle () { return title; }      // package private only used in libg

  /** Sets the MWindow associated with this panel
      @param mw the new MWindow
   */
  @Override
  public void setMWindow (MWindow mw) {
    MW = mw;
    if (needrefresh) MW.process ("REFRESH",0,null);
    needrefresh = false;
  }

  @Override
  public boolean isOptimizedDrawingEnabled () {
    // Return true if we want it optimized, this needs to be set as a core io option
    // This will return false either way if it is a jlayeredpane.
    // Since NeXtMidas 3.9.0
    if (Shell.getMidasContext().io.getOptions().indexOf("EnableGraphicsOptimizationRequiringNoOverlappingPanes") > -1) {
      return true;
    }
    // Return false to prevent graphical issues with overlapping. (makes it like a jlayeredpane)
    return false;
  }

  @Override
  public MWindow getMWindow () {
    return MW;
  }

  @Override
  public void update (Graphics g) {
    paint(g);
  }

  @Override
  public void paintComponent (Graphics g) {
    if (MW == null) paintComponentBasic(g, title);
    else if (MW.status!=MWindow.INIT ) MW.paintComponent(g); // updated in NeXtMidas 3.9.0 for graphical timing issues
    else MW.process("PAINT",0, g); //This is only hit if we are in INIT state, this will change it to front.
  }

  /** Paints just a basic title on the component. This is used when waiting for
      it to attach.
      @param g    The Graphics object used for painting
      @param text The title
   */
  void paintComponentBasic (Graphics g, String text) {
    Dimension d = getSize();
    g.setClip(0, 0, d.width, d.height);
    g.clearRect(0, 0, d.width, d.height);
    g.drawRect(4, 4, d.width-8, d.height-8);
    if (text != null) g.drawString(text, 10, 20);
    needrefresh = true;
  }

// JC4: I don't think this is ever called... and it does not work for MBox...
//  /** Sets the bounds based on an arbitrary object.
//      @param obj The object which specified the bounds.
//   */
//  public void setBounds (Object obj) {
//    if (obj instanceof MBox) {
//      MBox pos = (MBox)obj;
//    }
//    else if(obj instanceof Rectangle) {
//      super.setBounds((Rectangle)obj);
//    }
//    else {
//      System.err.println("MJPanel: Can not set bounds based on a " + obj.getClass().getName());
//    }
//  }
//

  @Override
  public void setBounds (MBox pos) {
    setBounds(pos.x, pos.y, pos.w, pos.h);
  }

  @Override
  public void resizePanel () {
   // Do Nothing
  }

  @Override
  public void display (int mode) {
    // Do Nothing
  }

  @Override
  public void componentMoved (ComponentEvent e) {
    // Do nothing
  }

  @Override
  public void componentResized (ComponentEvent e) {
    // Do nothing
  }

  @Override
  public void componentShown (ComponentEvent e) {
    // Do nothing
  }

  @Override
  public void componentHidden (ComponentEvent e) {
    // Do nothing
  }

  @Override
  public void windowOpened (WindowEvent e) {
    if (MW != null) MW.process("SHOWN", 1);
  }

  @Override
  public void windowActivated (WindowEvent e) {
    // Do nothing
  }

  @Override
  public void windowDeactivated (WindowEvent e) {
    // Do nothing
  }

  @Override
  public void windowIconified (WindowEvent e) {
    if (MW != null) MW.process("SHOWN", -1);
  }

  @Override
  public void windowDeiconified (WindowEvent e) {
    repaint();
    if (MW!=null) MW.process("SHOWN",1);
  }

  @Override
  public void windowClosing (WindowEvent e) {
    if (MW != null) MW.process("EXIT",0);
  }

  @Override
  public void windowClosed (WindowEvent e) {
    if (MW != null) {
      for (int ic=0; ic<MW.panel.getComponentCount(); ic++) {
        Object sub = MW.panel.getComponent(ic);
        if (sub instanceof MPanel) {
          ((MPanel)sub).MW.process("SHOWN",-1);
        }
      }
    }
  }

  @Override
  public Graphics getGraphics() {
    Graphics g = super.getGraphics();
    // Workaround for a possible bug in JPanel
    if (g instanceof Graphics2D) {
      ((Graphics2D)g).setBackground(getBackground());
    }
    return g;
  }

  /** Overwrite default serialization for writeObject
   *  to throw an UnsupportedOperationException since Java Serialization currently not supported
   *
   *  @param oos Object Output Stream
   *  @throws IOException Exception thrown by writeObject
   */
  private void writeObject (ObjectOutputStream oos) throws IOException {
    throw new UnsupportedOperationException(UNSERIALIZABLE_MJPANEL_OBJECT_MESSAGE);
  }

  /** Overwrite default deserialization for readObject
   *  to throw an UnsupportedOperationException since Java Serialization currently not supported
   *
   *  @param ois Object Input Stream
   *  @throws ClassNotFoundException Exception thrown by readObject
   *  @throws IOException Exception thrown by readObject
   */
  private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException {
    throw new UnsupportedOperationException(UNSERIALIZABLE_MJPANEL_OBJECT_MESSAGE);
  }
}
