/*
 * $Id: HtmlChart.java 5827 2009-07-08 19:18:25Z uckelman $
 *
 * Copyright (c) 2000-2003 by Rodney Kinney
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License (LGPL) as published by the Free Software Foundation.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, copies are available
 * at http://www.opensource.org.
 */
package VASSAL.build.widget;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.ComponentView;
import javax.swing.text.Element;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;

import VASSAL.build.BadDataReport;
import VASSAL.build.Buildable;
import VASSAL.build.GameModule;
import VASSAL.build.Widget;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.i18n.Resources;
import VASSAL.tools.ErrorDialog;
import VASSAL.tools.ReadErrorDialog;
import VASSAL.tools.ScrollPane;
import VASSAL.tools.imageop.Op;
import VASSAL.tools.imageop.OpIcon;
import VASSAL.tools.imageop.SourceOp;
import VASSAL.tools.io.IOUtils;

/**
 * An HtmlChart is used for displaying html information for the module. The
 * charts are loaded as html files stored in the DataArchive. As a subclass of
 * Widget, a Chart may be added to any Widget, but it may not contain children
 * of its own
 */
public class HtmlChart extends Widget implements MouseListener {
  public static final String NAME = "chartName";
  public static final String FILE = "fileName";

  private String fileName;
  private JScrollPane scroller;
  private JEditorPane htmlWin;

  public HtmlChart() {
  }

  private boolean isURL() {
    return htmlWin.getDocument().getProperty("stream") != null;
  }

  private void setText(String text) {
    htmlWin.setText(text);
    // ensure hyperlink engine knows we are no longer at the last URL
    htmlWin.getDocument().putProperty("stream", null);
    htmlWin.revalidate();
  }

  private void setFile(String fname) {
    setText(getFile(fname));
  }

  private String getFile(final String fname) {
    if (fname == null) return null;

    String s = null;
    InputStream in = null;
    try {
      in = new BufferedInputStream(
        GameModule.getGameModule().getDataArchive().getInputStream(fname));
      s =  IOUtils.toString(in);
      in.close();
    }
    catch (IOException e) {
      ErrorDialog.dataError(new BadDataReport(this, Resources.getString("Error.not_found", "Chart"),fname,e));
    }
    finally {
      IOUtils.closeQuietly(in);
    }

    return s;
  }

  // Warning: Creating a JEditorPane with a "jar" url or using setPage()
  // with a jar URL will leave a resource open in the MOD file, making it
  // impossible to save or rename it. This might be acceptable for people
  // playing a module, but is unacceptable for editors; they can only save
  // their work to a new file. Therefore, we read the entire file instead
  // of simply using:
  //    GameModule.getGameModule().getDataArchive().getURL( fileName );
  public Component getComponent() {
    if (htmlWin == null) {
      htmlWin = new JEditorPane();
      htmlWin.setEditable(false);
      htmlWin.setContentType("text/html");
      XTMLEditorKit myHTMLEditorKit = new XTMLEditorKit();
      htmlWin.setEditorKit(myHTMLEditorKit);

      htmlWin.addHyperlinkListener(new HtmlChartHyperlinkListener());
      htmlWin.addMouseListener(this);

      setFile(fileName);

      scroller = new ScrollPane(htmlWin);
      scroller.getViewport().setPreferredSize(htmlWin.getPreferredSize());
      scroller.getViewport().setAlignmentY(0.0F);
    }
    return scroller;
  }

  public String getFileName() {
    return fileName;
  }

  public void addTo(Buildable parent) {
  }

  public static String getConfigureTypeName() {
    return "HTML Chart";
  }

  public void removeFrom(Buildable parent) {
  }

  public HelpFile getHelpFile() {
    return HelpFile.getReferenceManualPage("ChartWindow.htm", "HtmlChart");
  }

  public void setAttribute(String key, Object val) {
    if (NAME.equals(key)) {
      setConfigureName((String) val);
    }
    else if (FILE.equals(key)) {
      if (val instanceof File) {
        val = ((File) val).getName();
      }
      fileName = (String) val;
      if (htmlWin != null) {
        setFile(fileName);
      }
    }
  }

  public Class<?>[] getAllowableConfigureComponents() {
    return new Class[0];
  }

  /**
   * The Attributes of a Chart are:
   * <dl>
   *  <dt><code>NAME</code></dt><dd>for the name of the chart</dd>
   *  <dt><code>FILE</code></dt><dd>for the name of the HTML file
   *  in the {@link VASSAL.tools.DataArchive}</dd>
   * </dl>
   */
  public String[] getAttributeNames() {
    return new String[]{NAME, FILE};
  }

  public String[] getAttributeDescriptions() {
    return new String[]{"Name:  ", "HTML File:  "};
  }

  public Class<?>[] getAttributeTypes() {
    return new Class<?>[]{String.class, File.class};
  }

  public String getAttributeValueString(String name) {
    if (NAME.equals(name)) {
      return getConfigureName();
    }
    else if (FILE.equals(name)) {
      return fileName;
    }
    return null;
  }

  public void mousePressed(MouseEvent event) {
    if (event.isMetaDown()) {
      final JPopupMenu popup = new JPopupMenu();
      final JMenuItem item = new JMenuItem("Return to default page");

      item.addActionListener(new ActionListener() {
        // Return to default page
        public void actionPerformed(ActionEvent e) {
          setFile(fileName);
        }
      });

      popup.add(item);
      if (event.getComponent().isShowing()) {
        popup.show(event.getComponent(), event.getX(), event.getY());
      }
    }
  }

  public void mouseClicked(MouseEvent e) {
  }

  public void mouseEntered(MouseEvent e) {
  }

  public void mouseExited(MouseEvent e) {
  }

  public void mouseReleased(MouseEvent e) {
  }

  public class HtmlChartHyperlinkListener implements HyperlinkListener {
    public void hyperlinkUpdate(HyperlinkEvent event) {
      if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
        if ((!isURL() && event.getDescription().indexOf("/") < 0) ||
            event.getURL() == null) {
          setFile(event.getDescription());
        }
        else {
          try {
            htmlWin.setPage(event.getURL());
          }
          catch (IOException ex) {
            ReadErrorDialog.error(ex, event.getURL().toString());
          }
          htmlWin.revalidate();
        }
      }
    }
  }

  /**
   * Extended HTML Editor kit to extend the <src> tag to display images
   * from the module DataArchive where no pathname included in the image name.
   * The image is placed on a label and returned as a ComponentView. An
   * ImageView cannot be used as the standard Java HTML Renderer can only
   * display Images from an external URL.
   */
  public class XTMLEditorKit extends HTMLEditorKit {
    private static final long serialVersionUID = 1L;

    public ViewFactory getViewFactory() {
      return new XTMLFactory();
    }

    public class XTMLFactory extends HTMLFactory implements ViewFactory {

      public XTMLFactory() {
        super();
      }

      public View create(javax.swing.text.Element element) {
        final HTML.Tag kind = (HTML.Tag) (element.getAttributes().getAttribute(javax.swing.text.StyleConstants.NameAttribute));

        if (kind instanceof HTML.Tag && element.getName().equals("img")) {
          final String imageName = (String) element.getAttributes().getAttribute(HTML.Attribute.SRC);
          if (imageName.indexOf("/") < 0) {
            return new ImageComponentView(element);
          }
        }
        return super.create(element);
      }

      public class ImageComponentView extends ComponentView {
        protected String imageName;
        protected SourceOp srcOp;

        /**
         * Very basic Attribute handling only. Expand as needed.
         */
        public ImageComponentView(Element e) {
          super(e);
          imageName = (String) e.getAttributes()
                                .getAttribute(HTML.Attribute.SRC);
          srcOp = imageName == null || imageName.trim().length() == 0
                ? null : Op.load(imageName);
        }

        protected Component createComponent() {
          final JLabel label = new JLabel();
          label.setIcon(new OpIcon(srcOp));
          return label;
        }
      }
    }
  }
}
