/*
 * Licensed Materials - Property of Rogue Wave Software, Inc. 
 * © Copyright Rogue Wave Software, Inc. 2014, 2015 
 * © 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 ilog.views.IlvGraphic;
import ilog.views.IlvGraphicBag;
import ilog.views.IlvManager;
import ilog.views.IlvManagerLayer;
import ilog.views.IlvManagerView;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.IlvTransformer;
import ilog.views.graphic.IlvEllipse;
import ilog.views.interactor.IlvMakeRectangleInteractor;
import ilog.views.interactor.IlvRectangularObjectFactory;
import ilog.views.io.IlvFieldNotFoundException;
import ilog.views.io.IlvInputStream;
import ilog.views.io.IlvOutputStream;
import ilog.views.io.IlvReadFileException;
import ilog.views.maps.IlvAreasOfInterestProperty;
import ilog.views.maps.IlvAttributeProperty;
import ilog.views.maps.IlvCoordinate;
import ilog.views.maps.IlvCoordinateSystemProperty;
import ilog.views.maps.IlvFeatureAttribute;
import ilog.views.maps.IlvFeatureAttributeProperty;
import ilog.views.maps.IlvMapGeometry;
import ilog.views.maps.IlvMapInputStream;
import ilog.views.maps.IlvMapLayerTreeProperty;
import ilog.views.maps.IlvMapOutputStream;
import ilog.views.maps.IlvMapScaleLimiter;
import ilog.views.maps.IlvMapUtil;
import ilog.views.maps.attribute.IlvStringAttribute;
import ilog.views.maps.beans.IlvJCoordinateSystemEditorPanel;
import ilog.views.maps.beans.IlvJMapsManagerViewControlBar;
import ilog.views.maps.beans.IlvLayerTreePanel;
import ilog.views.maps.beans.IlvMapAnnotationModel;
import ilog.views.maps.beans.IlvMapAnnotationProperty;
import ilog.views.maps.beans.IlvMapAnnotationToolBar;
import ilog.views.maps.beans.IlvMapLayer;
import ilog.views.maps.beans.IlvMapLayerTreeModel;
import ilog.views.maps.datasource.IlvGraphicLayerDataSource;
import ilog.views.maps.datasource.IlvMapDataSource;
import ilog.views.maps.datasource.IlvMapDataSourceModel;
import ilog.views.maps.datasource.IlvMapDataSourceProperty;
import ilog.views.maps.datasource.IlvShapeDataSource;
import ilog.views.maps.format.image.IlvRasterBasicImageReader;
import ilog.views.maps.geometry.IlvMapEllipse;
import ilog.views.maps.graphic.IlvMapGraphic;
import ilog.views.maps.graphic.IlvMapSelectionFactory;
import ilog.views.maps.graphic.style.IlvGraphicPathStyle;
import ilog.views.maps.graphic.style.IlvMapStyle;
import ilog.views.maps.graphic.style.IlvMapStyleBeanInfo;
import ilog.views.maps.graphic.style.IlvPolylineStyle;
import ilog.views.maps.label.IlvMapLabeler;
import ilog.views.maps.label.IlvMapLabelerProperty;
import ilog.views.maps.projection.IlvProjectionDictionary;
import ilog.views.maps.raster.IlvRasterAbstractReader;
import ilog.views.maps.raster.IlvRasterMappedBuffer;
import ilog.views.maps.raster.datasource.IlvRasterDataSourceFactory;
import ilog.views.maps.srs.coordsys.IlvCoordinateSystem;
import ilog.views.maps.srs.coordsys.IlvGeographicCoordinateSystem;
import ilog.views.maps.srs.coordtrans.IlvCoordinateTransformation;
import ilog.views.maps.srs.coordtrans.IlvCoordinateTransformationException;
import ilog.views.maps.theme.IlvMapStyleControllerProperty;
import ilog.views.swing.IlvJScrollManagerView;
import ilog.views.swing.IlvToolTipManager;
import ilog.views.tiling.IlvThreadedTileLoader;
import ilog.views.tiling.IlvTileLoader;
import ilog.views.tiling.IlvTiledLayer;
import ilog.views.util.IlvProductUtil;
import ilog.views.util.IlvResourceUtil;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;

import javax.swing.AbstractButton;
import javax.swing.JApplet;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.tree.DefaultMutableTreeNode;

/**
 * Draw on map demo. The purpose of this demonstration is to show how to put
 * graphics on top of a map using the IlvGraphicLayerDataSource.
 */
public class DrawOnMap extends JApplet {

  static {
    // This applet is designed to run only with default resource bundle
    // and various selected other resource bundles.
    // Setting the available resource suffixes avoids that the applet
    // tries to load resource bundles for other locales over the net,
    // even if the current locale of the browser is different.
    if (IlvResourceUtil.isInApplet())
      IlvResourceUtil.setAvailableResourceSuffixes("", "_ja");  //$NON-NLS-1$//$NON-NLS-2$
    // JV-4073
    // increase number of projected points to find the total image size
    IlvRasterAbstractReader.setDefaultMaximumIntervalsForDeterminingImageLocation(100);
  }

  {
    // This sample uses JViews Maps features. When deploying an
    // application that includes this code, you need to be in possession
    // of a Rogue Wave JViews Maps Deployment license.
    IlvProductUtil.DeploymentLicenseRequired(
        IlvProductUtil.JViews_Maps_Deployment);
  }

  private static final String WORLD_JPG = "world.jpg";//$NON-NLS-1$
  private static final String WORLD_SHP = "World.shp";//$NON-NLS-1$
  // tell if lanched in applet mode.
  private boolean isApplet;
  // last saved files
  private File ivlFile;

  // main map view
  private IlvManager manager = new IlvManager(0);
  private IlvManagerView view = new IlvManagerView(manager) {
    public void fitTransformerToContent(Insets i) {
      IlvManager pmanager = getManager();
      IlvMapDataSourceProperty p = (IlvMapDataSourceProperty) pmanager.getNamedProperty(IlvMapDataSourceProperty.NAME);
      Enumeration e = p.getMapDataSourceModel().getEnumeration();
      while (e.hasMoreElements()) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
        Object o = node.getUserObject();
        if(!(o instanceof IlvMapDataSource))
          continue;
        IlvMapDataSource source = (IlvMapDataSource) o;
        if(!WORLD_SHP.equals(source.getName()))
          pmanager.setVisible(source.getInsertionLayer().getManagerLayer().getIndex(), false, false);
      }

      super.fitTransformerToContent(i);

      e = p.getMapDataSourceModel().getEnumeration();
      while (e.hasMoreElements()) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
        Object o = node.getUserObject();
        if(!(o instanceof IlvMapDataSource))
          continue;
        IlvMapDataSource source = (IlvMapDataSource) o;
        if(!WORLD_SHP.equals(source.getName()))
          pmanager.setVisible(source.getInsertionLayer().getManagerLayer().getIndex(), true, false);
      }

    }

  };

  /**
   * Return a HTML formated text when argument contains new lines.
   * 
   * @param s
   * The string
   * @return The HTML formated string.
   */
  String getFormatedExtendedText(String s) {
    if(BasicHTML.isHTMLString(s))
      return s;
    String sp[] = s.split("\n"); //$NON-NLS-1$
    if(sp.length <= 1)
      return s;
    s = "<html> "; //$NON-NLS-1$
    for (int i = 0; i < sp.length; i++) {
      if(i > 0)
        s += " <br> "; //$NON-NLS-1$
      s += "\n" + sp[i]; //$NON-NLS-1$
    }
    s += "\n </html> "; //$NON-NLS-1$
    return s;
  }

  // coordinate system panel
  private IlvJCoordinateSystemEditorPanel coordPanel;
  // scroll around the map view
  private IlvJScrollManagerView viewScroll = new IlvJScrollManagerView(view) {
    public String getToolTipText(MouseEvent event) {
      Point pt = SwingUtilities.convertPoint(this, event.getPoint(), view);
      // Find component located at point pt
      IlvGraphic o = view.getManager().getObject(new IlvPoint(pt.x, pt.y), view);
      if(o != null) {
        IlvFeatureAttributeProperty p = (IlvFeatureAttributeProperty) o.getNamedProperty(IlvAttributeProperty.NAME);
        if(p != null) {
          IlvStringAttribute att = null;
          try {
            att = (IlvStringAttribute) p.getValue(IlvMapAnnotationToolBar.EXT_ANNOTATION_PROPERTY_NAME);
          } catch (IllegalArgumentException e) {
            att = null;
          }
          if(att != null) {
            String v = att.getString();
            if(v != null && !"".equals(v) && !"__null__".equals(v)) //$NON-NLS-1$//$NON-NLS-2$
              return getFormatedExtendedText(v);
          }
        }
      }
      return null;
    }
  };

  // zoom & selection toolbar
  private IlvJMapsManagerViewControlBar viewToolbar = new IlvJMapsManagerViewControlBar();
  private IlvMapAnnotationToolBar annotationsToolBar = new IlvMapAnnotationToolBar();
  // The save button
  private IlvMapAnnotationToolBar.ToolBarButton save;
  // The load button
  private IlvMapAnnotationToolBar.ToolBarButton load;

  private PropertyChangeListener coordinateSystemChanged = new PropertyChangeListener() {
    public void propertyChange(PropertyChangeEvent evt) {
      IlvCoordinateSystem system = (IlvCoordinateSystem) evt.getNewValue();
      // change coordinate system of the map according to panel's selection
      manager.setNamedProperty(new IlvCoordinateSystemProperty(system));
      // erase existing data in manager, and reload sources.

      clearManager(false);

      IlvMapDataSourceModel dsm = IlvMapDataSourceProperty.GetMapDataSourceModel(manager);
      DefaultMutableTreeNode root = (DefaultMutableTreeNode) dsm.getRoot();
      int count = root.getChildCount();
      for (int i = 0; i < count; i++) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) root.getChildAt(i);
        IlvMapDataSource source = (IlvMapDataSource) node.getUserObject();
        IlvMapLayer layer = source.getInsertionLayer();
        if(layer.getManager() == manager) {
          source.reset();
          try {
            source.start();
          } catch (Exception e) {
            e.printStackTrace();
          }
          IlvManagerLayer layers[] = layer.getManagerLayers();
          for (int l = 0; l < layers.length; l++) {
            if(layers[l] instanceof IlvTiledLayer) {
              ((IlvTiledLayer) layers[l]).getTileController().updateView(view);
            }
          }
        }
      }
      view.fitTransformerToContent();
    }

  };

  /**
   * Constructor called on applet mode.
   */
  public DrawOnMap() {
    this(true);
  }

  /**
   * Constructor called from main.
   * 
   * @param applet
   * applet mode
   */
  public DrawOnMap(boolean applet) {
    super();
    manager.setSelectionFactory(new IlvMapSelectionFactory());
    // let the zoom interactor be permanent.
  
    // Am I an applet ?
    setApplet(applet);
    // Show all the style properties
    IlvMapStyleBeanInfo.setAdvancedMode(true);
    // Make sure the swing construction is called in Swing event thread.
    ilog.views.util.swing.IlvSwingUtil.invokeAndWait(new Runnable() { public void run() {
    // Toolbar feed
    setupToolbar();
    // configure view
    setupView();
    // configure GUI
    setupGUI();
    // Load the initial file
    loadData(true);
    // JV-2760
    IlvToolTipManager.registerView(view);
    }}); // event thread runnable
  }


  /**
   * called by the applet container when the applet starts.
   */
  public void start() {
    view.fitTransformerToContent();
  }

  /**
   * applet mode accessors.
   */
  void setApplet(boolean applet) {
    isApplet = applet;
  }

  boolean isApplet() {
    return isApplet;
  }

  IlvJCoordinateSystemEditorPanel getCoordPanel() {
    if(coordPanel == null) {
      coordPanel = new IlvJCoordinateSystemEditorPanel() {
        public IlvProjectionDictionary createProjectionDictionary() {
          return new ProjectionDictionary();
        }
      };
    }
    return coordPanel;
  }

  /**
   * build GUI.
   */
  void setupGUI() {
    IlvJCoordinateSystemEditorPanel cp = getCoordPanel();
    cp.setAdvancedPanelsVisible(false);
    IlvCoordinateSystem cs = IlvCoordinateSystemProperty.GetCoordinateSystem(manager);
    cp.setCoordinateSystem(cs);
    cp.addCoordinateSystemChangeListener(coordinateSystemChanged);
    IlvLayerTreePanel layerTree = new IlvLayerTreePanel();
    layerTree.setView(view);

    JSplitPane split1 = new JSplitPane();
    split1.setOrientation(JSplitPane.VERTICAL_SPLIT);
    split1.setLeftComponent(cp);
    split1.setRightComponent(layerTree);

    JSplitPane split2 = new JSplitPane();
    split2.setLeftComponent(split1);
    split2.setRightComponent(viewScroll);
    getContentPane().add(split2, BorderLayout.CENTER);
    JPanel toolBarPanel = new JPanel(new BorderLayout());
    toolBarPanel.add(viewToolbar, BorderLayout.LINE_START);
    toolBarPanel.add(annotationsToolBar);
    getContentPane().add(toolBarPanel, BorderLayout.NORTH);
  }

  /**
   * configure view.
   */
  void setupView() {
    view.setSize(new Dimension(800, 600));
    view.setKeepingAspectRatio(true);
    viewScroll.setPreferredSize(view.getSize());
    // geo reference the manager

    manager.setNamedProperty(new IlvCoordinateSystemProperty(getCoordPanel().getCoordinateSystem()));

    // limit the zoom to correct scales.
    IlvMapScaleLimiter limiter = new IlvMapScaleLimiter(1f / 10000000f, (float) (1 / 1E9));
    limiter.setView(view);
    view.setAntialiasing(true);
  }

  /**
   * configure toolbar
   */
  void setupToolbar() {
    viewToolbar.setView(view);
    annotationsToolBar.setView(view);

    if(!isApplet()) { // cannot access file system in a non signed applet.
      // save button.
      ActionListener sac = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          ((AbstractButton) e.getSource()).setSelected(false);
          try {
            // create temp files fore writting the manager.
            if(ivlFile != null)
              ivlFile.delete();
            ivlFile = File.createTempFile("DrawOnMap", IlvMapOutputStream.getFileSuffix()); //$NON-NLS-1$
            ivlFile.deleteOnExit();
            FileOutputStream stream = new FileOutputStream(ivlFile);
            message("saving to " + ivlFile.getAbsolutePath());//$NON-NLS-1$// should betranslated
            IlvMapOutputStream str = new IlvMapOutputStream(stream, null, false);
            // write only data sources, not the objects
            str.setWritingObjects(false);
            // write the content of the manager, that is data sources, layer and
            // data source tree.
            str.write(manager);
            str.flush();
            str.closeImageStream();
            stream.close();
            message("saving done");// should be translated //$NON-NLS-1$
            load.setEnabled(true);
          } catch (IOException e1) {
            e1.printStackTrace();
          }
        }
      };
      save = annotationsToolBar.new ToolBarButton("/save.gif", sac, "Save to Temporary File");//$NON-NLS-1$// should be translated //$NON-NLS-2$

      // load button
      ActionListener lac = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
          ((AbstractButton) e.getSource()).setSelected(false);
          try {
            InputStream str = null;
            if(ivlFile != null)
              str = new FileInputStream(ivlFile);
            if(str != null) {
              message("loading from " + ivlFile.getAbsolutePath());//$NON-NLS-1$// should be translated
              clearManager(true);
              IlvMapInputStream stream = new IlvMapInputStream(str, (String) null);
              stream.read(manager);
              str.close();
              view.fitTransformerToContent();
            }
          } catch (IOException e1) {
            e1.printStackTrace();
          } catch (IlvReadFileException e1) {
            e1.printStackTrace();
          }
        }
      };
      load = annotationsToolBar.new ToolBarButton("/load.gif", lac, "Load from Temporary File");//$NON-NLS-1$// should be translated //$NON-NLS-2$
      load.setEnabled(false);
    }

    if(save != null)
      viewToolbar.add(save);
    if(load != null)
      viewToolbar.add(load);

    IlvMapAnnotationToolBar.ToolBarButton b;

    b = annotationsToolBar.new ToolBarButton("/ellipse.gif", new MakeEllipseInteractor(), "Make an Ellipse Annotation");//$NON-NLS-1$// should be translated //$NON-NLS-2$

    annotationsToolBar.add(b);

  }

  /**
   * Creates a map. All map creation steps are performed here.
   */
  private void loadData(boolean start) {

    URL jpgURL = DrawOnMap.class.getResource(WORLD_JPG);
    URL shpFile = DrawOnMap.class.getResource(WORLD_SHP);

    try {
      IlvShapeDataSource shpDataSource = new IlvShapeDataSource(shpFile, false);
      shpDataSource.setName(WORLD_SHP);
      shpDataSource.setCoordinateSystem(IlvGeographicCoordinateSystem.WGS84);
      if(isApplet()) // in an unsigned applet, creating a temporary file is
        // forbidden.
        IlvRasterMappedBuffer.setDefaultMemoryPolicy(IlvRasterMappedBuffer.USE_MEMORY);
      // create a raster reader for the gif file (necessary to create a
      // datasource below)
      IlvRasterBasicImageReader imageReader = new IlvRasterBasicImageReader();
      imageReader.addMap(jpgURL);
      // georeference this image (it covers the whole earth)
      imageReader.setImageBounds(0, -Math.PI, Math.PI / 2, Math.PI, -Math.PI / 2);

      // create a datasource for the jpg file.
      IlvMapDataSource imageDataSource = IlvRasterDataSourceFactory.buildTiledImageDataSource(manager, imageReader,
          true, true, null);
      imageDataSource.setName(WORLD_JPG);
      // insert it in the manager's data source tree
      IlvMapDataSourceModel dataSourceModel = IlvMapDataSourceProperty.GetMapDataSourceModel(manager);

      dataSourceModel.insert(shpDataSource);
      dataSourceModel.insert(imageDataSource);

      IlvMapLayer shpLayer = shpDataSource.getInsertionLayer();
      shpLayer.setName("ESRI layer (world.shp)"); //$NON-NLS-1$// should be translated
        // setup the shape layer style
      shpLayer.setStyle(new IlvGraphicPathStyle());
      shpLayer.getStyle().setAttribute(IlvPolylineStyle.FOREGROUND, Color.cyan.darker());
      shpLayer.getStyle().setAttribute(IlvGraphicPathStyle.PAINT, new Color(1, 1, 1, 0.25f));

      IlvMapLayer imageLayer = imageDataSource.getInsertionLayer();
      imageLayer.setName("Image layer (world.jpg)"); //$NON-NLS-1$// should be translated
      // insert it on the manager's map layer tree
      IlvMapLayerTreeModel ltm = IlvMapLayerTreeProperty.GetMapLayerTreeModel(manager);
      ltm.addChild(null, imageLayer);
      ltm.addChild(null, shpLayer);

      IlvMapLayerTreeProperty.GetMapLayerTreeModel(manager).arrangeLayers();
      // start reading (recusively start all data sources of this model)
      if(start)
        dataSourceModel.start();
    } catch (Exception e1) {
      e1.printStackTrace();
    }
    view.setTripleBufferedLayerCount(view.getManager().getLayersCount());
    // fit the view
    view.fitTransformerToContent();
    view.repaint();
  }

  /**
   * Removes all layers from a manager and clear the associated model
   */
  private void clearManager(boolean removeProperties) {
    if(removeProperties) {// JV-2853
      IlvMapLayerTreeModel model = IlvMapLayerTreeProperty.GetMapLayerTreeModel(manager);
      model.clearAllObjects();
    }
    manager.deleteAll(false);
    if(removeProperties)
      while (manager.getLayersCount() > 0) {
        if(true) {
          IlvManagerLayer layer = manager.getManagerLayer(0);
          if(layer instanceof IlvTiledLayer) {
            IlvTileLoader loader = ((IlvTiledLayer) layer).getTileLoader();
            if(loader instanceof IlvThreadedTileLoader) {
              ((IlvThreadedTileLoader) loader).dispose();
            }
            ((IlvTiledLayer) layer).getTileController().removeAllFreeTiles();
          }
        }
        manager.removeLayer(0, false);
      }

    if(removeProperties) {
      manager.removeNamedProperty(IlvMapLayerTreeProperty.NAME);
      manager.removeNamedProperty(IlvMapDataSourceProperty.NAME);
      manager.removeNamedProperty(IlvAreasOfInterestProperty.NAME);
      manager.removeNamedProperty(IlvMapStyleControllerProperty.NAME);
      manager.removeNamedProperty(IlvMapLabelerProperty.NAME);
      manager.removeNamedProperty(IlvMapAnnotationProperty.NAME);
    }
  }

  void message(String s) {
    System.out.println(s);
  }

  /**
   * @param a
   */
  public static void main(String a[]) {
    // Sun recommends that to put the entire GUI initialization into the
    // AWT thread
    javax.swing.SwingUtilities.invokeLater(
      new Runnable() {
        public void run() {
    DrawOnMap demo = new DrawOnMap(false);
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(demo);
    frame.pack();
    frame.setTitle("Draw On Map Demo"); //$NON-NLS-1$
    frame.setVisible(true);
        }});
  }

  class ProjectionDictionary extends IlvProjectionDictionary {
    ProjectionDictionary() {
      super();
      unregisterProjection("Polyconic"); //$NON-NLS-1$
    }
  }

  /**
   * Interactor to create an ellipse interaction.
   */
  public class MakeEllipseInteractor extends IlvMakeRectangleInteractor {
    /**
     * Object name
     */
    protected static final String ELLIPSE = "Ellipse";  //$NON-NLS-1$// should be translated

    IlvRectangularObjectFactory of = new IlvRectangularObjectFactory() {
      public IlvGraphic createObject(IlvRect rect) {
        IlvManager pmanager = annotationsToolBar.getView().getManager();
        if(rect.width < 3 && rect.height < 3)
          rect.expand(2f);
        setFillOn(true);
        MapEllipse ellipse = new MapEllipse(rect, true, true);

        IlvMapAnnotationModel model = IlvMapAnnotationProperty.GetMapAnnotationModel(pmanager);
        IlvGraphicLayerDataSource dataSource = model.getDataSource(pmanager, ELLIPSE);
        String name = ELLIPSE + " Annotation"; //$NON-NLS-1$// should be translated
        dataSource.getInsertionLayer().setName(name);
        dataSource.add(ellipse, IlvCoordinateSystemProperty.GetCoordinateSystem(pmanager));
        if(dataSource.getInsertionLayer().getStyle() == null) {
          MapEllipseStyle style = new MapEllipseStyle();
          style.setFill(true);
          style.setAttributeInfo(IlvMapAnnotationModel.info);
          style.setLabelAttribute(IlvMapAnnotationModel.info.getAttributeName(0));
          dataSource.getInsertionLayer().setStyle(style);
        }
        dataSource.getInsertionLayer().setAllowingMoveObjects(true);
        ellipse.setStyle(dataSource.getInsertionLayer().getStyle());
        pmanager.setInsertionLayer(dataSource.getInsertionLayer().getManagerLayer().getIndex());
        String[] s = annotationsToolBar.askForStrings(null, null);
        IlvFeatureAttributeProperty properties = new IlvFeatureAttributeProperty(IlvMapAnnotationModel.info,
            new IlvFeatureAttribute[] {
                s == null ? new IlvStringAttribute((String) null) : new IlvStringAttribute(s[0]),
                s == null ? new IlvStringAttribute((String) null) : new IlvStringAttribute(s[1]) });
        ellipse.setNamedProperty(properties);
        ellipse.setPopupMenu(annotationsToolBar.getPopupMenu());
        IlvMapSelectionFactory.setEditable(ellipse, true);
        if(s != null) {
          IlvMapLabeler labeler = IlvMapLabelerProperty.GetMapLabeler(pmanager);
          labeler.setView(getManagerView());
          labeler.addLayer(dataSource.getInsertionLayer());
          labeler.performLabeling();
        }
        return ellipse;
      }
    };

    /**
     * Creates a new <code>MakeEllipseInteractor</code>.
     */
    public MakeEllipseInteractor() {
      super();
      setObjectFactory(of);
    }

    protected void drawGhost(Graphics g) {
      IlvRect r = getDraggedRectangle();
      g.drawArc(Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height), 0, 360);
    }

  }

  /**
   * Ellipse map object
   */
  public static class MapEllipse extends IlvEllipse implements IlvMapGraphic {
    private static final String STYLE_FIELD = "style"; //$NON-NLS-1$
    /**
     * ellipse style
     */
    protected MapEllipseStyle style;

    /**
     * Creates a new <code>MapEllipse</code>.
     * @param r
     * @param b
     * @param c
     */
    public MapEllipse(IlvRect r, boolean b, boolean c) {
      super(r, b, c);
      IlvMapSelectionFactory.setEditable(this, true);
    }

    /**
     * Creates a new <code>MapEllipse</code>.
     * @param source
     */
    public MapEllipse(MapEllipse source) {
      super(source);
      IlvMapSelectionFactory.setEditable(this, true);
    }

    public IlvGraphic copy() {
      return new MapEllipse(this);
    }

    public void write(IlvOutputStream stream) throws IOException {
      super.write(stream);
      if(style != null)
        stream.write(STYLE_FIELD, style);
    }

    /**
     * Creates a new <code>MapEllipse</code>.
     * @param stream
     * @throws IlvReadFileException
     */
    public MapEllipse(IlvInputStream stream) throws IlvReadFileException {
      super(stream);
      try {
        setStyle((IlvMapStyle) stream.readPersistentObject(STYLE_FIELD));
      } catch (IlvFieldNotFoundException e) {
        // ignore
      }
    }

    /**
     * Applies the transformation. Labeling is performed if necessary.
     * 
     * @see ilog.views.graphic.IlvMarker#applyTransform(ilog.views.IlvTransformer)
     */
    public void applyTransform(IlvTransformer t) {
      super.applyTransform(t);
      IlvGraphicBag bag = getTopLevelGraphicBag();
      if(bag instanceof IlvManager) {
        IlvMapLabeler labeler = IlvMapLabelerProperty.GetMapLabeler((IlvManager) bag);
        labeler.performLabeling();
      }
    }

    public IlvMapStyle getStyle() {
      // if (style == null)
      // style = new MapEllipseStyle();
      return style;
    }

    public void setStyle(IlvMapStyle style) {
      this.style = (MapEllipseStyle) style;
    }

    public IlvGraphic copy(IlvCoordinateTransformation transform) throws IlvCoordinateTransformationException {
      IlvRect r = getDefinitionRect();
      IlvPoint p = new IlvPoint(r.x + r.width / 2f, r.y + r.height / 2f);
      p = IlvMapUtil.transform(p, transform);
      r = IlvMapUtil.transform(r, transform);
      r.setRect(p.x - r.width / 2f, p.y - r.height / 2f, r.width, r.height);
      MapEllipse rtn = (MapEllipse) copy();
      rtn.drawrect.reshape(r.x, r.y, r.width, r.height);
      IlvMapSelectionFactory.setEditable(rtn, true);
      return rtn;
    }

    public Color getBackground() {
      if(getEllipseStyle() != null)
        return getEllipseStyle().getBackground();
      return null;
    }

    public Color getForeground() {
      if(getEllipseStyle() != null)
        return getEllipseStyle().getForeground();
      return null;
    }

    public boolean isFillOn() {
      if(getEllipseStyle() != null)
        return getEllipseStyle().isFill();
      return false;
    }

    public boolean isStrokeOn() {
      if(getEllipseStyle() != null)
        return getEllipseStyle().isStroke();
      return false;
    }

    public void setBackground(Color color) {
      if(getEllipseStyle() != null)
        getEllipseStyle().setBackground(color);
    }

    public void setFillOn(boolean set) {
      if(!set)
        new Throwable().printStackTrace();
      if(getEllipseStyle() != null)
        getEllipseStyle().setFill(set);
    }

    public void setForeground(Color color) {
      if(getEllipseStyle() != null)
        getEllipseStyle().setForeground(color);
    }

    public void setStrokeOn(boolean set) {
      if(getEllipseStyle() != null)
        getEllipseStyle().setStroke(set);
    }

    private MapEllipseStyle getEllipseStyle() {
      return (MapEllipseStyle) getStyle();
    }

    public IlvMapGeometry makeGeometry() {
      IlvRect r = boundingBox();
      IlvCoordinate ul = new IlvCoordinate(r.x, -r.y - r.height);
      IlvCoordinate lr = new IlvCoordinate(r.x + r.width, -r.y);
      IlvMapEllipse geom = new IlvMapEllipse(ul, lr, r.width, r.height);
      return geom;
    }
  }

  /**
   * Style of ellipses
   */
  public static class MapEllipseStyle extends IlvMapStyle {
    /**
     * Line indicator attribute
     */
    public static final String STROKE = "stroke";//$NON-NLS-1$
        /**
         * Fill indicator attribute
         */
    public static final String FILL = "fill"; //$NON-NLS-1$
        /**
         * Foreground color attribute
         */
    public static final String FOREGROUND = "foreground"; //$NON-NLS-1$
        /**
         * Background color attribute
         */
    public static final String BACKGROUND = "background"; //$NON-NLS-1$

    /**
     * Creates a new <code>MapEllipseStyle</code>.
     * @param stream
     * @throws IlvReadFileException
     */
    public MapEllipseStyle(IlvInputStream stream) throws IlvReadFileException {
      super(stream);
      init();
      try {
        setStroke(stream.readBoolean(STROKE));
      } catch (IlvFieldNotFoundException e) {
        // ignore
      }
      try {
        setFill(stream.readBoolean(FILL));
      } catch (IlvFieldNotFoundException e) {
        // ignore
      }
      try {
        setForeground(stream.readColor(FOREGROUND));
      } catch (IlvFieldNotFoundException e) {/* ignore */
      }
      try {
        setBackground(stream.readColor(BACKGROUND));
      } catch (IlvFieldNotFoundException e) {/* ignore */
      }
    }

    public void write(IlvOutputStream stream) throws IOException {
      super.write(stream);
      Boolean b = (Boolean) getAttribute(STROKE, false);
      if(b != null)
        stream.write(STROKE, b.booleanValue());
      b = (Boolean) getAttribute(FILL, false);
      if(b != null)
        stream.write(FILL, b.booleanValue());
      Color c = (Color) getAttribute(FOREGROUND);
      if(c != null)
        stream.write(FOREGROUND, c);
      c = (Color) getAttribute(BACKGROUND);
      if(c != null)
        stream.write(BACKGROUND, c);
    }

    /**
     * Creates a new <code>MapEllipseStyle</code>.
     */
    public MapEllipseStyle() {
      super();
      init();
    }

    /**
     * Creates a new <code>MapEllipseStyle</code>.
     * @param source
     */
    public MapEllipseStyle(MapEllipseStyle source) {
      super(source);
      init();
      setAttribute(FILL, source.getAttribute(FILL, false));
      setAttribute(STROKE, source.getAttribute(STROKE, false));
      setAttribute(FOREGROUND, source.getAttribute(FOREGROUND, false));
      setAttribute(BACKGROUND, source.getAttribute(BACKGROUND, false));
    }

    public IlvMapStyle copy() {
      return new MapEllipseStyle(this);
    }

    private void init() {
      getDefaultValues().put(STROKE, Boolean.TRUE);
      getDefaultValues().put(FILL, Boolean.TRUE);
      getDefaultValues().put(FOREGROUND, Color.black);
      getDefaultValues().put(BACKGROUND, null);
    }

    /**
     * @return the background color
     */
    public Color getBackground() {
      return (Color) getAttribute(BACKGROUND, true);
    }

    /**
         * Sets the bakground color
     * @param c
     */
    public void setBackground(Color c) {
      setAttribute(BACKGROUND, c);
    }

    /**
     * @return the foreground color
     */
    public Color getForeground() {
      return (Color) getAttribute(FOREGROUND, true);
    }

    /**
         * Sets the foreground color
     * @param c
     */
    public void setForeground(Color c) {
      setAttribute(FOREGROUND, c);
    }

    /**
         * Indicates whether ellipse should be filled
     * @param fill
     */
    public void setFill(boolean fill) {
      setAttribute(FILL, Boolean.valueOf(fill));
    }

    /**
     * @return whether the ellipse is filled
     */
    public boolean isFill() {
      return ((Boolean) getAttribute(FILL, true)).booleanValue();
    }

    /**
         * Indicates whether ellipse should be stroked
     * @param stroke
     */
    public void setStroke(boolean stroke) {
      setAttribute(STROKE, Boolean.valueOf(stroke));
    }

    /**
     * @return whether the ellipse is stroked
     */
    public boolean isStroke() {
      return ((Boolean) getAttribute(STROKE, true)).booleanValue();
    }

  }
  //JV-3814
  Override
  public void destroy() { 
    super.destroy(); 
    // This method is intended to workaround memory management issues 
    // in the Sun JRE. Please refer to the method documentation for more 
    // details and a description of the known issues. 
    ilog.views.util.swing.IlvSwingUtil.cleanupApplet(); 
  } 

}