package prefuse.render;

import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import prefuse.Constants;
import prefuse.util.ColorLib;
import prefuse.util.GraphicsLib;
import prefuse.visual.VisualItem;

/**
 * Renderer for drawing an axis tick mark and label.
 * 
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class AxisRenderer extends AbstractShapeRenderer {

    private Line2D      m_line = new Line2D.Double();
    private Rectangle2D m_box  = new Rectangle2D.Double();
    
    private int m_xalign;
    private int m_yalign;
    private int m_ascent;
    
    /**
     * Create a new AxisRenderer. By default, axis labels are drawn along the
     * left edge and underneath the tick marks.
     */
    public AxisRenderer() {
        this(Constants.LEFT, Constants.BOTTOM);
    }
    
    /**
     * Create a new AxisRenderer.
     * @param xalign the horizontal alignment for the axis label. One of
     * {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT},
     * or {@link prefuse.Constants#CENTER}.
     * @param yalign the vertical alignment for the axis label. One of
     * {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM},
     * or {@link prefuse.Constants#CENTER}.
     */
    public AxisRenderer(int xalign, int yalign) {
        m_xalign = xalign;
        m_yalign = yalign;
    }
    
    /**
     * Set the horizontal alignment of axis labels.
     * @param xalign the horizontal alignment for the axis label. One of
     * {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT},
     * or {@link prefuse.Constants#CENTER}.
     */
    public void setHorizontalAlignment(int xalign) {
        m_xalign = xalign;
    }
    
    /**
     * Set the vertical alignment of axis labels.
     * @param yalign the vertical alignment for the axis label. One of
     * {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM},
     * or {@link prefuse.Constants#CENTER}.
     */
    public void setVerticalAlignment(int yalign) {
        m_yalign = yalign;
    }
    
    /**
     * @see prefuse.render.AbstractShapeRenderer#getRawShape(prefuse.visual.VisualItem)
     */
    protected Shape getRawShape(VisualItem item) {
        double x1 = item.getDouble(VisualItem.X);
        double y1 = item.getDouble(VisualItem.Y);
        double x2 = item.getDouble(VisualItem.X2);
        double y2 = item.getDouble(VisualItem.Y2);
        m_line.setLine(x1,y1,x2,y2);
        
        if ( !item.canGetString(VisualItem.LABEL) )
            return m_line;
        
        String label = item.getString(VisualItem.LABEL);
        if ( label == null ) return m_line;
        
        FontMetrics fm = DEFAULT_GRAPHICS.getFontMetrics(item.getFont());
        m_ascent = fm.getAscent();
        int h = fm.getHeight();
        int w = fm.stringWidth(label);
        
        double tx, ty;
        
        // get text x-coord
        switch ( m_xalign ) {
        case Constants.FAR_RIGHT:
            tx = x2 + 2;
            break;
        case Constants.FAR_LEFT:
            tx = x1 - w - 2;
            break;
        case Constants.CENTER:
            tx = x1 + (x2-x1)/2 - w/2;
            break;
        case Constants.RIGHT:
            tx = x2 - w;
            break;
        case Constants.LEFT:
        default:
            tx = x1;
        }
        // get text y-coord
        switch ( m_yalign ) {
        case Constants.FAR_TOP:
            ty = y1-h;
            break;
        case Constants.FAR_BOTTOM:
            ty = y2;
            break;
        case Constants.CENTER:
            ty = y1 + (y2-y1)/2 - h/2;
            break;
        case Constants.TOP:
            ty = y1;
            break;
        case Constants.BOTTOM:
        default:
            ty = y2-h; 
        }
        m_box.setFrame(tx,ty,w,h);
        return m_box;
    }
    
    /**
     * @see prefuse.render.Renderer#render(java.awt.Graphics2D, prefuse.visual.VisualItem)
     */
    public void render(Graphics2D g, VisualItem item) {
        Shape s = getShape(item);
        GraphicsLib.paint(g, item, m_line, getStroke(item), getRenderType(item));
        
        // check if we have a text label, if so, render it
        if ( s == m_box ) {
            float x = (float)m_box.getMinX();
            float y = (float)m_box.getMinY() + m_ascent;
            
            // draw label background
            GraphicsLib.paint(g, item, s, null, RENDER_TYPE_FILL);
            
            String str = item.getString(VisualItem.LABEL);
            g.setFont(item.getFont());
            g.setColor(ColorLib.getColor(item.getTextColor()));
            g.drawString(str, x, y);
        }
    }

    /**
     * @see prefuse.render.Renderer#locatePoint(java.awt.geom.Point2D, prefuse.visual.VisualItem)
     */
    public boolean locatePoint(Point2D p, VisualItem item) {
        Shape s = getShape(item);
        if ( s == null ) {
            return false;
        } else if ( s == m_box && m_box.contains(p) ) {
            return true;
        } else {
            double width = Math.max(2, item.getSize());
            double halfWidth = width/2.0;
            return s.intersects(p.getX()-halfWidth,
                                p.getY()-halfWidth,
                                width,width);
        }
    }

    /**
     * @see prefuse.render.Renderer#setBounds(prefuse.visual.VisualItem)
     */
    public void setBounds(VisualItem item) {
        if ( !m_manageBounds ) return;
        Shape shape = getShape(item);
        if ( shape == null ) {
            item.setBounds(item.getX(), item.getY(), 0, 0);
        } else if ( shape == m_line ) {
            GraphicsLib.setBounds(item, shape, getStroke(item));
        } else {
            m_box.add(m_line.getX1(),m_line.getY1());
            m_box.add(m_line.getX2(),m_line.getY2());
            item.setBounds(m_box.getMinX(), m_box.getMinY(),
                           m_box.getWidth(), m_box.getHeight());
        }
    }
    
} // end of class AxisRenderer
