/*
 * 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.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JToolBar;

import ilog.views.IlvManager;
import ilog.views.IlvManagerView;
import ilog.views.accelerator.IlvDeleteSelectionAccelerator;
import ilog.views.accelerator.IlvFitToSizeAccelerator;
import ilog.views.accelerator.IlvIdentityAccelerator;
import ilog.views.accelerator.IlvZoomInAccelerator;
import ilog.views.accelerator.IlvZoomOutAccelerator;
import ilog.views.interactor.IlvPanInteractor;
import ilog.views.maps.ExceptionHandler;
import ilog.views.maps.IlvCoordinateSystemProperty;
import ilog.views.maps.IlvMapLayerTreeProperty;
import ilog.views.maps.IlvMapScaleLimiter;
import ilog.views.maps.IlvMapUtil;
import ilog.views.maps.beans.IlvExceptionMessage;
import ilog.views.maps.beans.IlvJMapsManagerViewControlBar;
import ilog.views.maps.beans.IlvLayerTreePanel;
import ilog.views.maps.beans.IlvMapLayer;
import ilog.views.maps.beans.IlvMapLayerTreeModel;
import ilog.views.maps.datasource.IlvMapDataSourceModel;
import ilog.views.maps.datasource.IlvMapDataSourceProperty;
import ilog.views.maps.datasource.ibm.IlvDBConnectionInfo;
import ilog.views.maps.datasource.ibm.IlvDBDataSource;
import ilog.views.maps.datasource.ibm.db2.IlvDB2ConnectionInfo;
import ilog.views.maps.datasource.ibm.db2.IlvDB2DataSource;
import ilog.views.maps.datasource.ibm.informix.IlvInformixConnectionInfo;
import ilog.views.maps.datasource.ibm.informix.IlvInformixDataSource;
import ilog.views.maps.graphic.style.IlvMapCompositeStyle;
import ilog.views.maps.srs.coordsys.IlvCoordinateSystem;
import ilog.views.maps.srs.coordsys.IlvGeographicCoordinateSystem;
import ilog.views.swing.IlvJScrollManagerView;
import ilog.views.swing.IlvThreadedActivityMonitor;
import ilog.views.swing.IlvThreadedActivityMonitorPanel;
import ilog.views.swing.IlvThreadedActivityMonitorProperty;
import ilog.views.util.IlvProductUtil;

/**
 * Sample application to load layers from a DB2 table or Informix table.
 */
public class IBMDBSample extends JFrame implements ExceptionHandler {
  private static final long serialVersionUID = 1L;

  /**
   * Instantiates the sample.
   * 
   * @param args
   *          - not used
   */
  public static void main(String args[]) {
    // This sample uses JViews Maps features. When deploying an
    // application that includes this code, you need to be in possession
    // of a Perforce JViews Maps Deployment license.
    IlvProductUtil.DeploymentLicenseRequired(IlvProductUtil.JViews_Maps_Deployment);

    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      Override
      public void run() {
        final IBMDBSample demo = new IBMDBSample();
        demo.setTitle(IlvMapUtil.getString(IBMDBSample.class, "IBMDBSample.DemoTitle")); //$NON-NLS-1$
        demo.addWindowListener(new WindowAdapter() {
          Override
          public void windowClosing(WindowEvent e) {
            demo.exitSample();
          }
        });
        demo.setVisible(true);
      }
    });
  }

  private JPanel accessoryPanel;
  // Bars
  private IlvJMapsManagerViewControlBar controlBar;
  // The debug view and the frame containing it.
  private JToolBar demoToolBar;
  private boolean firstLayer = true;
  private IlvLayerTreePanel layerTreePanel;
  private DBSpatialTableChooser loadDB2Dialog;
  // Dialogs
  private DBSpatialTableChooser loadInformixDialog;
  private IlvManager manager;
  private IlvThreadedActivityMonitor monitor;
  private IlvThreadedActivityMonitorPanel monitorPanel;
  private IlvManagerView view;

  /**
   * Constructs the sample and it's GUI.
   */
  public IBMDBSample() {
    manager = new IlvManager();
    manager.setNamedProperty(new IlvCoordinateSystemProperty(IlvGeographicCoordinateSystem.WGS84));
    view = new IlvManagerView(manager);
    loadDB2Dialog = new DB2SpatialTableChooser();
    loadInformixDialog = new InformixSpatialTableChooser();
    setSize(900, 600);
    view.setKeepingAspectRatio(true);
    // limit the zoom to correct scales.
    IlvMapScaleLimiter limiter = new IlvMapScaleLimiter(1, (float) (1 / 1E9));
    limiter.setView(view);
    initButtons();
    createMenuBar();
    initAccelerators();
    IlvJScrollManagerView managerView = new IlvJScrollManagerView(view);
    managerView.setPreferredSize(new Dimension(400, 400));
    getContentPane().add(managerView, BorderLayout.CENTER);
    layerTreePanel = new IlvLayerTreePanel();
    layerTreePanel.setPreferredSize(new Dimension(220, 300));
    layerTreePanel.setView(view);
    accessoryPanel = new JPanel(new BorderLayout());
    accessoryPanel.add(layerTreePanel, BorderLayout.CENTER);
    getContentPane().add(accessoryPanel, BorderLayout.LINE_START);
  }

  /**
   * Creates the menu bar.
   */
  private void createMenuBar() {
    JMenuItem voidItem;
    JMenu demoMenu = new JMenu(IlvMapUtil.getString(IBMDBSample.class, "IBMDBSample.MainMenu"));//$NON-NLS-1$
    demoMenu.add(voidItem = new JMenuItem(IlvMapUtil.getString(IBMDBSample.class, "IBMDBSample.NewMap")));//$NON-NLS-1$
    voidItem.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        clearMap();
      }
    });
    demoMenu.addSeparator();
    // Load DB2 using fixed connection information.
    demoMenu.add(voidItem = new JMenuItem(IlvMapUtil.getString(IBMDBSample.class, "IBMDBSample.LoadDb2Label"))); //$NON-NLS-1$
    voidItem.setIcon(new ImageIcon(IBMDBSample.class.getResource("load_db2.gif")));//$NON-NLS-1$
    voidItem.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        try {
          loadFromDB2();
        } catch (SQLException e1) {
          e1.printStackTrace();
        } catch (IOException e1) {
          e1.printStackTrace();
        }
      }
    });
    // Load Informix using FileChooser.
    demoMenu.add(voidItem = new JMenuItem(IlvMapUtil.getString(IBMDBSample.class, "IBMDBSample.LoadInformixLabel"))); //$NON-NLS-1$
    voidItem.setIcon(new ImageIcon(IBMDBSample.class.getResource("load_informix.gif"))); //$NON-NLS-1$
    voidItem.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        try {
          loadFromInformix();
        } catch (SQLException e1) {
          e1.printStackTrace();
        } catch (IOException e1) {
          e1.printStackTrace();
        }
      }
    });
    demoMenu.addSeparator();
    // Quit.
    demoMenu.add(voidItem = new JMenuItem(IlvMapUtil.getString(IBMDBSample.class, "IBMDBSample.Quit"))); //$NON-NLS-1$
    voidItem.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        exitSample();
      }
    });
    JMenuBar menubar = new JMenuBar();
    menubar.add(demoMenu);
    setJMenuBar(menubar);
  }

  /**
   * Exits the sample. It closes the database connections before exiting.
   */
  public void exitSample() {
    closeConnection();
    System.exit(0);
  }

  Override
  public void handle(Exception e) throws Exception {
    reportError(e);
  }

  /**
   * Sets the accelerators for the manager.
   */
  private void initAccelerators() {
    manager.addAccelerator(new IlvZoomInAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_Z, InputEvent.CTRL_MASK));
    manager.addAccelerator(new IlvZoomOutAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_U, InputEvent.CTRL_MASK));
    manager.addAccelerator(new IlvIdentityAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_I, InputEvent.CTRL_MASK));
    manager.addAccelerator(new IlvFitToSizeAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_F, InputEvent.CTRL_MASK));
    manager.addAccelerator(new IlvDeleteSelectionAccelerator(KeyEvent.KEY_PRESSED, KeyEvent.VK_DELETE, 0));
  }

  /**
   * Builds the buttons and the bars.
   */
  private void initButtons() {
    getContentPane().setLayout(new BorderLayout());
    JPanel p = new JPanel();
    p.setLayout(new FlowLayout(FlowLayout.LEFT));
    JButton loadDb2Button = new JButton(new ImageIcon(IBMDBSample.class.getResource("load_db2.gif"))); //$NON-NLS-1$
    JButton loadInformixButton = new JButton(new ImageIcon(IBMDBSample.class.getResource("load_informix.gif"))); //$NON-NLS-1$
    monitor = IlvThreadedActivityMonitorProperty.GetThreadedActivityMonitor(manager);
    monitorPanel = new IlvThreadedActivityMonitorPanel(monitor);
    monitorPanel.setOpaque(false);
    controlBar = new IlvJMapsManagerViewControlBar();
    controlBar.setFloatable(false);
    controlBar.setBorderPainted(false);
    controlBar.setOpaque(false);
    demoToolBar = new JToolBar();
    demoToolBar.setFloatable(false);
    demoToolBar.setBorderPainted(false);
    demoToolBar.setOpaque(false);
    p.add(demoToolBar);
    p.add(controlBar);
    controlBar.setView(view);
    loadDb2Button.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        try {
          loadFromDB2();
        } catch (SQLException e1) {
          e1.printStackTrace();
        } catch (IOException e1) {
          e1.printStackTrace();
        }
      }
    });
    loadInformixButton.addActionListener(new ActionListener() {
      Override
      public void actionPerformed(ActionEvent e) {
        try {
          loadFromInformix();
        } catch (SQLException e1) {
          e1.printStackTrace();
        } catch (IOException e1) {
          e1.printStackTrace();
        }
      }
    });
    p.add(controlBar);
    loadDb2Button.setToolTipText(IlvMapUtil.getString(IBMDBSample.class, "IBMDBSample.LoadDb2Label")); //$NON-NLS-1$
    loadInformixButton.setToolTipText(IlvMapUtil.getString(IBMDBSample.class, "IBMDBSample.LoadInformixLabel")); //$NON-NLS-1$
    demoToolBar.addSeparator();
    demoToolBar.add(loadDb2Button);
    demoToolBar.add(loadInformixButton);
    controlBar.addSeparator();
    p.add(monitorPanel);
    loadDb2Button.setPreferredSize(new Dimension(26, 26));
    loadInformixButton.setPreferredSize(new Dimension(26, 26));
    ((IlvPanInteractor) controlBar.getPanInteractor()).setOpaqueMove(true);
    getContentPane().add(p, BorderLayout.PAGE_START);
  }

  /**
   * Loads a DB2 Spatial layer using a dialog GUI.
   * 
   * @throws SQLException
   * @throws IOException
   */
  private void loadFromDB2() throws SQLException, IOException {
    loadDB2Dialog.setSelectedFile(null);
    int openRC = loadDB2Dialog.showOpenDialog(this);
    if (JFileChooser.APPROVE_OPTION == openRC) {
      final File[] files = loadDB2Dialog.getSelectedFiles();
      IlvDB2ConnectionInfo conn = (IlvDB2ConnectionInfo) loadDB2Dialog.getConnection();
      for (int idx = 0; idx < files.length; idx++) {
        String tableName = files[idx].getName();
        IlvDBDataSource ds = new IlvDB2DataSource(conn, tableName);
        addDataSource(ds, IlvMapUtil.getString(IBMDBSample.class, "IBMDBSample.DB2Layer")); //$NON-NLS-1$
      }
    }
  }

  /**
   * Loads an Informix Spatial layer using a dialog GUI.
   * 
   * @throws SQLException
   * @throws IOException
   */
  private void loadFromInformix() throws SQLException, IOException {
    loadInformixDialog.setSelectedFile(null);
    int openRC = loadInformixDialog.showOpenDialog(this);
    if (JFileChooser.APPROVE_OPTION == openRC) {
      final File[] files = loadInformixDialog.getSelectedFiles();
      for (int idx = 0; idx < files.length; idx++) {
        String tableName = files[idx].getName();
        IlvInformixConnectionInfo conn = (IlvInformixConnectionInfo) loadInformixDialog.getConnection();
        IlvDBDataSource ds = new IlvInformixDataSource(conn, tableName);
        addDataSource(ds, IlvMapUtil.getString(IBMDBSample.class, "IBMDBSample.InformixLayer")); //$NON-NLS-1$
      }
    }
  }

  /**
   * This method handles exceptions.
   * 
   * @param e
   */
  private void reportError(Throwable e) {
    new IlvExceptionMessage(e, null);
  }

  /**
   * Clears the map.
   */
  private void clearMap() {
    IlvMapDataSourceModel dataSourceModel = IlvMapDataSourceProperty.GetMapDataSourceModel(manager);
    dataSourceModel.clear();
    IlvMapLayerTreeModel ltm = IlvMapLayerTreeProperty.GetMapLayerTreeModel(view.getManager());
    ltm.clear();
    int nb = manager.getLayersCount();
    for (int i = 1; i < nb; i++)
      manager.removeLayer(1, false);
    manager.deleteAll(true);
    view.repaint();
    firstLayer = true;
  }

  /**
   * Add a data source to the specified parent layer.
   * 
   * @param ds
   *          - data source
   * @param parentLayerName
   *          - name of parent layer (DB2 or Informix)
   * @throws IOException
   */
  private void addDataSource(final IlvDBDataSource ds, final String parentLayerName) throws IOException {
    final IlvMapDataSourceModel dataSourceModel = IlvMapDataSourceProperty.GetMapDataSourceModel(view.getManager());
    IlvMapLayerTreeModel ltm = IlvMapLayerTreeProperty.GetMapLayerTreeModel(view.getManager());
    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    IlvCoordinateSystem dataSourceCS = ds.getCoordinateSystem();
    IlvMapLayer parent = ltm.findChildLayer(null, parentLayerName);
    if (parent == null) {
      parent = new IlvMapLayer();
      parent.setName(parentLayerName);
      IlvMapCompositeStyle parentStyle = new IlvMapCompositeStyle();
      parent.setStyle(parentStyle);
      ltm.addChild(null, parent);
    }
    IlvMapLayer layer = ds.getInsertionLayer();
    layer.setName(ds.getName());
    dataSourceModel.insert(ds);
    IlvMapLayer mapLayer = ds.getInsertionLayer();
    ltm.addChild(parent, mapLayer);
    final IlvDBDataSource fds = ds;
    manager.setNamedProperty(new IlvCoordinateSystemProperty(dataSourceCS));
    // start data source model in a separate thread
    Thread loadThread = new Thread() {
      Override
      public void run() {
        try {
          fds.start();
        } catch (Throwable e) {
          reportError(e);
          doCleanup();
          return;
        }
        if (firstLayer) {
          // fit only on first layer loaded
          view.fitTransformerToContent();
          firstLayer = false;
        }
        doCleanup();
      }

      private void doCleanup() {
        view.repaint();
        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
        Toolkit.getDefaultToolkit().sync();
      }
    };
    loadThread.start();
  }

  /**
   * Close any connections that may exist.
   * 
   */
  private void closeConnection() {
    IlvDBConnectionInfo conn = loadDB2Dialog.getConnection();
    if (null != conn && conn.isReady())
      try {
        conn.disconnect();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    conn = loadInformixDialog.getConnection();
    if (null != conn && conn.isReady())
      try {
        conn.disconnect();
      } catch (SQLException e) {
        e.printStackTrace();
      }
  }
}