/* Copyright (c) 1997-2004
 Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
 http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de
 
 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
 Free Software Foundation; either version 2, or (at your option) any
 later version: http://www.gnu.org/licenses/gpl.txt.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 $Project: polymake $$Id: EmbeddedGeometries.java,v 1.6 2005/11/02 15:05:52 thilosch Exp $
 */

package de.tuberlin.polymake.common;

import java.awt.Color;
import java.util.HashMap;
import java.util.Vector;

import jv.geom.PgPointSet;
import jv.object.PsObject;
import jv.project.PgGeometry;
import jv.vecmath.PdVector;

/**
 * This class implements a set of <code>EmbeddedGeometry</code>'s all whose coordinates
 * depend on a unique embedding. Manipulating any of the geometries will manipulate all
 * geometries which are dynamically embedded.
 *
 * @author Thilo Schr&ouml;der
 * @version 1.0
 */
public class EmbeddedGeometries {
    
    /**
     * The underlying <code>PgPointSet</code> containing the coordinates on which all
     * <code>EmbeddedGeometry</code>'s of <code>geometries</code> are embedded.
     */
    protected PgPointSet embedding = new PgPointSet();
    
    /**
     * Maps the names of the <code>EmbeddedGeometry</code>'s to their index in <code>geometries</code>.
     */
    protected HashMap geomIndexMap = new HashMap(8);
    
    /**
     * An array containing <code>EmbeddedGeometry</code>'s all embedded on the same <code>embedding</code>.
     */
    protected EmbeddedGeometry[] geometries;
    
    /**
     * The name of the entire geometric structure.
     */
    protected String name = "NoName";
    
    /**
     * Creates a new <code>EmbeddedGeometries</code> instance.
     *
     * @param title title of the set of geometries
     * @param geoms the set of geometries
     */
    public EmbeddedGeometries(String title, EmbeddedGeometry[] geoms) {
        name = title;
        geometries = new EmbeddedGeometry[geoms.length];
        for(int i = 0; i < geometries.length; ++i) {
            geometries[i] = (EmbeddedGeometry)geoms[i].clone();
            geomIndexMap.put(geometries[i].getName(),new Integer(i));
        }
    }
    
    /**
     * Creates a new <code>EmbeddedGeometries</code> instance.
     *
     * @param title title of the set of geometries
     * @param emb   the underlying <code>PgPointSet</code>
     * @param geoms the set of geometries
     */
    public EmbeddedGeometries(String title, PgPointSet emb, EmbeddedGeometry[] geoms) {
        name = title;
        if(emb != null) {
            embedding = (PgPointSet)emb.clone();
        }
        embedding.setName(title);
        geometries = new EmbeddedGeometry[geoms.length];
        for(int i = 0; i < geometries.length; ++i) {
            geometries[i] = (EmbeddedGeometry)geoms[i].clone();
            geomIndexMap.put(geometries[i].getName(),new Integer(i));
        }
    }
    
    /**
     * Move a vertex with index <code>index</code> of the <code>EmbeddedGeometry</code>
     * with name <code>geomName</code> to its new <code>coords</code> and update the embedding
     * and all other geometries on the same embedding.
     *
     * @param geomName the name of the geometry
     * @param index    index of the vertex in the geometry <code>geomName</code>
     * @param coords   the new coordinates of the vertex
     */
    public void moveVertex(String geomName,int index, PdVector coords) {
        int geomIndex = ((Integer)geomIndexMap.get(geomName)).intValue();
        int vertexIndex = geometries[geomIndex].getVertexIndex(index);
        embedding.setVertex(vertexIndex,coords);
    }
    
    /**
     * Get a vertex by <code>geomName</code> and <code>index</code>.
     *
     * @param geomName the name of the geometry
     * @param index    the index of the vertex in this geometry
     * @return         the vertex
     */
    public PdVector getEmbeddedVertex(String geomName, int index) {
        int geomIndex = ((Integer)geomIndexMap.get(geomName)).intValue();
        int vertexIndex = geometries[geomIndex].getVertexIndex(index);
        PdVector tmpVertex = embedding.getVertex(vertexIndex);
        tmpVertex.setName(Integer.toString(vertexIndex));
        return tmpVertex;
    }
    
    /**
     * Update the underlying embedding of all geometries.
     *
     * @param ps       the new coordinates of the embedding
     * @param clearTag if true, all vertexTags are removed
     */
    public void update(PointSet ps, boolean clearTag) {
        name = ps.getName();
        embedding.setName(name);
        Color[] colors = embedding.getVertexColors();
        if(embedding.getNumVertices() <= ps.getNPoints()) {
            embedding.setNumVertices(ps.getNPoints());
        }
        for(int i = 0; i < ps.getNPoints(); ++i) {
            PolymakePoint point = ps.getPoint(i);
            if(point.getLabel() != null) {
                embedding.setVertex(Integer.parseInt(point.getLabel()),new PdVector(point.getCoords()));
            } else {
                embedding.setVertex(i,new PdVector(point.getCoords()));
            }
        }
        embedding.setVertexColors(colors);
        embedding.update(embedding);
        for(int i = 0; i < geometries.length; ++i) {
            if(geometries[i].isDynamic()) {
                geometries[i].update(JavaviewUtils.toPointSet(embedding),clearTag);
            }
        }
    }
    
    /**
     * Apply new coordinates to the embedding and the embedded geometries. Same as <code>update(ps,false)</code>.
     *
     * @param ps the new coordinates of the embedding.
     */
    public void update(PointSet ps) {
        update(ps, false);
    }
    
    /**
     * Get the coordinates of the vertex with index <code>index</code> of the embedding.
     *
     * @param index the index of the vertex
     * @return      the coordinates of the vertex
     */
    public double[] getVertexCoords(int index) {
        return embedding.getVertex(index).getEntries();
    }
    
    /**
     * Get the number of embedded geometries
     *
     * @return the number of embedded geometries
     */
    public int getNumberOfGeometries() {
        return geometries.length;
    }
    
    /**
     * Get the embedding.
     *
     * @return the embedding
     */
    public PgPointSet getEmbedding() {
        return (PgPointSet)embedding.clone();
    }
    
    /**
     * Change the embedded geometry at index.
     *
     * @param index the index of the geometry
     * @param g     the geometry
     */
    public void setGeometry(int index, PgGeometry g) {
        geometries[index].setGeometry((PgGeometry)(g.clone()));
    }
    
    /**
     * Get the embedded geometry at index.
     *
     * @param index the index of the geometry
     * @return      the geometry
     */
    public PgGeometry getGeometry(int index) {
        return geometries[index].getGeometry();
    }
    
    /**
     * Get the geometric structure of all embedded geometries.
     *
     * @return the geometric structure of all embedded geometries
     */
    public PgGeometry[] getGeometries() {
        PgGeometry[] polygonSets = new PgGeometry[geometries.length];
        for(int i = 0; i < polygonSets.length; ++i) {
            polygonSets[i] = geometries[i].getGeometry();
        }
        return polygonSets;
    }
    
    /**
     * Get the marked vertices
     *
     * @return the marked vertices
     */
    public Vector getMarkedVertices() {
        Vector markedVertices = new Vector();
        for(int j=0; j < embedding.getNumVertices(); ++j) {
            embedding.clearTagVertex(j,PsObject.IS_SELECTED);
        }
        for(int j = 0; j < geometries.length; ++j) {
            PgPointSet m_geom = (PgPointSet)getGeometry(j);
            for (int i = 0; i < m_geom.getNumVertices(); i++) {
                if (m_geom.hasTagVertex(i, PsObject.IS_SELECTED)) {
                    markedVertices.add(Integer.toString(geometries[j].vertexList[i]));
                    double[] vertex = m_geom.getVertex(i).getEntries();
                    embedding.setVertex(geometries[j].vertexList[i],vertex[0],vertex[1],vertex[2]);
                    embedding.setTagVertex(geometries[j].vertexList[i],PsObject.IS_SELECTED);
                }
            }
        }
        return markedVertices;
    }
    
    /**
     * Get the vertex colors
     *
     * @return the vertex colors
     */
    public Color[] getVertexColors() {
        return embedding.getVertexColors();
    }
    
    /**
     * Get the name.
     *
     * @return the name
     */
    public String getName() {
        return name;
    }
    
    /**
     * Change the name
     *
     * @param name the name
     */
    public void setName(String name) {
        this.name = name;
    }
    
    /**
     * Get a copy of the whole.
     *
     * @return a copy
     */
    public Object clone() {
        return new EmbeddedGeometries(name,embedding,geometries);
    }
    
    /**
     * Get a String representation of the embedding and all the embedded geometries.
     *
     * @return String representation of the instance
     */
    public String toString() {
        String msg = new String();
        msg += "name = " + name + "\n";
        msg += "embedding = " + embedding.toString() + "\n";
        for(int i = 0; i < geometries.length; ++i) {
            msg += "geometries["+i+"]=" + geometries[i].toString();
        }
        return msg;
    }
}
