/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.jme3.gde.nmgen;



import com.jme3.gde.core.assets.AssetDataObject;
import com.jme3.gde.core.assets.ProjectAssetManager;
import com.jme3.gde.core.scene.PreviewRequest;
import com.jme3.gde.core.scene.SceneApplication;
import com.jme3.gde.core.scene.SceneListener;
import com.jme3.gde.core.scene.SceneRequest;
import com.jme3.gde.core.sceneexplorer.nodes.JmeNode;
import com.jme3.gde.core.sceneexplorer.nodes.JmeSpatial;
import com.jme3.gde.core.sceneexplorer.nodes.NodeUtility;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import java.util.Collection;
import java.util.logging.Logger;
import org.openide.util.LookupEvent;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.util.ImageUtilities;
import org.openide.util.Lookup.Result;
import org.openide.util.LookupListener;
import org.openide.util.Utilities;
import org.openide.windows.WindowManager;



/**
 * Top component which displays the Nav Mesh editor.
 * 
 * http://critterai.org/javadoc/nmgen/org/critterai/nmgen/NavmeshGenerator.html
 */
@ConvertAsProperties(
    dtd="-//com.sploreg.tritium.editor.navmesh//NavMesh//EN",
    autostore=false
)
public final class NavMeshTopComponent extends TopComponent implements SceneListener, LookupListener {
    
    private static NavMeshTopComponent instance;
    static final String ICON_PATH = "com/sploreg/tritium/editor/navmesh/logo.png";
    private static final String PREFERRED_ID = "NavMeshTopComponent";
    private SceneRequest currentRequest;
    private final Result<JmeSpatial> result;
    private NavMeshController editorController;
    private NavMeshToolController toolController;
    private NavMeshCameraController cameraController;
    private JmeSpatial selectedSpat;
    
    public NavMeshTopComponent() {
        initComponents();
        setName(NbBundle.getMessage(NavMeshTopComponent.class, "CTL_NavMeshTopComponent"));
        setToolTipText(NbBundle.getMessage(NavMeshTopComponent.class, "HINT_NavMeshTopComponent"));
        setIcon(ImageUtilities.loadImage(ICON_PATH, true));
        result = Utilities.actionsGlobalContext().lookupResult(JmeSpatial.class);
    }

    /**
     * Gets default instance. Do not use directly: reserved for *.settings files only,
     * i.e. deserialization routines; otherwise you could get a non-deserialized instance.
     * To obtain the singleton instance, use {@link #findInstance}.
     */
    public static synchronized NavMeshTopComponent getDefault() {
        if (instance == null) {
            instance = new NavMeshTopComponent();
        }
        return instance;
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        jPanel1 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        cellSizeField = new javax.swing.JTextField();
        jLabel2 = new javax.swing.JLabel();
        cellHeightField = new javax.swing.JTextField();
        jLabel3 = new javax.swing.JLabel();
        minTraversableHeightField = new javax.swing.JTextField();
        jLabel4 = new javax.swing.JLabel();
        maxTraversableStepField = new javax.swing.JTextField();
        jLabel5 = new javax.swing.JLabel();
        maxTraversableSlopeField = new javax.swing.JTextField();
        jLabel6 = new javax.swing.JLabel();
        clipLedgesBox = new javax.swing.JCheckBox();
        jLabel7 = new javax.swing.JLabel();
        traversableAreaBorderSizeField = new javax.swing.JTextField();
        jLabel8 = new javax.swing.JLabel();
        smoothingThresholdField = new javax.swing.JTextField();
        jLabel9 = new javax.swing.JLabel();
        useConservativeExpansionBox = new javax.swing.JCheckBox();
        jLabel10 = new javax.swing.JLabel();
        mergeRegionSizeField = new javax.swing.JTextField();
        jLabel11 = new javax.swing.JLabel();
        maxEdgeLengthField = new javax.swing.JTextField();
        jLabel12 = new javax.swing.JLabel();
        edgeMaxDeviationField = new javax.swing.JTextField();
        jLabel13 = new javax.swing.JLabel();
        maxVertsPerPolyField = new javax.swing.JTextField();
        jLabel14 = new javax.swing.JLabel();
        contourSampleDistanceField = new javax.swing.JTextField();
        jLabel15 = new javax.swing.JLabel();
        contourMaxDeviationField = new javax.swing.JTextField();
        jLabel16 = new javax.swing.JLabel();
        minUnconnectedRegionSizeField = new javax.swing.JTextField();
        jToolBar1 = new javax.swing.JToolBar();
        jButton1 = new javax.swing.JButton();

        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, "Cell Size");
        jLabel1.setToolTipText("The width and depth resolution used when sampling the source mesh");

        cellSizeField.setText("1");
        cellSizeField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cellSizeFieldActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, "Cell Height");
        jLabel2.setToolTipText("The height resolution used when sampling the source mesh");

        cellHeightField.setText("1.5");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel3, "Min Traversable Height");
        jLabel3.setToolTipText("Represents the minimum floor to ceiling height that will still allow the floor area to be considered walkable");

        minTraversableHeightField.setText("7.5");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel4, "Max Traversable Step");
        jLabel4.setToolTipText("Represents the maximum ledge height that is considered to still be walkable");

        maxTraversableStepField.setText("1");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel5, "Max Traversable Slope");
        jLabel5.setToolTipText("The maximum slope that is considered walkable. (Degrees)");

        maxTraversableSlopeField.setText("48");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel6, "Clip Ledges");
        jLabel6.setToolTipText("Indicates whether ledges should be marked as unwalkable");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel7, "Traversable Area Border Size");
        jLabel7.setToolTipText("Represents the closest any part of the navmesh can get to an obstruction in the source mesh");

        traversableAreaBorderSizeField.setText("1.2");
        traversableAreaBorderSizeField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                traversableAreaBorderSizeFieldActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jLabel8, "Smoothing Threshold");
        jLabel8.setToolTipText("The amount of smoothing to be performed when generating the distance field");

        smoothingThresholdField.setText("2");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel9, "Use Conservative Expansion");
        jLabel9.setToolTipText("Applies extra algorithms to regions to help prevent poorly formed regions from forming. Enabling this feature significantly increased processing cost.");

        useConservativeExpansionBox.setSelected(true);

        org.openide.awt.Mnemonics.setLocalizedText(jLabel10, "Merge Region Size");
        jLabel10.setToolTipText("Any regions smaller than this size will, if possible, be merged with larger regions. (Voxels)");

        mergeRegionSizeField.setText("10");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel11, "Max Edge Length");
        jLabel11.setToolTipText("The maximum length of polygon edges that represent the border of the navmesh");

        maxEdgeLengthField.setText("0");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel12, "Edge Max Deviation");
        jLabel12.setToolTipText("The maximum distance the edge of the navmesh may deviate from the source geometry. Setting this lower will result in the navmesh edges following the geometry contour more accurately at the expense of an increased triangle count.");

        edgeMaxDeviationField.setText("2.4");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel13, "Max Verts Per Poly");
        jLabel13.setToolTipText("The maximum number of vertices per polygon for polygons generated during the voxel to polygon conversion stage.  Higher values reduce performance, but can also result in better formed triangles in the navmesh. A value of around 6 is generally adequate with diminishing returns for values higher than 6.");

        maxVertsPerPolyField.setText("6");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel14, "Contour Sample Distance");
        jLabel14.setToolTipText("Sets the sampling distance to use when matching the navmesh to the surface of the original geometry. Impacts how well the final mesh conforms to the original geometry's surface contour. Higher values result in a navmesh which conforms more closely to the original geometry's surface at the cost of a higher final triangle count and higher processing cost");

        contourSampleDistanceField.setText("25");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel15, "Contour Max Deviation");
        jLabel15.setToolTipText("The maximum distance the surface of the navmesh may deviate from the surface of the original geometry.");

        contourMaxDeviationField.setText("25");

        org.openide.awt.Mnemonics.setLocalizedText(jLabel16, "Min Unconnected Region Size");
        jLabel16.setToolTipText("The minimum region size for unconnected (island) regions. (Voxels) ");

        minUnconnectedRegionSizeField.setText("3");

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jLabel16)
                    .addComponent(jLabel15)
                    .addComponent(jLabel14)
                    .addComponent(jLabel13)
                    .addComponent(jLabel12)
                    .addComponent(jLabel11)
                    .addComponent(jLabel10)
                    .addComponent(jLabel9)
                    .addComponent(jLabel8)
                    .addComponent(jLabel7)
                    .addComponent(jLabel6)
                    .addComponent(jLabel5)
                    .addComponent(jLabel4)
                    .addComponent(jLabel3)
                    .addComponent(jLabel2)
                    .addComponent(jLabel1))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(minUnconnectedRegionSizeField)
                    .addComponent(contourMaxDeviationField)
                    .addComponent(contourSampleDistanceField)
                    .addComponent(maxVertsPerPolyField)
                    .addComponent(edgeMaxDeviationField)
                    .addComponent(maxEdgeLengthField)
                    .addComponent(smoothingThresholdField)
                    .addComponent(maxTraversableSlopeField)
                    .addComponent(maxTraversableStepField)
                    .addComponent(minTraversableHeightField)
                    .addComponent(cellSizeField, javax.swing.GroupLayout.DEFAULT_SIZE, 58, Short.MAX_VALUE)
                    .addComponent(cellHeightField)
                    .addComponent(clipLedgesBox)
                    .addComponent(traversableAreaBorderSizeField)
                    .addComponent(useConservativeExpansionBox)
                    .addComponent(mergeRegionSizeField))
                .addContainerGap(594, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGap(31, 31, 31)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(cellSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel1))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel2)
                    .addComponent(cellHeightField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel3)
                    .addComponent(minTraversableHeightField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel4)
                    .addComponent(maxTraversableStepField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel5)
                    .addComponent(maxTraversableSlopeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel6)
                    .addComponent(clipLedgesBox))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel7)
                    .addComponent(traversableAreaBorderSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel8)
                    .addComponent(smoothingThresholdField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel9)
                    .addComponent(useConservativeExpansionBox))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel10)
                    .addComponent(mergeRegionSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel11)
                    .addComponent(maxEdgeLengthField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel12)
                    .addComponent(edgeMaxDeviationField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel13)
                    .addComponent(maxVertsPerPolyField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel14)
                    .addComponent(contourSampleDistanceField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel15)
                    .addComponent(contourMaxDeviationField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel16)
                    .addComponent(minUnconnectedRegionSizeField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(57, Short.MAX_VALUE))
        );

        jScrollPane1.setViewportView(jPanel1);

        jToolBar1.setRollover(true);

        org.openide.awt.Mnemonics.setLocalizedText(jButton1, "Generate");
        jButton1.setFocusable(false);
        jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });
        jToolBar1.add(jButton1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jToolBar1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 664, Short.MAX_VALUE)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 664, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jToolBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 194, javax.swing.GroupLayout.PREFERRED_SIZE))
        );
    }// </editor-fold>//GEN-END:initComponents

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
    generateNavMesh();
}//GEN-LAST:event_jButton1ActionPerformed

private void cellSizeFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cellSizeFieldActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_cellSizeFieldActionPerformed

private void traversableAreaBorderSizeFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_traversableAreaBorderSizeFieldActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_traversableAreaBorderSizeFieldActionPerformed


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTextField cellHeightField;
    private javax.swing.JTextField cellSizeField;
    private javax.swing.JCheckBox clipLedgesBox;
    private javax.swing.JTextField contourMaxDeviationField;
    private javax.swing.JTextField contourSampleDistanceField;
    private javax.swing.JTextField edgeMaxDeviationField;
    private javax.swing.JButton jButton1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel10;
    private javax.swing.JLabel jLabel11;
    private javax.swing.JLabel jLabel12;
    private javax.swing.JLabel jLabel13;
    private javax.swing.JLabel jLabel14;
    private javax.swing.JLabel jLabel15;
    private javax.swing.JLabel jLabel16;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel jLabel8;
    private javax.swing.JLabel jLabel9;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JToolBar jToolBar1;
    private javax.swing.JTextField maxEdgeLengthField;
    private javax.swing.JTextField maxTraversableSlopeField;
    private javax.swing.JTextField maxTraversableStepField;
    private javax.swing.JTextField maxVertsPerPolyField;
    private javax.swing.JTextField mergeRegionSizeField;
    private javax.swing.JTextField minTraversableHeightField;
    private javax.swing.JTextField minUnconnectedRegionSizeField;
    private javax.swing.JTextField smoothingThresholdField;
    private javax.swing.JTextField traversableAreaBorderSizeField;
    private javax.swing.JCheckBox useConservativeExpansionBox;
    // End of variables declaration//GEN-END:variables

    @Override
    public void componentOpened() {
        super.componentOpened();
        if (currentRequest == null) {
            close();
        }
    }

    @Override
    public void componentClosed() {
        super.componentClosed();
        if (currentRequest != null) {
            SceneApplication.getApplication().closeScene(currentRequest);
        }
    }

    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }
    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version
    }
    
     /**
     * Obtain the TerrainEditorTopComponent instance. Never call {@link #getDefault} directly!
     */
    public static synchronized NavMeshTopComponent findInstance() {
        TopComponent win = WindowManager.getDefault().findTopComponent(PREFERRED_ID);
        if (win == null) {
            Logger.getLogger(NavMeshTopComponent.class.getName()).warning(
                    "Cannot find " + PREFERRED_ID + " component. It will not be located properly in the window system.");
            return getDefault();
        }
        if (win instanceof NavMeshTopComponent) {
            return (NavMeshTopComponent) win;
        }
        Logger.getLogger(NavMeshTopComponent.class.getName()).warning(
                "There seem to be multiple components with the '" + PREFERRED_ID
                + "' ID. That is a potential source of errors and unexpected behavior.");
        return getDefault();
    }
    
    // run on swing thread
    public void openScene(Spatial spat, AssetDataObject file, ProjectAssetManager manager) {
        cleanupControllers();
        SceneApplication.getApplication().addSceneListener(this);
        result.addLookupListener(this);
        //TODO: handle request change
        Node node;
        if (spat instanceof Node) {
            node = (Node) spat;
        } else {
            node = new Node();
            node.attachChild(spat);
        }
        JmeNode jmeNode = NodeUtility.createNode(node, file, false);
        SceneRequest request = new SceneRequest(this, jmeNode, manager);
        request.setDataObject(file);

        addSaveNode(jmeNode);

        Logger.getLogger(NavMeshTopComponent.class.getName()).finer("NavMesh openScene " + file.getName());

        if (editorController != null) {
            editorController.cleanup();
        }
        editorController = new NavMeshController(jmeNode, file, this);
        this.currentRequest = request;
        request.setWindowTitle("NavMesh - " + manager.getRelativeAssetPath(file.getPrimaryFile().getPath()));
        request.setToolNode(new Node("NavMeshEditorToolNode"));
        SceneApplication.getApplication().openScene(request);
    }

    @Override
    public void previewCreated(PreviewRequest request) {
    }
    
    // run on GL thread
    /*@Override
    public void sceneRequested(SceneRequest request) {
        if (request.equals(currentRequest)) {
            Logger.getLogger(NavMeshTopComponent.class.getName()).finer("Terrain sceneRequested " + request.getWindowTitle());

            setSceneInfo(currentRequest.getJmeNode(), true);
            
            if (cameraController != null) {
                cameraController.disable();
            }
            if (toolController != null) {
                toolController.cleanup();
            }

            //for (int i=0; i<textureTable.getModel().getRowCount(); i++)
            //    ((TextureTableModel)textureTable.getModel()).removeRow(i);

            toolController = new NavMeshToolController(currentRequest.getToolNode(), currentRequest.getManager().getManager(), request.getJmeNode());
            
            cameraController = new NavMeshCameraController(SceneApplication.getApplication().getCamera());
            cameraController.setMaster(this);
            cameraController.enable();
            cameraController.setToolController(toolController);
            cameraController.setEditorController(editorController);
           
            toolController.setEditorController(editorController);
            toolController.setCameraController(cameraController);
            
            initUIFields();
            Mesh debugMesh = editorController.createDebugMesh();
            toolController.attachNavMesh(debugMesh);
        }
    }*/

    @Override
    public void sceneClosed(SceneRequest request) {
        if (request.equals(currentRequest)) {
            SceneApplication.getApplication().removeSceneListener(this);
            setSceneInfo(null, false);
            currentRequest = null;
            cleanupControllers();
        }
    }

    @Override
    public void resultChanged(LookupEvent ev) {
        if (currentRequest == null || !currentRequest.isDisplayed()) {
            return;
        }
        Collection<JmeSpatial> items = (Collection<JmeSpatial>) result.allInstances();
        for (JmeSpatial spatial : items) {
            selectSpatial(spatial);
            return;
        }
    }
    
    private void setSceneInfo(final JmeNode jmeNode, final boolean active) {
        final NavMeshTopComponent inst = this;
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                if (jmeNode != null) {
                } else {
                }

                if (!active) {
                    result.removeLookupListener(inst);
                    close();
                } else {
                    open();
                    requestActive();
                }
            }
        });
    }
    
    protected void addSaveNode(org.openide.nodes.Node node) {
        setActivatedNodes(new org.openide.nodes.Node[]{node});
    }
    
    private void cleanupControllers() {
        if (cameraController != null) {
            cameraController.disable();
            cameraController = null;
        }
        if (toolController != null) {
            toolController.cleanup();
            toolController = null;
        }
        if (editorController != null) {
            editorController.cleanup();
            editorController = null;
        }
        setActivatedNodes(new org.openide.nodes.Node[]{});
    }
    
    private void selectSpatial(JmeSpatial spatial) {
        selectedSpat = spatial;
    }

    private void generateNavMesh() {
        float cellHeight = Float.parseFloat(cellHeightField.getText());
        float cellSize = Float.parseFloat(cellSizeField.getText());
        boolean clipLedges = clipLedgesBox.isSelected();
        float contourMaxDeviation = Float.parseFloat(contourMaxDeviationField.getText());
        float contourSampleDistance = Float.parseFloat(contourSampleDistanceField.getText());
        float edgeMaxDeviation = Float.parseFloat(edgeMaxDeviationField.getText());
        float maxEdgeLength = Float.parseFloat(maxEdgeLengthField.getText());
        float maxTraversableSlope = Float.parseFloat(maxTraversableSlopeField.getText());
        float maxTraversableStep = Float.parseFloat(maxTraversableStepField.getText());
        float maxVertsPerPoly = Float.parseFloat(maxVertsPerPolyField.getText());
        float mergeRegionSize = Float.parseFloat(mergeRegionSizeField.getText());
        float minTraversableHeight = Float.parseFloat(minTraversableHeightField.getText());
        float smoothingThreshold = Float.parseFloat(smoothingThresholdField.getText());
        float traversableAreaBorderSize = Float.parseFloat(traversableAreaBorderSizeField.getText());
        boolean useConservativeExpansion = useConservativeExpansionBox.isSelected();
        float minUnconnectedRegionSize = Float.parseFloat(minUnconnectedRegionSizeField.getText());
        
        Mesh debugMesh = editorController.generateNavMesh(cellSize, cellHeight, minTraversableHeight,
                maxTraversableStep, maxTraversableSlope,
                clipLedges, traversableAreaBorderSize,
                smoothingThreshold, useConservativeExpansion,
                minUnconnectedRegionSize, mergeRegionSize,
                maxEdgeLength, edgeMaxDeviation, maxVertsPerPoly,
                contourSampleDistance, contourMaxDeviation);
        
        toolController.attachNavMesh(debugMesh);
    }
    

    private void initUIFields() {
        NavMeshGenerator generator = editorController.getNavMeshGenerator();
        if (generator != null) {
            cellSizeField.setText(""+generator.getCellSize());
            cellHeightField.setText(""+generator.getCellHeight());
            minTraversableHeightField.setText(""+generator.getMinTraversableHeight());
            maxTraversableStepField.setText(""+generator.getMaxTraversableStep());
            maxTraversableSlopeField.setText(""+generator.getMaxTraversableSlope());
            clipLedgesBox.setSelected(generator.isClipLedges());
            traversableAreaBorderSizeField.setText(""+generator.getTraversableAreaBorderSize());
            smoothingThresholdField.setText(""+generator.getSmoothingThreshold());
            useConservativeExpansionBox.setSelected(generator.isUseConservativeExpansion());
            minUnconnectedRegionSizeField.setText(""+generator.getMinUnconnectedRegionSize());
            mergeRegionSizeField.setText(""+generator.getMergeRegionSize());
            maxEdgeLengthField.setText(""+generator.getMaxEdgeLength());
            edgeMaxDeviationField.setText(""+generator.getEdgeMaxDeviation());
            maxVertsPerPolyField.setText(""+generator.getMaxVertsPerPoly());
            contourSampleDistanceField.setText(""+generator.getContourSampleDistance());
            contourMaxDeviationField.setText(""+generator.getContourMaxDeviation());
        }
    }

    @Override
    public void sceneOpened(SceneRequest request) {
        if (request.equals(currentRequest)) {
            Logger.getLogger(NavMeshTopComponent.class.getName()).finer("Terrain sceneRequested " + request.getWindowTitle());

            setSceneInfo(currentRequest.getJmeNode(), true);
            
            if (cameraController != null) {
                cameraController.disable();
            }
            if (toolController != null) {
                toolController.cleanup();
            }

            //for (int i=0; i<textureTable.getModel().getRowCount(); i++)
            //    ((TextureTableModel)textureTable.getModel()).removeRow(i);

            toolController = new NavMeshToolController(currentRequest.getToolNode(), currentRequest.getManager().getManager(), request.getJmeNode());
            
            cameraController = new NavMeshCameraController(SceneApplication.getApplication().getCamera());
            cameraController.setMaster(this);
            cameraController.enable();
            cameraController.setToolController(toolController);
            cameraController.setEditorController(editorController);
           
            toolController.setEditorController(editorController);
            toolController.setCameraController(cameraController);
            
            initUIFields();
            //Mesh debugMesh = editorController.createDebugMesh();
            //toolController.attachNavMesh(debugMesh);
        }
    }

}
