/**
 * Copyright (c) Artenum SARL 2004-2005
 * @author Sebastien Jourdain
 *
 * All rights reserved. This software can
 * not be used or copy or diffused without
 * an explicit license of Artenum SARL, Paris-France
 */
package com.artenum.cassandra.pipeline.graph;

import com.artenum.cassandra.pipeline.ConnectivityListener;
import com.artenum.cassandra.pipeline.Filter;
import com.artenum.cassandra.pipeline.PipeLineManager;
import com.artenum.cassandra.pipeline.CassandraObject;
import com.artenum.graph.impl.DefaultGraphModel;
import com.artenum.graph.interfaces.Cell;
import com.artenum.graph.interfaces.GraphViewListener;
import com.artenum.cassandra.plugin.PluginManager;


import vtk.vtkActor;
import vtk.vtkMapper;
import vtk.vtkScalarBarActor;

import java.util.Iterator;

import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

/**
 * This class is the connectivity listener related to the pipeline manager. Its update and maintained
 * the connectivity between the pipeline components. Currently, two models are defined: One liked to the 
 * pipeline manager and is stored into each VtkObject, the second is related to the graphical representation
 * through the DedalEGraph library. 
 * 
 * @author Sebastien Jourdain, Artenum SARL
 * @author Julien Forest, Artenum SARL
 * 
 * @version 2.0
 */
public class VtkPipeLineGraphModel extends DefaultGraphModel implements ListDataListener, ConnectivityListener {
    private PipeLineManager pipeLineManager;
    private PluginManager pluginManager;
    private int lastYPos = 0;

    public VtkPipeLineGraphModel(PipeLineManager pipeLineManager, PluginManager pluginManager) {
        this.pipeLineManager = pipeLineManager;
        this.pluginManager = pluginManager;
        pipeLineManager.getActorList().addListDataListener(this);
        pipeLineManager.getMapperList().addListDataListener(this);
        pipeLineManager.getDataSetList().addListDataListener(this);
        pipeLineManager.getFilterList().addListDataListener(this);
        pipeLineManager.getLookupTableList().addListDataListener(this);
        pipeLineManager.getScalarBarList().addListDataListener(this);
        pipeLineManager.getTextActorList().addListDataListener(this);
        pipeLineManager.addConnectivityListener(this);
    }

    public void contentsChanged(ListDataEvent e) {
        updateCell();
        updateConnection();
        reload();
    }

    public void reload() {
        // save pos
        Object[] cell = getCellList().toArray();
        for (int i = 0; i < cell.length; i++)
            ((VtkObjectCellAdapter) cell[i]).savePosition();
        super.reload();
    }

    /**
     * Align the CassandraObjectUI (i.e cell renderer) on the lowest component.
     * @param cellUI
     */
    public void alignVerticalPosition(VtkObjectUI cellUI){
        //System.out.println("DEBUG - XXXXXXXXXXXXXXXXXXXXXXXXXXXXX  In alignVerticalPosition   XXXXXXXXXXXXXXXXXXX");
        //System.out.println("DEBUG - Initial cell position:" + cellUI.getPosition().x +", " + cellUI.getPosition().y);
        if (cellUI.getPosition().y < lastYPos) {
            cellUI.getPosition().y = lastYPos;
        } else {
            lastYPos = cellUI.getPosition().y;
        }
        //System.out.println("DEBUG - Final cell position:" + cellUI.getPosition().x +", " + cellUI.getPosition().y);
    }
    
    public void updateCell() {
        CassandraObject tmp = null;
        VtkObjectCellAdapter tmpCell = null;
        
        // The actors list
        for (Iterator i = pipeLineManager.getActorList().getData().iterator(); i.hasNext();) {
            tmp = (CassandraObject) i.next();
            if (tmp.getMetaData().get(CassandraObject.CELL) == null) {
                tmpCell = new VtkObjectCellAdapter(tmp);
                tmp.getMetaData().put(CassandraObject.CELL, tmpCell);
            } else {
                tmpCell = (VtkObjectCellAdapter) tmp.getMetaData().get(CassandraObject.CELL);
            }

            if (!getCellList().contains(tmpCell)) {
                insertCell((Cell) tmpCell);
            }
        }

        // the mappers list
        for (Iterator i = pipeLineManager.getMapperList().getData().iterator(); i.hasNext();) {
            tmp = (CassandraObject) i.next();
            if (tmp.getMetaData().get(CassandraObject.CELL) == null) {
                tmpCell = new VtkObjectCellAdapter(tmp);
                tmp.getMetaData().put(CassandraObject.CELL, tmpCell);
            } else {
                tmpCell = (VtkObjectCellAdapter) tmp.getMetaData().get(CassandraObject.CELL);
            }

            if (!getCellList().contains(tmpCell)) {
                insertCell((Cell) tmpCell);
            }
        }

        // the data set list
        for (Iterator i = pipeLineManager.getDataSetList().getData().iterator(); i.hasNext();) {
            tmp = (CassandraObject) i.next();
            if (tmp.getMetaData().get(CassandraObject.CELL) == null) {
                tmpCell = new VtkObjectCellAdapter(tmp);
                tmp.getMetaData().put(CassandraObject.CELL, tmpCell);
                alignVerticalPosition((VtkObjectUI) tmpCell.getUI());
            } else {
                tmpCell = (VtkObjectCellAdapter) tmp.getMetaData().get(CassandraObject.CELL);
            }

            if (!getCellList().contains(tmpCell)) {
                insertCell((Cell) tmpCell);
            }
        }

        //the filters list
        for (Iterator i = pipeLineManager.getFilterList().getData().iterator(); i.hasNext();) {
            tmp = (CassandraObject) i.next();
            if (tmp.getMetaData().get(CassandraObject.CELL) == null) {
                tmpCell = new VtkObjectCellAdapter(tmp);
                tmp.getMetaData().put(CassandraObject.CELL, tmpCell);
                alignVerticalPosition((VtkObjectUI) tmpCell.getUI());
            } else {
                tmpCell = (VtkObjectCellAdapter) tmp.getMetaData().get(CassandraObject.CELL);
            }

            if (!getCellList().contains(tmpCell)) {
                insertCell((Cell) tmpCell);
            }
        }

        // the lookup table list
        for (Iterator i = pipeLineManager.getLookupTableList().getData().iterator(); i.hasNext();) {
            tmp = (CassandraObject) i.next();
            if (tmp.getMetaData().get(CassandraObject.CELL) == null) {
                tmpCell = new VtkObjectCellAdapter(tmp);
                tmp.getMetaData().put(CassandraObject.CELL, tmpCell);
            } else {
                tmpCell = (VtkObjectCellAdapter) tmp.getMetaData().get(CassandraObject.CELL);
            }

            if (!getCellList().contains(tmpCell)) {
                insertCell((Cell) tmpCell);
            }
        }

        for (Iterator i = pipeLineManager.getScalarBarList().getData().iterator(); i.hasNext();) {
            tmp = (CassandraObject) i.next();
            if (tmp.getMetaData().get(CassandraObject.CELL) == null) {
                tmpCell = new VtkObjectCellAdapter(tmp);
                tmp.getMetaData().put(CassandraObject.CELL, tmpCell);
            } else {
                tmpCell = (VtkObjectCellAdapter) tmp.getMetaData().get(CassandraObject.CELL);
            }

            if (!getCellList().contains(tmpCell)) {
                insertCell((Cell) tmpCell);
            }
        }

        for (Iterator i = pipeLineManager.getTextActorList().getData().iterator(); i.hasNext();) {
            tmp = (CassandraObject) i.next();
            if (tmp.getMetaData().get(CassandraObject.CELL) == null) {
                tmpCell = new VtkObjectCellAdapter(tmp);
                tmp.getMetaData().put(CassandraObject.CELL, tmpCell);
            } else {
                tmpCell = (VtkObjectCellAdapter) tmp.getMetaData().get(CassandraObject.CELL);
            }

            if (!getCellList().contains(tmpCell)) {
                insertCell((Cell) tmpCell);
            }
        }
    }

    /**
     * update the connection topology between cells (i.e VtkObject) and perform
     * the graphical connection through DedaleGraph
     */
    public void updateConnection() {
        
        // clear the connectivity
        getConnections().clear();
        Object[] cellList = getCellList().toArray();
        for (int i = 0; i < cellList.length; i++) {
            ((Cell) cellList[i]).getConnections().clear();
        }
        
        for (Iterator i = pipeLineManager.getActorList().getData().iterator(); i.hasNext();) {
            CassandraObject actor = ((CassandraObject) i.next());
            actor.clearInputConnectivityList();
            actor.clearOutputConnecvityLsit();
        }
        
        for (Iterator i = pipeLineManager.getDataSetList().getData().iterator(); i.hasNext();) {
            CassandraObject dataset = ((CassandraObject) i.next());
            dataset.clearInputConnectivityList();
            dataset.clearOutputConnecvityLsit();
        }
        
        for (Iterator iMap = pipeLineManager.getMapperList().getData().iterator(); iMap.hasNext();) {
            CassandraObject mapper = ((CassandraObject) iMap.next());
            mapper.clearInputConnectivityList();
            mapper.clearOutputConnecvityLsit();
        }
        
        for (Iterator i = pipeLineManager.getLookupTableList().getData().iterator(); i.hasNext();) {
            CassandraObject lookupTable = ((CassandraObject) i.next());
            lookupTable.clearInputConnectivityList();
            lookupTable.clearOutputConnecvityLsit();
        }
        
        for (Iterator i = pipeLineManager.getFilterList().getData().iterator(); i.hasNext();) {
            CassandraObject filter = ((CassandraObject) i.next());
            filter.clearInputConnectivityList();
            filter.clearOutputConnecvityLsit();
        }
        
        for (Iterator iScalar = pipeLineManager.getScalarBarList().getData().iterator(); iScalar.hasNext();) {
            CassandraObject scalarBar = ((CassandraObject) iScalar.next());
            scalarBar.clearInputConnectivityList();
            scalarBar.clearOutputConnecvityLsit();
        }
        

        // we scan all Actors to find their input mapper
        for (Iterator i = pipeLineManager.getActorList().getData().iterator(); i.hasNext();) {
            CassandraObject actor = ((CassandraObject) i.next());

            if (actor.getVtkObject() instanceof vtkActor) {
                vtkMapper actorMapper = ((vtkActor) actor.getVtkObject()).GetMapper();

                // we scan the mappers to find the corresponding one.
                for (Iterator iMap = pipeLineManager.getMapperList().getData().iterator(); iMap.hasNext();) {
                    CassandraObject mapper = ((CassandraObject) iMap.next());
                    if (mapper.getVtkObject().equals(actorMapper)) {

                        // we the update the DedalEgraph model for the view
                        // representation
                        connect((Cell) actor.getMetaData().get(CassandraObject.CELL), (Cell) mapper.getMetaData().get(CassandraObject.CELL));

                        // we update the connectivity between each VtkObject.
                        actor.addInputConnectivtiy(mapper.getId());
                        mapper.addOutputConnectivity(actor.getId());
                    }
                }
            }
        }

        // Dataset
        for (Iterator i = pipeLineManager.getDataSetList().getData().iterator(); i.hasNext();) {
            CassandraObject dataset = ((CassandraObject) i.next());

            for (Iterator iMap = pipeLineManager.getMapperList().getData().iterator(); iMap.hasNext();) {
                CassandraObject mapper = ((CassandraObject) iMap.next());
                if (((vtkMapper) mapper.getVtkObject()).GetInputAsDataSet() == null) {
                    continue;
                }

                if (((vtkMapper) mapper.getVtkObject()).GetInputAsDataSet().equals(dataset.getVtkObject())) {

                    // we the update the DedalEgraph model for the view
                    // representation
                    connect((Cell) dataset.getMetaData().get(CassandraObject.CELL), (Cell) mapper.getMetaData().get(CassandraObject.CELL));

                    // we update the connectivity between each VtkObject
                    mapper.addInputConnectivtiy(dataset.getId());
                    dataset.addOutputConnectivity(mapper.getId());
                }
            }
        }

        // LookupTable
        for (Iterator i = pipeLineManager.getLookupTableList().getData().iterator(); i.hasNext();) {
            CassandraObject lookupTable = ((CassandraObject) i.next());

            // Mapper link
            for (Iterator iMap = pipeLineManager.getMapperList().getData().iterator(); iMap.hasNext();) {
                CassandraObject mapper = ((CassandraObject) iMap.next());

                if (((vtkMapper) mapper.getVtkObject()).GetLookupTable() == null) {
                    continue;
                }

                if (((vtkMapper) mapper.getVtkObject()).GetLookupTable().equals(lookupTable.getVtkObject())) {
                    // we the update the DedalEgraph model for the view
                    // representation
                    connect((Cell) lookupTable.getMetaData().get(CassandraObject.CELL), (Cell) mapper.getMetaData().get(CassandraObject.CELL));
                    // we update the connectivity between each VtkObject
                    mapper.addOutputConnectivity(lookupTable.getId());
                    lookupTable.addInputConnectivtiy(mapper.getId());
                }
            }

            for (Iterator iScalar = pipeLineManager.getScalarBarList().getData().iterator(); iScalar.hasNext();) {
                CassandraObject scalarBar = ((CassandraObject) iScalar.next());
                if (((vtkScalarBarActor) scalarBar.getVtkObject()).GetLookupTable() == null) {
                    continue;
                }

                if (((vtkScalarBarActor) scalarBar.getVtkObject()).GetLookupTable().equals(lookupTable.getVtkObject())) {
                    connect((Cell) lookupTable.getMetaData().get(CassandraObject.CELL), (Cell) scalarBar.getMetaData().get(CassandraObject.CELL));

                    lookupTable.addOutputConnectivity(scalarBar.getId());
                    scalarBar.addInputConnectivtiy(lookupTable.getId());
                }
            }
        }

        // Filter
        for (Iterator i = pipeLineManager.getFilterList().getData().iterator(); i.hasNext();) {
            CassandraObject filter = ((CassandraObject) i.next());

            Object vtkConnection;
            
            // filters to mappers connectivity
            for (Iterator iMap = pipeLineManager.getMapperList().getData().iterator(); iMap.hasNext();) {
                CassandraObject mapper = ((CassandraObject) iMap.next());

                // for filters with an input mapper 
                for (Iterator fMapperInIterator = ((Filter) filter.getVtkObject()).getInputMapper().iterator(); fMapperInIterator.hasNext();) {
                    vtkConnection = fMapperInIterator.next();
                    if (vtkConnection.equals(mapper.getVtkObject())) {
                        connect((Cell) filter.getMetaData().get(CassandraObject.CELL), (Cell) mapper.getMetaData().get(CassandraObject.CELL));
                        
                        //
                        filter.addInputConnectivtiy(mapper.getId());
                        mapper.addOutputConnectivity(filter);
                    }

                }

                // for filters with an output mapper
                for (Iterator fMapperOutIterator = ((Filter) filter.getVtkObject()).getOutputMapper().iterator(); fMapperOutIterator.hasNext();) {
                    vtkConnection = fMapperOutIterator.next();
                    if (vtkConnection.equals(mapper.getVtkObject())) {
                        connect((Cell) filter.getMetaData().get(CassandraObject.CELL), (Cell) mapper.getMetaData().get(CassandraObject.CELL));
                        
                        //
                        filter.addOutputConnectivity(mapper.getId());
                        mapper.addInputConnectivtiy(filter.getId());
                    }
                }
            }

            // filters to actors connectivity
            for (Iterator iActor = pipeLineManager.getActorList().getData().iterator(); iActor.hasNext();) {
                CassandraObject actor = ((CassandraObject) iActor.next());
                
                // for filters with an actor as input
                for (Iterator fMapperInIterator = ((Filter) filter.getVtkObject()).getInputActor().iterator(); fMapperInIterator.hasNext();) {
                    vtkConnection = fMapperInIterator.next();
                    if (vtkConnection.equals(actor.getVtkObject())) {
                        connect((Cell) filter.getMetaData().get(CassandraObject.CELL), (Cell) actor.getMetaData().get(CassandraObject.CELL));
                        
                        //
                        filter.addInputConnectivtiy(actor.getId());
                        actor.addOutputConnectivity(filter.getId());
                    }
                }

                // for filter with an actor as output
                for (Iterator fMapperOutIterator = ((Filter) filter.getVtkObject()).getOutputActor().iterator(); fMapperOutIterator.hasNext();) {
                    vtkConnection = fMapperOutIterator.next();
                    if (vtkConnection.equals(actor.getVtkObject())) {
                        connect((Cell) filter.getMetaData().get(CassandraObject.CELL), (Cell) actor.getMetaData().get(CassandraObject.CELL));
                        
                        //
                        filter.addOutputConnectivity(actor.getId());
                        actor.addInputConnectivtiy(filter.getId());
                    }
                }
            }

            // filter to data set connectivity
            for (Iterator iDataSet = pipeLineManager.getDataSetList().getData().iterator(); iDataSet.hasNext();) {
                CassandraObject dataset = ((CassandraObject) iDataSet.next());
                
                // for filters with a data set as input
                for (Iterator fMapperInIterator = ((Filter) filter.getVtkObject()).getInputDataSet().iterator(); fMapperInIterator.hasNext();) {
                    vtkConnection = fMapperInIterator.next();
                    if (vtkConnection.equals(dataset.getVtkObject())) {
                        connect((Cell) filter.getMetaData().get(CassandraObject.CELL), (Cell) dataset.getMetaData().get(CassandraObject.CELL));
                        
                        //
                        filter.addInputConnectivtiy(dataset.getId());
                        dataset.addOutputConnectivity(filter.getId());
                    }
                }

                // for filters with a data set as output
                for (Iterator fMapperOutIterator = ((Filter) filter.getVtkObject()).getOutputDataSet().iterator(); fMapperOutIterator.hasNext();) {
                    vtkConnection = fMapperOutIterator.next();
                    if (vtkConnection.equals(dataset.getVtkObject())) {
                        connect((Cell) filter.getMetaData().get(CassandraObject.CELL), (Cell) dataset.getMetaData().get(CassandraObject.CELL));
                        
                        //
                        filter.addOutputConnectivity(dataset.getId());
                        dataset.addInputConnectivtiy(filter.getId());
                    }
                }
            }
        }
    }

    public void intervalAdded(ListDataEvent e) {
        updateCell();
        updateConnection();
        reload();
    }

    public void intervalRemoved(ListDataEvent e) {
        VtkObjectCellAdapter cellAdapter = null;
        Object[] vtkObjectCellAdapterList = getCellList().toArray();
        for (int index = 0; index < vtkObjectCellAdapterList.length; index++) {
            cellAdapter = (VtkObjectCellAdapter) vtkObjectCellAdapterList[index];
            if (pipeLineManager.getVtkObject(cellAdapter.getVtkObject().getId()) == null) {
                for (Iterator i = viewListenerList.iterator(); i.hasNext();) {
                    ((GraphViewListener) i.next()).removeCell(cellAdapter);
                }

                getCellList().remove(cellAdapter);
            }
        }

        updateCell();
        updateConnection();
        reload();
    }

    public void connectivityChange(CassandraObject vtkObject) {
        updateConnection();
        reload();
    }

    /**
     * return the pipelineManager
     * 
     * @return pipeLineManager
     */
    public PipeLineManager getPipelineManager() {
        return pipeLineManager;
    }

    public PluginManager getPluginManager() {
        return pluginManager;
    }
}
