/*
 * 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.GridLayout;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.URL;
import java.util.Enumeration;

import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.filechooser.FileFilter;

import ilog.views.IlvManager;
import ilog.views.IlvManagerLayer;
import ilog.views.IlvManagerView;
import ilog.views.maps.IlvCoordinateSystemProperty;
import ilog.views.maps.IlvMapLayerTreeProperty;
import ilog.views.maps.IlvMapScaleLimiter;
import ilog.views.maps.beans.IlvMapLayer;
import ilog.views.maps.beans.IlvMapLayerTreeModel;
import ilog.views.maps.datasource.DataSourceEvent;
import ilog.views.maps.datasource.DataSourceListener;
import ilog.views.maps.datasource.IlvMapDataSource;
import ilog.views.maps.datasource.IlvMapDataSourceModel;
import ilog.views.maps.datasource.IlvMapDataSourceNode;
import ilog.views.maps.datasource.IlvMapDataSourceProperty;
import ilog.views.maps.defense.format.cadrg.IlvCADRGCoverageList;
import ilog.views.maps.defense.format.cadrg.IlvRasterCADRGReader;
import ilog.views.maps.format.cadrg.IlvCADRGCoverage;
import ilog.views.maps.format.cadrg.IlvCADRGTocReader;
import ilog.views.maps.graphic.style.IlvMapCompositeStyle;
import ilog.views.maps.raster.IlvRasterSubsamplingLoader;
import ilog.views.maps.raster.datasource.IlvRasterDataSourceFactory;
import ilog.views.maps.raster.datasource.IlvThreadMonitoringData;
import ilog.views.maps.srs.coordsys.IlvGeographicCoordinateSystem;
import ilog.views.maps.theme.IlvMapStyleController;
import ilog.views.maps.theme.IlvMapStyleControllerProperty;
import ilog.views.swing.IlvThreadedActivityMonitor;
import ilog.views.swing.IlvThreadedActivityMonitorProperty;
import ilog.views.tiling.IlvThreadedTileLoader;
import ilog.views.tiling.IlvTileLoader;
import ilog.views.tiling.IlvTiledLayer;
import ilog.views.util.IlvProductUtil;

/**
 * This class shows how to read and display CADRG images with Perforce JViews.
 */
SuppressWarnings("serial")
public class CADRGViewer extends JRootPane {
  {
    // This sample uses JViews Maps for Defense features. When deploying an
    // application that includes this code, you need to be in possession
    // of a Perforce JViews Maps for Defense Deployment license.
    IlvProductUtil.DeploymentLicenseRequired(IlvProductUtil.JViews_Maps_for_Defense_Deployment);
  }

  private IlvManager manager;
  private IlvManagerView mgrview;
  private IlvCADRGTocReader tocReader = null;
  // ----------------------------------------------
  // The following objects are used for the AWT GUI
  // ----------------------------------------------
  // The frame for the main window.

  private MyPanel panel = null;

  private final class FitViewWhenDataSourceLoaded implements DataSourceListener {
    Override
    public void dataSourceLoaded(DataSourceEvent event) {
      final IlvMapDataSource src = event.getDataSource();
      mgrview.fitTransformerToContent();
      IlvMapStyleController themeController = IlvMapStyleControllerProperty.GetMapStyleController(mgrview.getManager());
      themeController.setView(mgrview);
      themeController.updateCurrentTheme();
      mgrview.repaint();
      src.removeDataSourceListener(this);
    }
  }

  private static final class CADRGTocFilter extends FileFilter {
    /**
     * @see javax.swing.filechooser.FileFilter#accept(java.io.File)
     */
    Override
    public boolean accept(File f) {
      return f.isDirectory() || f.getName().equalsIgnoreCase("a.toc"); //$NON-NLS-1$
    }

    /**
     * @see javax.swing.filechooser.FileFilter#getDescription()
     */
    Override
    public String getDescription() {
      return "CADRG Table of content (*.toc file)"; // should be //$NON-NLS-1$
                                                    // translated
    }
  }

  class MyPanel extends ViewerPanel {
    /**
     * Creates a viewer panel using CADRG file filter.
     */
    public MyPanel() {
      super(true, null);
    }

    /**
     * Calls {@link CADRGViewer#loadFromFile(String)}
     * 
     * @see ViewerPanel#openFile()
     */
    Override
    public String openFile() {
      JFileChooser fs = getFileChooser();
      if (fs != null) {
        fs.resetChoosableFileFilters();
        fs.setSelectedFile(new File("A.TOC")); //$NON-NLS-1$
        fs.setFileFilter(new CADRGTocFilter());
      }
      String name = super.openFile();
      if (name != null)
        CADRGViewer.this.loadFromFile(name);
      return name;
    }
  }

  /**
   * Creates a CADRG viewer for an application with a menu bar.
   */
  public CADRGViewer() {
    // Make sure the swing construction is called in Swing event thread.
    ilog.views.util.swing.IlvSwingUtil.invokeAndWait(new Runnable() {
      Override
      public void run() {
        panel = new MyPanel();
        getContentPane().add(panel);
        mgrview = panel.getManagerView();
        // limit the zoom to correct scales.
        IlvMapScaleLimiter limiter = new IlvMapScaleLimiter(1f / 1000,  (1 / 1E9));
        limiter.setView(mgrview);
        manager = mgrview.getManager();
        clearManager();
        // Initialize view parameters.
        mgrview.setKeepingAspectRatio(true);
        manager.reDraw();
      }
    }); // event thread runnable
  }

  double screenMeter = (100 / 2.54) * Toolkit.getDefaultToolkit().getScreenResolution();

  /**
   * Performs the loading of specified products.
   * 
   * @param coverages
   *          CADRG coverages to load.
   * @param lowerResolution
   *          true if those coverages are the lower resolution ones.
   * @param higherResolution
   *          true if those coverages are the higher resolution ones.
   */
  public void loadCoverages(IlvCADRGCoverage coverages[], boolean lowerResolution, boolean higherResolution) {
    String name = coverages[0].getScaleDescription().trim();
    final int scale = (int) (screenMeter * coverages[0].getVerticalResolution());

    IlvMapLayerTreeModel ltm = IlvMapLayerTreeProperty.GetMapLayerTreeModel(manager);
    IlvMapLayer groupLayer = new IlvMapLayer();
    groupLayer.setName(name);
    IlvMapCompositeStyle groupStyle = new IlvMapCompositeStyle();
    groupLayer.setStyle(groupStyle);
    ltm.addChild(null, groupLayer);
    // subsample images exactly as they should be:
    IlvRasterSubsamplingLoader.setImageSubsamplingFactor(1);
    // Small scale layer.
    IlvThreadedActivityMonitor mon = IlvThreadedActivityMonitorProperty.GetThreadedActivityMonitor(manager);
    for (int i = 0; i < coverages.length; i++) {
      IlvRasterCADRGReader frameReader = new IlvRasterCADRGReader();
      frameReader.addCADRGCoverage(coverages[i]);
      IlvThreadMonitoringData data = new IlvThreadMonitoringData(mon, coverages[i],
          "Loading CADRG " + coverages[i].getName(), 0, 100);// should //$NON-NLS-1$
                                                             // be translated
      IlvMapDataSource ds = IlvRasterDataSourceFactory.buildTiledImageDataSource(manager, frameReader, false, true,
          data);
      String tocName = coverages[i].getTocPathName();
      ds.getInsertionLayer().setName(coverages[i].getName().substring(tocName.length()));
      IlvMapStyleController themeControl = IlvMapStyleControllerProperty.GetMapStyleController(manager);
      float tscale = scale * 4;
      if (!lowerResolution) {
        themeControl.addTheme(1f / tscale, ds.getInsertionLayer(), "Invisible");// should //$NON-NLS-1$
                                                                                // be
                                                                                // translated
        themeControl.getStyle(ds.getInsertionLayer(), 1f / tscale).setVisibleInView(false);
      }
      tscale = scale / 4;
      themeControl.addTheme(1f / tscale, ds.getInsertionLayer(), "Visible");// should //$NON-NLS-1$
                                                                            // be
                                                                            // translated
      themeControl.getStyle(ds.getInsertionLayer(), 1f / tscale).setVisibleInView(true);
      if (!higherResolution) {
        themeControl.getStyle(ds.getInsertionLayer(), 1).setVisibleInView(false);
      }
      addDataSource(groupLayer, ds);
    }
  }

  /**
   * @param source
   */
  private void addDataSource(IlvMapLayer parent, IlvMapDataSource source) {
    // insert it in the manager's data source tree
    IlvMapDataSourceModel dataSourceModel = IlvMapDataSourceProperty.GetMapDataSourceModel(manager);
    dataSourceModel.insert(source);
    // get the maplayer used to display that data source
    IlvMapLayer layer = source.getInsertionLayer();
    // insert it on the manager's map layer tree
    IlvMapLayerTreeModel ltm = IlvMapLayerTreeProperty.GetMapLayerTreeModel(manager);
    ltm.addChild(parent, layer);
  }

  /**
   * Displays a message when the file to load is not a CADRG TOC.
   */
  public void warning() {
    Object[] options = { "OK" };// should be translated //$NON-NLS-1$
    JOptionPane.showOptionDialog(this, "Not a CADRG file !", "Error", JOptionPane.OK_OPTION, //$NON-NLS-1$ //$NON-NLS-2$
        JOptionPane.WARNING_MESSAGE, null, options, options[0]);// should be
                                                                // translated
  }

  /**
   * Loads coverages from specified toc file. Toc file is opened and a dialog
   * allowing the selection of coverages to be read is opened.
   * 
   * @param fileName
   *          TOC File to read.
   */
  public void loadFromFile(String fileName) {
    clearManager();
    // Create a toc reader
    try {
      tocReader = new IlvCADRGTocReader(fileName);
      // Get all coverage info
      IlvCADRGCoverageList list = new IlvCADRGCoverageList();
      list.addCoverages(fileName, tocReader.getCoverages());
      Integer scales[] = list.getOrderedScaleList();
      String scaleDesc[] = list.getOrderedScaleDescription();
      JPanel pane = new JPanel(new GridLayout(0, 1));
      JCheckBox box[] = new JCheckBox[scaleDesc.length];
      for (int i = 0; i < scaleDesc.length; i++) {
        box[i] = new JCheckBox(scaleDesc[i]);
        pane.add(box[i], 0);
      }
      // Display a coverage chooser
      int ans = JOptionPane.showConfirmDialog(mgrview, pane, "Select coverage scales", JOptionPane.OK_CANCEL_OPTION, //$NON-NLS-1$
          JOptionPane.QUESTION_MESSAGE, null);// should be translated
      if (ans == JOptionPane.OK_OPTION) {
        int lowerResolution = -1;
        int higherResolution = -1;
        // compute lower resolution
        for (int i = 0; i < scaleDesc.length; i++) {
          if (box[i].isSelected()) {
            lowerResolution = i;
          }
          if (box[i].isSelected() && higherResolution == -1) {
            higherResolution = i;
          }
        }
        for (int i = scaleDesc.length - 1; i >= 0; i--) {// higher scales deeper
          if (box[i].isSelected()) {
            loadCoverages(list.getCoverageList(scales[i]), (i == lowerResolution), (i == higherResolution));
          }
        }
        // start reading (recusively start all data sources of this model)
        IlvMapDataSourceModel dataSourceModel = IlvMapDataSourceProperty.GetMapDataSourceModel(manager);
        dataSourceModel.start();
        // add a listener to fit and repaint the view when data source is
        // loaded.
        Enumeration<?> e = dataSourceModel.getEnumeration();
        while (e.hasMoreElements()) {
          IlvMapDataSourceNode node = (IlvMapDataSourceNode) e.nextElement();
          Object data = node.getUserObject();
          if (data instanceof IlvMapDataSource) {
            IlvMapDataSource ds = (IlvMapDataSource) data;
            ds.addDataSourceListener(new FitViewWhenDataSourceLoaded());
            break;
          }
        }

        mgrview.setBackgroundPatternLocation(null);
      }
    } catch (Exception e) {
      warning();
    }
  }

  /**
   * Removes all layers from a manager and clear the associated model
   */
  private void clearManager() {
    IlvMapLayerTreeModel model = IlvMapLayerTreeProperty.GetMapLayerTreeModel(manager);
    model.clearAllObjects();
    while (manager.getLayersCount() > 0) {
      IlvManagerLayer layer = manager.getManagerLayer(0);
      if (layer instanceof IlvTiledLayer) {
        IlvTileLoader loader = ((IlvTiledLayer) layer).getTileLoader();
        if (loader instanceof IlvThreadedTileLoader) {
          ((IlvThreadedTileLoader) loader).dispose();
        }
      }
      manager.removeLayer(0, false);
    }
    manager.removeNamedProperty(IlvMapLayerTreeProperty.NAME);
    manager.removeNamedProperty(IlvMapDataSourceProperty.NAME);
    try {
      URL patternUrl = CADRGViewer.class.getResource("data/nodata.jpg"); //$NON-NLS-1$
      mgrview.setBackgroundPatternLocation(patternUrl);
    } catch (Exception e) {
      e.printStackTrace();
    }
    // georeference manager
    manager.setNamedProperty(new IlvCoordinateSystemProperty(IlvGeographicCoordinateSystem.WGS84));
  }

  /**
   * The main function of the sample.
   * 
   * @param args
   *          ignored argument.
   */
  public static void main(final String[] args) {
    // 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("Perforce JViews CADRG Viewer");// should //$NON-NLS-1$
                                                                          // be
                                                                          // translated
        final CADRGViewer viewer = new CADRGViewer();
        frame.getContentPane().add(viewer);
        frame.addWindowListener(new WindowAdapter() {
          Override
          public void windowClosing(WindowEvent e) {
            System.exit(0);
          }
        });

        frame.pack();
        frame.setVisible(true);
        // Read table of contents of CADRG.
        if (args.length >= 1)
          viewer.loadFromFile(args[0]);
      }
    });
  }
}