package view;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.JViewport;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import animation.PseudoCodeNode;

/**
 * JComponent used to draw line numbers. This JComponent should be added as a row header view in a JScrollPane. Based upon the 
 * LineNumberModel provided, this component will draw the line numbers as needed.
 * @author Greg Cope
 *
 */

public class PseudoCodeLines extends JComponent implements MouseListener{
   
    static final long serialVersionUID = 432143214L;
   
    //pixel padding on left and right
    private static final int HORIZONTAL_PADDING = 1;
    //pixel padding on left and right
    private static final int VERTICAL_PADDING = 3;
    
    private static ImageIcon currentLine = new ImageIcon( PseudoCodeNode.class.getResource( "/img/current_line.png" ) );
    
    private JTree tree;
    
    /**
     * Constructs a new Component with no model
     */
    public PseudoCodeLines( JTree t ){
        super();
        tree = t;
        this.addMouseListener( this );
    }
    
    /**
     * Checks and adjusts the width of this component based upon the line numbers
     */
    public void adjustWidth(){
        int max = getLineNumber( (TreeNode)tree.getPathForRow( tree.getRowCount() - 1 ).getLastPathComponent() );
        if ( getGraphics() == null ){
            return;
        }
        int width = getGraphics().getFontMetrics().stringWidth(String.valueOf(max)) + 2 * HORIZONTAL_PADDING + 30;
        JComponent c = (JComponent)getParent();
        if (c == null){//not within a container
            return;
        }
        Dimension dimension = c.getPreferredSize();
        if ( c instanceof JViewport ){//do some climbing up the component tree to get the main JScrollPane view
            JViewport view = (JViewport)c;
            Component parent = view.getParent();
            if ( parent != null && parent instanceof JScrollPane){
                JScrollPane scroller = (JScrollPane)view.getParent();
                dimension = scroller.getViewport().getView().getPreferredSize();
            }           
        }
        if ( width > super.getPreferredSize().width || width < super.getPreferredSize().width){
            setPreferredSize(new Dimension(width + 2*HORIZONTAL_PADDING, dimension.height));
            revalidate();
            repaint();
        }
    }
    
    @Override
    public Dimension getPreferredSize()
    {
        return super.getPreferredSize();
    }
    
    private int countChildren( TreeNode treeNode )
    {
        if( treeNode.isLeaf() )
            return 0;
        else
        {
            int sum = 0;
            for( int i = 0; i < treeNode.getChildCount(); i++ )
            {
                sum += countChildren( treeNode.getChildAt( i ) ) + 1;
            }
            return sum;
        }
    }

    private int getLineNumber( TreeNode treeNode )
    {
        TreeNode parent = treeNode.getParent();
        if( parent == null )
            return 1;
        int sum = getLineNumber( parent ) + 1;
        for( int i = 0; i < parent.getChildCount(); i++ )
        {
            if( i == parent.getIndex( treeNode ) )
                return sum;
            sum += countChildren( parent.getChildAt( i ) ) + 1;
        }
        return 0;
    }
    
    @Override
    public void paintComponent(Graphics g){
        adjustWidth();
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setColor(RenderHelper.BACKGROUND_COLOR);
        g2d.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(RenderHelper.FOREGROUND_COLOR);
        for ( int i = 0; i < tree.getRowCount(); i++ ){
            PseudoCodeNode node = (PseudoCodeNode)tree.getPathForRow( i ).getLastPathComponent();
            int number = getLineNumber( node );
            Rectangle rect = tree.getRowBounds( i );
            String text = String.valueOf( number );
            int yPosition = rect.y + rect.height / 2 + 4;
            if( !node.hasSelectedSubnode() && node.isSelected() )
                g.drawImage( currentLine.getImage(), HORIZONTAL_PADDING, rect.y + rect.height / 2 - 10, 20, 20, null );
            g2d.drawString( text, HORIZONTAL_PADDING * 2 + 20, yPosition );
            if( node.hasBreakPoint() )
            {
                Color c = g.getColor();
                g.setColor( new Color (0xe7887a) );
                g.fillOval( getWidth() - HORIZONTAL_PADDING - 10, rect.y + rect.height / 2 - 5, 10, 10 );
                g.setColor( c );
            }
        }
    }
    
    private int getRowNumber( int y ) {
        int curr = 0;
        int res = -1;
        for ( int i = 0; i < tree.getRowCount(); i++ ){
            Rectangle rect = tree.getRowBounds( i );
            if( y > curr && y < rect.y + rect.height )
            {
                res = i;
                break;
            }
            curr = rect.y + rect.height;
        }
        return res;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        int rNum = getRowNumber( e.getY() );
        if( rNum < 0 )
            return;
        TreePath path = tree.getPathForRow( rNum );
        if( path == null )
            return;
        ((PseudoCodeNode)path.getLastPathComponent()).setBreakPoint( !((PseudoCodeNode)path.getLastPathComponent()).hasBreakPoint() );
        tree.repaint();
    }

    @Override
    public void mousePressed(MouseEvent e) {}

    @Override
    public void mouseReleased(MouseEvent e) {}

    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) {}
}
