/*
 Violet - A program for editing UML diagrams.

 Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com)
 Alexandre de Pellegrin (http://alexdp.free.fr);

 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.product.diagram.object.node;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.List;

import com.horstmann.violet.framework.graphics.Separator;
import com.horstmann.violet.framework.graphics.content.ContentBackground;
import com.horstmann.violet.framework.graphics.content.ContentBorder;
import com.horstmann.violet.framework.graphics.content.ContentInsideShape;
import com.horstmann.violet.framework.graphics.content.HorizontalLayout;
import com.horstmann.violet.framework.graphics.content.RelativeLayout;
import com.horstmann.violet.framework.graphics.content.TextContent;
import com.horstmann.violet.framework.graphics.shape.ContentInsideRectangle;
import com.horstmann.violet.product.diagram.abstracts.edge.IEdge;
import com.horstmann.violet.product.diagram.abstracts.node.AbstractNode;
import com.horstmann.violet.product.diagram.abstracts.node.INode;
import com.horstmann.violet.product.diagram.object.ObjectDiagramConstant;
import com.horstmann.violet.product.diagram.object.edge.AssociationEdge;
import com.horstmann.violet.product.diagram.object.edge.ObjectReferenceEdge;
import com.horstmann.violet.product.diagram.property.text.LineText;
import com.horstmann.violet.product.diagram.property.text.SingleLineText;

/**
 * A field node_old in an object diagram.
 */
public class FieldNode extends AbstractNode
{
    /**
     * Default constructor
     */
    public FieldNode()
    {
        super();

        name = new SingleLineText();
        name.setAlignment(LineText.RIGHT);
        name.setPadding(0, 10, 0, 15);
        value = new SingleLineText();
        value.setAlignment(LineText.LEFT);
        value.setPadding(0, 15, 0, 10);
        createContentStructure();
    }

    protected FieldNode(FieldNode node) throws CloneNotSupportedException
    {
        super(node);
        name = node.name.clone();
        value = node.value.clone();
        createContentStructure();
    }

    @Override
    protected void beforeReconstruction()
    {
        super.beforeReconstruction();

        if(null == name)
        {
            name = new SingleLineText();
        }
        if(null == value)
        {
            value = new SingleLineText();
        }
        name.reconstruction();
        value.reconstruction();
        name.setPadding(0, 10, 0, 15);
        value.setPadding(0, 15, 0, 10);
    }

    @Override
    protected INode copy() throws CloneNotSupportedException
    {
        return new FieldNode(this);
    }

    @Override
    protected void createContentStructure()
    {
        TextContent nameContent = new TextContent(name);
        nameContent.setMinHeight(DEFAULT_HEIGHT);
        nameContent.setMinWidth(DEFAULT_WIDTH/2);
        TextContent valueContent = new TextContent(value);
        valueContent.setMinHeight(DEFAULT_HEIGHT);
        valueContent.setMinWidth(DEFAULT_WIDTH/2);

        HorizontalLayout horizontalGroupContent = new HorizontalLayout();
        horizontalGroupContent.add(nameContent);
        horizontalGroupContent.add(valueContent);
        horizontalGroupContent.setSeparator(EQUAL_SEPARATOR);

        ContentInsideShape contentInsideShape = new ContentInsideRectangle(horizontalGroupContent);

        setBorder(new ContentBorder(contentInsideShape, null));
        setBackground(new ContentBackground(contentInsideShape, getBackgroundColor()));

        RelativeLayout relativeLayout =new RelativeLayout();
        relativeLayout.add(getBackground(), new Point2D.Double(1,0));

        setContent(new ContentInsideRectangle(relativeLayout));
    }

    @Override
    public Rectangle2D getBounds()
    {
        Point2D location = getLocationOnGraph();
        Rectangle2D contentBounds = getContent().getBounds();
        return new Rectangle2D.Double(location.getX(), location.getY(), contentBounds.getWidth(), contentBounds.getHeight());
    }

    @Override
    public void setTextColor(Color textColor)
    {
        name.setTextColor(textColor);
        value.setTextColor(textColor);
        super.setTextColor(textColor);
    }

    @Override
    public String getToolTip()
    {
        return ObjectDiagramConstant.OBJECT_DIAGRAM_RESOURCE.getString("tooltip.field_node");
    }




    @Override
    public Point2D getLocation()
    {
        INode parent = getParent();
        if (parent == null )
        {
            return new Point2D.Double(0, 0);
        }
        if (!(parent instanceof ObjectNode))
        {
            throw new IllegalStateException("Field node can be only ObjectNode child");
        }
        Point2D location = ((ObjectNode)parent).getFieldsGroup().getLocation(getContent());

        return new Point2D.Double(location.getX(), location.getY()+((ObjectNode)parent).getFieldsTopOffset());
    }

    @Override
    public boolean addConnection(IEdge edge)
    {
        INode endingINode = edge.getEndNode();
        if (edge.getClass().isAssignableFrom(ObjectReferenceEdge.class) && endingINode.getClass().isAssignableFrom(ObjectNode.class))
        {
            value.setText( "" );
            return true;
        }
        // Hack to allow drawing relationship edge over fields
        if (edge instanceof AssociationEdge)
        {
            INode startingNode = edge.getStartNode();
            INode endingNode = edge.getEndNode();
            if (startingNode.getClass().isAssignableFrom(FieldNode.class))
            {
                startingNode = startingNode.getParent();
            }
            if (endingNode.getClass().isAssignableFrom(FieldNode.class))
            {
                endingNode = endingNode.getParent();
            }
            edge.setStartNode(startingNode);
            edge.setEndNode(endingNode);
            return getParent().addConnection(edge);
        }
        return false;
    }

    /**
     * Hack to be able to add fields on object when we do a single click on another field READ THIS : due to this hack, when you
     * dble click to edit this field, the first click triggers this methods (which is a correct framework behavior). The workaround
     * for end users is to use right click instead of dble click to edit fields. It is so simple to find it so we accept to deal
     * with this bug.
     */
    @Override
    public boolean addChild(INode node, Point2D point)
    {
        if (!node.getClass().isAssignableFrom(FieldNode.class))
        {
            return false;
        }
        INode parent = getParent();
        List<INode> parentChildren = parent.getChildren();
        int currentPosition = parentChildren.indexOf(this);
        parent.addChild(node, currentPosition + 1);
        return true;
    }

    @Override
    public Point2D getConnectionPoint(IEdge edge)
    {
        Point2D location = getLocationOnGraph();
        int x = (int)name.getBounds().getWidth();
        if(0==x)
        {
            x = DEFAULT_WIDTH/2;
        }
        return new Point2D.Double(x + 20 + location.getX(), location.getY() + DEFAULT_HEIGHT/2);
    }

    /**
     * Sets the name property value.
     * 
     * @param newValue the field name
     */
    public void setName(LineText newValue)
    {
        name.setText(newValue);
    }

    /**
     * Gets the name property value.
     * 
     * @return the field name
     */
    public LineText getName()
    {
        return name;
    }

    /**
     * Sets the value property value.
     * 
     * @param newValue the field value
     */
    public void setValue(LineText newValue)
    {
        if(0 == getConnectedEdges().size())
        {
            value.setText(newValue);
        }
    }

    /**
     * Gets the value property value.
     * 
     * @return the field value
     */
    public LineText getValue()
    {
        if(0<getConnectedEdges().size())
        {
            return null;
        }
        return value;
    }

    private SingleLineText name;
    private SingleLineText value;

    private static final int DEFAULT_WIDTH = 80;
    private static final int DEFAULT_HEIGHT = 20;
    private static final int XGAP = 5;
    private static final int YGAP = 5;

    private static final Separator EQUAL_SEPARATOR = new Separator()
    {
        @Override
        public void draw(Graphics2D graphics, Point2D startPoint, Point2D endPoint)
        {
            graphics.drawLine((int)startPoint.getX()-3, (int)startPoint.getY() + DEFAULT_HEIGHT/2 -1, (int)startPoint.getX()+3, (int)startPoint.getY() + DEFAULT_HEIGHT/2 -1);
            graphics.drawLine((int)startPoint.getX()-3, (int)startPoint.getY() + DEFAULT_HEIGHT/2 +2, (int)startPoint.getX()+3, (int)startPoint.getY() + DEFAULT_HEIGHT/2 +2);
        }
    };
}
