/*
Violet - A program for editing UML diagrams.

Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com)

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 of the License, or
(at your option) any later version.

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.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package com.horstmann.violet;

import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import com.horstmann.violet.framework.Edge;
import com.horstmann.violet.framework.Graph;
import com.horstmann.violet.framework.Grid;
import com.horstmann.violet.framework.Node;


/**
   A UML sequence diagram.
*/
public class SequenceDiagramGraph extends Graph
{
   public boolean add(Node n, Point2D p)
   {
      if (n instanceof CallNode) // must be inside an object
      {
         Collection nodes = getNodes();
         boolean inside = false;
         Iterator iter = nodes.iterator();
         while (!inside && iter.hasNext())
         {
            Node n2 = (Node)iter.next();
            if (n2 instanceof ImplicitParameterNode
               && n2.contains(p)) 
            {
               inside = true;
               ((CallNode)n).setImplicitParameter(
                  (ImplicitParameterNode)(n2));
            }
         }
         if (!inside) return false;
      }

      if (!super.add(n, p)) return false;

      return true;
   }

   public void removeEdge(Edge e)
   {
      super.removeEdge(e);
      if (e instanceof CallEdge && e.getEnd().getChildren().size() == 0)
         removeNode(e.getEnd());
   }
 
   public void layout(Graphics2D g2, Grid grid)
   {
      super.layout(g2, grid);

      ArrayList topLevelCalls = new ArrayList();
      ArrayList objects = new ArrayList();
      Collection nodes = getNodes();
      Iterator iter = nodes.iterator();
      while (iter.hasNext())
      {
         Node n = (Node)iter.next();
         
         if (n instanceof CallNode && n.getParent() == null) 
            topLevelCalls.add(n);
         else if (n instanceof ImplicitParameterNode)
            objects.add(n);      
      }

      Collection edges = getEdges();
      iter = edges.iterator();
      while (iter.hasNext())
      {
         Edge e = (Edge)iter.next();
         if (e instanceof CallEdge)
         {
            Node end = e.getEnd();
            if (end instanceof CallNode)
               ((CallNode)end).setSignaled(((CallEdge)e).isSignal());
         }
      }

      double left = 0;

      // find the max of the heights of the objects

      double top = 0;
      for (int i = 0; i < objects.size(); i++)
      {
         ImplicitParameterNode n = (ImplicitParameterNode)objects.get(i);
         n.translate(0, -n.getBounds().getY());
         top = Math.max(top, n.getTopRectangle().getHeight());
      }

      /*

      // sort topLevelCalls by y position
      Collections.sort(topLevelCalls, new
         Comparator()
         {
            public int compare(Object o1, Object o2)
            {
               CallNode c1 = (CallNode)o1;
               CallNode c2 = (CallNode)o2;
               double diff = c1.getBounds().getY()
                  - c2.getBounds().getY();
               if (diff < 0) return -1;
               if (diff > 0) return 1;
               return 0;
            }            
         });

      for (int i = 0; i < topLevelCalls.size(); i++)
      {
         CallNode call = (CallNode)topLevelCalls.get(i);
         top += CallNode.CALL_YGAP;

         call.translate(0, top - call.getBounds().getY());
         call.layout(this, g2, grid);
         top += call.getBounds().getHeight();
      }
      */

      for (int i = 0; i < topLevelCalls.size(); i++)
      {
         CallNode call = (CallNode) topLevelCalls.get(i);
         call.layout(this, g2, grid);
      }

      iter = nodes.iterator();
      while (iter.hasNext())
      {
         Node n = (Node)iter.next();
         if (n instanceof CallNode)
            top = Math.max(top, n.getBounds().getY()
               + n.getBounds().getHeight());
      }

      top += CallNode.CALL_YGAP;

      for (int i = 0; i < objects.size(); i++)
      {
         ImplicitParameterNode n = (ImplicitParameterNode) objects.get(i);
         Rectangle2D b = n.getBounds();
         n.setBounds(new Rectangle2D.Double(
            b.getX(), b.getY(), 
            b.getWidth(), top - b.getY()));         
      }
   }

   public void draw(Graphics2D g2, Grid g)
   {
      layout(g2, g);

      Collection nodes = getNodes();
      Iterator iter = nodes.iterator();
      while (iter.hasNext())
      {
         Node n = (Node) iter.next();
         if (!(n instanceof CallNode))
            n.draw(g2);
      }

      iter = nodes.iterator();
      while (iter.hasNext())
      {
         Node n = (Node) iter.next();
         if (n instanceof CallNode)
            n.draw(g2);
      }

      Collection edges = getEdges();
      iter = edges.iterator();
      while (iter.hasNext())
      {
         Edge e = (Edge) iter.next();
         e.draw(g2);
      }
   }

   public Node[] getNodePrototypes()
   {
      return NODE_PROTOTYPES;
   }

   public Edge[] getEdgePrototypes()
   {
      return EDGE_PROTOTYPES;
   }

   protected static final Node[] NODE_PROTOTYPES = new Node[3];

   protected static final Edge[] EDGE_PROTOTYPES = new Edge[3];
}





