/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Formatter;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;

import ilog.views.IlvGraphic;
import ilog.views.IlvManagerLayer;
import ilog.views.IlvManagerView;
import ilog.views.IlvPoint;
import ilog.views.interactor.IlvSelectInteractor;
import ilog.views.maps.IlvAttributeProperty;
import ilog.views.maps.IlvMapInputStream;
import ilog.views.maps.IlvMapScaleLimiter;
import ilog.views.maps.attribute.IlvDoubleAttribute;
import ilog.views.maps.beans.IlvLayerTree;
import ilog.views.maps.beans.IlvMapLayer;
import ilog.views.maps.beans.IlvScaleBar;
import ilog.views.maps.graphic.style.IlvMapStyle;
import ilog.views.maps.label.IlvMapLabelFactory;
import ilog.views.maps.label.IlvMapLabeler;
import ilog.views.maps.label.IlvMapLabelerProperty;
import ilog.views.maps.label.IlvMapLabelingLabel;
import ilog.views.util.IlvImageUtil;
import shared.BaseDemo;
import shared.MapBuilder;
import shared.ui.AdvancedTabbedPane;

/**
 * This example shows how to extend or restrict the map builder
 * 
 * @since JViews 7.5
 */
public class ExtensionDemo extends MapBuilder {

  /**
   * Specific labelling factory to show both the currently selected labelling
   * property and "POP" if it exists.
   * 
   * @since JViews 8.1
   */
  static class MyLabelFactory implements IlvMapLabelFactory {
    private final IlvMapLabeler labeler;

    private MyLabelFactory(IlvMapLabeler labeler) {
      this.labeler = labeler;
    }

    Override
    public IlvMapLabelingLabel[] getGisLabel(IlvGraphic comp) {
      IlvManagerView pview = labeler.getView();
      if (pview == null)
        return null;
      IlvAttributeProperty properties = (IlvAttributeProperty) comp.getNamedProperty(IlvAttributeProperty.NAME);
      // find the selected "labeling" attribute
      IlvManagerLayer layer = pview.getManager().getManagerLayer(comp);
      IlvMapLayer mLayer = (layer != null) ? IlvMapLayer.get(layer) : null;
      IlvMapStyle style = (mLayer != null) ? mLayer.getStyle() : null;
      String attributeName = (style != null) ? style.getLabelAttribute() : null;
      String message = ""; //$NON-NLS-1$
      if (properties != null) {
        Object o = null;
        if (attributeName != null && !attributeName.equals("POP")) {//$NON-NLS-1$
          try {
            o = properties.getValue(attributeName);
          } catch (IllegalArgumentException e) {
            // Attribute not found
          }
          if (o != null)
            message += o.toString();
        }
        try {
          o = properties.getValue("POP");//$NON-NLS-1$
        } catch (IllegalArgumentException e) {
          // Attribute not found
        }
        // show and format population
        if (o instanceof IlvDoubleAttribute) {
          Formatter fmt = new Formatter();
          fmt.format(" [Pop %,.0f]", Double.valueOf(((IlvDoubleAttribute) o).getValue()));//$NON-NLS-1$
          message += fmt.toString();
          fmt.close();
        }
      }
      if (message.length() != 0) {
        return labeler.createLabels(comp, message);
      }
      return null;
    }
  }

  /* Specific extension view */
  JPanel myView;
  /* sample additional menu */
  JMenu myMenu;
  /* sample additional menu item */
  JMenuItem myAction;
  /* sample additional popup menu item */
  JMenuItem myPopupAction;
  /* sample additional menu on the layer tree */
  JMenuItem myLayerMenu;
  /* sample additional tabbed pane on the right */
  AdvancedTabbedPane myTabbedPane;


  /**
   * Overridden method to change the menu buttons available to the user
   * 
   */
  public ExtensionDemo() {
    super();
    // Make sure the swing construction is called in Swing event thread.
    ilog.views.util.swing.IlvSwingUtil.invokeAndWait(new Runnable() {
      Override
      public void run() {
        try {
          /*
           * MapBuilder defines the following option menu buttons:
           * tripleBuffered antiAliased showToolTips showGUIToolTips
           * advancedProperties selectSystem additionalViews
           * 
           * MapBuilder defines the following file menu buttons: importBtn
           * lastFilesMenu loadBtn saveBtn clearBtn exportBtn exitBtn
           */
          // hide some buttons
          lastFilesMenu.setVisible(false);
          loadBtn.setVisible(false);
          saveBtn.setVisible(false);
          clearBtn.setVisible(false);
          exportBtn.setVisible(false);
          tripleBuffered.setVisible(false);

          // hide the option menu.
          optionMenu.setVisible(false);

          // add a specific menu.
          myMenu = new JMenu("More"); //$NON-NLS-1$ should be externalized, but
                                      // lets keep code simple.
          myAction = new JMenuItem("My Action...");//$NON-NLS-1$ should be
                                                   // externalized, but lets
                                                   // keep code simple.
          myMenu.add(myAction);
          myAction.addActionListener(new ActionListener() {
            Override
            public void actionPerformed(ActionEvent e) {
              JOptionPane.showMessageDialog(view, "My Action executed");//$NON-NLS-1$ should
                                                                        // be
                                                                        // externalized,
                                                                        // but
                                                                        // lets
                                                                        // keep
                                                                        // code
                                                                        // simple.
            }
          });
          getJMenuBar().add(myMenu);
          IlvMapScaleLimiter limiter = new IlvMapScaleLimiter((float) (1 / 1E2), (float) (1 / 1E9));
          limiter.setView(view);
          // Add a listener on the view to see which graphical object is clicked
          view.addMouseListener(new MouseAdapter() {
            Override
            public void mouseReleased(MouseEvent event) {
              if (event.isPopupTrigger()) {
                return;
              }
              if (getView().getInteractor() == null || getView().getInteractor() instanceof IlvSelectInteractor) {
                Point pt = event.getPoint();
                // Find component located at point pt
                IlvGraphic graphic = getView().getManager().getObject(new IlvPoint(pt.x, pt.y), getView());
                if (graphic != null) {
                  JOptionPane.showMessageDialog(getView(), "Clicked on " + graphic);//$NON-NLS-1$ should
                                                                                    // be
                                                                                    // externalized,
                                                                                    // but
                                                                                    // lets
                                                                                    // keep
                                                                                    // code
                                                                                    // simple.
                }
              }
            }
          });
          myPopupAction = new JMenuItem("My Popup action...");//$NON-NLS-1$ should
                                                              // be
                                                              // externalized,
                                                              // but lets keep
                                                              // code simple.
          // add a menu to the popup menu.
          viewPopupMenu.add(myPopupAction);
          myPopupAction.addActionListener(new ActionListener() {
            Override
            public void actionPerformed(ActionEvent e) {
              JOptionPane.showMessageDialog(view, "My Popup action executed");//$NON-NLS-1$ should
                                                                              // be
                                                                              // externalized,
                                                                              // but
                                                                              // lets
                                                                              // keep
                                                                              // code
                                                                              // simple.
            }
          });
          // set up the layer tree.
          setupLayerTree();

          // remove buttons from the toolbar
          int maxBtns = 8;
          while (toolbar.getComponentCount() > maxBtns) {
            toolbar.remove(toolbar.getComponent(maxBtns));
          }

          /*
           * MapBuilder defines the following status bar components:
           * graphicScale scaleControl coordViewer monitorPanel
           */
          // remove some components from the status bar
          scaleControl.setVisible(false);
          monitorPanel.setVisible(false);

          // customize the coord viewer and graphic scale
          coordViewer.setHtmlTableProperties(""); //$NON-NLS-1$
          coordViewer.setOpaque(false);
          coordViewer.setShowingAltitudes(false);
          // prevent resizing of the toolbar whenever the width becomes too
          // small.
          // JV-3323
          coordViewer.setMinimumSize(new java.awt.Dimension(200, coordViewer.getHeight()));
          graphicScale.setScaleStyle(IlvScaleBar.SIMPLE_SCALE_BOTTOM);

          // customize the bottomleft panel
          getTabbedPane("BottomLeft").setTabPlacement(SwingConstants.BOTTOM); //$NON-NLS-1$

          // move the coord viewer and graphic scale on the toolbar
          toolbar.add(graphicScale.getParent());
          toolbar.add(coordViewer);

          // create a new split to place the right pane
          JSplitPane anotherSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mainSplit, myTabbedPane);
          anotherSplit.setOneTouchExpandable(true);
          anotherSplit.setResizeWeight(1);
          getContentPane().add(anotherSplit, BorderLayout.CENTER);

          // change the view background
          view.setBackground(Color.white);
          overview.setBackground(Color.white);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });// event thread runnable
  }

  /**
   * Set up the layer tree popup menu.
   */
  void setupLayerTree() {
    myLayerMenu = new JMenuItem("My layer action...");//$NON-NLS-1$ should be
                                                      // externalized, but lets
                                                      // keep code simple.
    // disallow any change
    final IlvLayerTree tree = layerTreePanel.getLayerTree();
    tree.setShowAllLayersMenu(false);
    tree.setShowNewLayerMenu(false);
    tree.setShowHideLayerMenu(false);
    tree.setShowDeleteLayerMenu(false);
    tree.setAllowDrag(false);
    tree.setAllowDelete(false);
    tree.setAllowRename(false);

    // add a new menu invoked in the map layer tree
    tree.getPopupMenu().add(myLayerMenu);
    myLayerMenu.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        // find the selected layer
        TreePath selection = tree.getSelectionPath();
        if (selection != null) {
          DefaultMutableTreeNode node = (DefaultMutableTreeNode) selection.getLastPathComponent();
          IlvMapLayer layer = (IlvMapLayer) node.getUserObject();

          // display some message
          JOptionPane.showMessageDialog(getView(), "Show some information on " + layer.getName());//$NON-NLS-1$ should
                                                                                                  // be
                                                                                                  // externalized,
                                                                                                  // but
                                                                                                  // lets
                                                                                                  // keep
                                                                                                  // code
                                                                                                  // simple.
        }
      }
    });
  }

  /** Overridden method to change the views available to the user */
  Override
  protected void initViews() {
    super.initViews();

    // create the sample view and panel
    myView = new JPanel();
    myView.add(new JLabel("[Custom]"));//$NON-NLS-1$ should be externalized, but
                                       // lets keep code simple.
    myView.setName("my View");//$NON-NLS-1$ should be externalized, but lets
                              // keep code simple.
    myTabbedPane = new AdvancedTabbedPane();
    myTabbedPane.setName("Right"); //$NON-NLS-1$
    addTabbedPane(myTabbedPane);
    /*
     * MapBuilder defines the following views: layerTreePanel advancedPanel
     * managerLayersLegend mapLayersLegend dataSourcePanel printPanel gridPanel
     * prefsPanel csPanel simpleSymbologyPanel overviewAndZoomPanel areaPanel
     */

    // set the list of views to manage.
    views = new JComponent[] { myView, layerTreePanel, mapLayersLegend, overviewAndZoomPanel, areaPanel };
  }

  /** Overridden method to change the way views are organized by default */
  Override
  protected void loadPreferences() {
    /*
     * MapBuilder defines the following panels, accessible through the
     * getTabbedPane(String) method: "BottomLeft" "AdditionalViews"
     * "BottomRight" "TopLeft"
     */

    // put "myView" by default (if no preference file exists) in the "right"
    // panel we created above.
    getTabbedPane("Right").addView(myView.getName(), myView, false); //$NON-NLS-1$
    super.loadPreferences();
  }

  /**
   * Overridden method to change the preference file name.
   * 
   * @return the name of the preference file.
   */
  Override
  protected String getPreferenceFileName() {
    return "Extension.settings"; //$NON-NLS-1$
  }

  /**
   * Overridden method to change formats available in the import menu.
   * 
   * @see shared.MapBuilder#getImportActionClassNames()
   */
  Override
  protected List<String> getImportActionClassNames() {
    List<String> list = super.getImportActionClassNames();

    // Remove some useless import formats
    list.remove("plugins.VMAPLoadAction"); //$NON-NLS-1$
    list.remove("plugins.DTEDAvgLoadAction"); //$NON-NLS-1$
    list.remove("plugins.DTEDMinLoadAction"); //$NON-NLS-1$
    list.remove("plugins.DTEDMaxLoadAction"); //$NON-NLS-1$
    list.remove("plugins.SDOLoadAction"); //$NON-NLS-1$
    // add my import format
    list.add("MyLoadAction");//$NON-NLS-1$
    return list;
  }

  /** Overridden method to load a different initial data */
  Override
  protected void loadInitialData() {
    // load a saved map.
    // Note: because we do not provide the initial shape files in this demo,
    // reprojection will not be possible.
    URL mapFile = findURL("data/Conus.ivl"); //$NON-NLS-1$
    try {
      // open a stream
      InputStream stream = new BufferedInputStream(mapFile.openStream());
      // Create a map input stream
      IlvMapInputStream mapInput = new IlvMapInputStream(stream, (String) null);
      // Clear current manager
      view.getManager().deleteAll(false);
      // Load the map
      mapInput.read(view.getManager());
      // close the stream
      stream.close();
      // fit the view to its new content
      view.fitTransformerToContent();
    } catch (Exception e1) {
      e1.printStackTrace();
    }
    // we indicate the map builder that it is not necessary to ask for saving
    // this data.
    setModified(false);
    // change the label factory to show more than one attribute
    IlvMapLabeler labeler = IlvMapLabelerProperty.GetMapLabeler(view.getManager());
    labeler.getLabelManager().setLabelFactory(new MyLabelFactory(labeler));
  }

  /**
   * Main method: creates an ExtensionDemo in a JFrame.
   * 
   * @param args
   *          (ignored)
   */
  public static void main(String[] args) {
    BaseDemo.showSplashScreen();
    // Sun recommends that to put the entire GUI initialization into the
    // AWT thread
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      Override
      public void run() {
        final JFrame frame = new JFrame();
        final ExtensionDemo app = new ExtensionDemo();
        frame.getContentPane().add(app);

        // change the frame title
        frame.setTitle("Extension Demo");//$NON-NLS-1$ should be externalized,
                                         // but lets keep code simple.
        // Change the frame icon
        try {
          frame.setIconImage(IlvImageUtil.getImageFromFile(BaseDemo.class, "/icons/default_frame_icon.gif"));//$NON-NLS-1$
        } catch (IOException e1) {
          // ignore
        }
        // Manage application exit when we close its window.
        frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
          Override
          public void windowClosing(WindowEvent e) {
            app.exitApplication();
          }
        });
        frame.pack();
        frame.setVisible(true);
      }
    });
  }
}