package cnp.ew.list;

import java.awt.*;
import java.util.*;

import cnp.ew.util.*;
import cnp.ew.displayer.*;
import cnp.ew.scrolling.*;
import cnp.ew.lightweight.*;

public class CpColumnHeader extends CpVariableCellSizeScrollable
{
    static final int PRESSING = 0;
    static final int SIZING = 1;
    static final int NORMAL = 2;

    Vector columns;

	int dragColumnIndex;
	int dragColumnStartX;
	int dragX;
	int mode;
	boolean mouseInside;
    public boolean cursorChanged = false;
    public int sizingPosition;
    public boolean quantize = true;

	CpAbstractScrollable pane;

	Cp3DBorderDisplayer displayer;
    CpGeneralStringDisplayer innerDisplayer;

    static int hSizingImageId;

    static {
       //  hSizingImageId = CpToolkit.registerImageName("hsizing.gif");
    }

    public CpColumnHeader(CpAbstractScrollable newPane)
    {
        pane = newPane;
        innerDisplayer = new CpGeneralStringDisplayer();
        // Issue: This should be modified if column returns alignment... down in paint?
        innerDisplayer.setHorizontalAlignment(CpGeneralStringDisplayer.ALIGN_CENTER);
        innerDisplayer.setVerticalAlignment(CpGeneralStringDisplayer.ALIGN_CENTER);
        innerDisplayer.setMargin(2, 0);

        displayer = new Cp3DBorderDisplayer(true, true);
        setBackground(Color.lightGray);
    }
    /********************* VARIABLECELLSIZE SCROLLING INFO ******************/

    public Dimension getSizeInCells()
    {
        return pane.getSizeInCells();
    }

    public int getCellHeight(int i)
    {
        return pane.getCellHeight(i);
    }

    public int getCellWidth(int i)
    {
        return pane.getCellWidth(i);
    }
    /********************* PAINTING *************************/

  	public void paintGeneral(Graphics g, Rectangle clip)
	{
	    CpSizableHeader column;
	    int curOffset = 0;

        // ISSUE:  Do we have convenience method, fill(g, color)? to erase whole background, or even fill(g)?
        g.setColor(getBackground());
        g.fillRect(0, 0, size().width, size().height);

        Point firstCell, firstPixelPoint;
        if (quantize) {
            firstCell = pane.pointToCell(clip.x, clip.y);
            firstPixelPoint = pane.cellToPoint(firstCell.x, firstCell.y);
        } else {
            firstCell = new Point(0, 0);
            firstPixelPoint = new Point(0, 0);
        }
        curOffset = getCoord(firstPixelPoint);
        int stopAt = getDimension(new Dimension(clip.x + clip.width, clip.y + clip.height));

		for(int c = getCoord(firstCell); c < columns.size(); c++) {
			column = (CpSizableHeader)columns.elementAt(c);
			innerDisplayer.setModel(column.getModel());
			innerDisplayer.setHorizontalAlignment(getAlignment(column));
			if (mode == PRESSING && c == dragColumnIndex) {
                displayer.setRaised(!mouseInside);
            } else {
                displayer.setRaised(true);
            }
            Rectangle rect = getRectForColumn(curOffset, column);
			displayer.paintIn(this, g, rect.x, rect.y, rect.width, rect.height);
		    g.setColor(Color.black);
			innerDisplayer.paintIn(this, g, rect.x + 2, rect.y + 2, rect.width - 4, rect.height - 4);

		    curOffset += column.getExtent();

		    drawLine(g, curOffset);
		    if (curOffset > stopAt) {
		        break;
		    }
		}

		if (curOffset < stopAt) {
			displayer.setRaised(true);
            Rectangle rect = getRectForColumn(curOffset, null);
			displayer.paintIn(this, g, rect.x, rect.y, rect.width, rect.height);
	    }
	    g.setColor(Color.black);
	}

  	public void paint(Graphics g, Rectangle clip)
	{
	    paintGeneral(g, clip);
	    g.drawLine(0, size().height - 1, size().width - 1, size().height - 1);
	  /*  if (cursorChanged) {
	        Image image = CpToolkit.getImage(hSizingImageId);
	        g.drawImage(image, sizingPosition - image.getWidth(CpToolkit.defaultComponent()) / 2, (size().height - image.getHeight(CpToolkit.defaultComponent())) / 2, CpToolkit.defaultComponent());
        }
        */
	}

    public Rectangle getRectForColumn(int curOffset, CpSizableHeader column)
    {
        if (column == null) {
            return new Rectangle(curOffset, 0, size().width + 10, size().height - 1);
        } else {
            return new Rectangle(curOffset, 0, column.getExtent() - 1, size().height - 1);
        }
    }

    public void drawLine(Graphics g, int curOffset)
    {
        g.drawLine(curOffset - 1, 0, curOffset - 1, size().height);
    }

    /**********************************************/

    public int getAlignment(CpSizableHeader column)
    {
        return column.getAlignment();
    }

    public int getDimension(Dimension d)
    {
        return getDimension(d.width, d.height);
    }

    public int getDimension(int w, int h)
    {
        return w;
    }

    public int getCoord(Point p)
    {
        return getCoord(p.x, p.y);
    }

    public int getCoord(int x, int y)
    {
        return x;
    }

    public void drawSizingLine(int x)
    {
        pane.drawVSizingLine(x);
    }

	public void setColumns(Vector v)
	{
		columns = v;
	}

	public Dimension preferredSize()
	{
	    // Issue:  Probably want a method for getting the font metrics?
	    return new Dimension(100, Toolkit.getDefaultToolkit().getFontMetrics(getFont()).getHeight() + 8);
	}

    int columnWidth(int i)
    {
        return ((CpSizableHeader)columns.elementAt(i)).getExtent();
    }

    void setColumnWidth(int i, int w)
    {
        ((CpSizableHeader)columns.elementAt(i)).setExtent(w);
    }

    public boolean mouseDown(Event e, int x, int y)
    {
        cursorChanged = false;

	    int cumulativeWidth = 0;
	    int threshold = 3;

        int coord = getCoord(x, y);

		for(int i = 0; i < columns.size(); i++) {
		    dragColumnIndex = i;
		    dragColumnStartX = cumulativeWidth;
			cumulativeWidth += columnWidth(i);
			if (coord > (cumulativeWidth - threshold) && coord < (cumulativeWidth + threshold)) {
			    mode = SIZING;
			    dragX = coord;
			    drawSizingLine(dragX);
			    return true;
			} else if (coord < cumulativeWidth) {
			    mode = PRESSING;
			    mouseDrag(e, x, y);
			    return true;
			}
		}
		return false;
    }

    public boolean mouseUp(Event e, int x, int y)
    {
        if (mode == PRESSING && mouseInside) {
            notifyObservers(CpEvent.COLUMN_SELECTED, new Integer(dragColumnIndex));
        }
        if (mode == SIZING) {
            drawSizingLine(dragX);
            notifyObservers(CpEvent.COLUMN_SIZED, new Integer(dragColumnIndex));
        }
        mode = NORMAL;
        repaint();
        return false;
    }


    public boolean mouseDrag(Event e, int x, int y)
    {
        int coord = getCoord(x, y);

        if (mode == SIZING) {
            if (coord != dragX) {
                drawSizingLine(dragX);
                drawSizingLine(coord);
                setColumnWidth(dragColumnIndex, Math.max(coord - dragColumnStartX, 0));
                dragX = coord;
                repaint();
            }
        } else if (mode == PRESSING) {
            mouseInside = (inside(x, y) && (coord > dragColumnStartX && coord < dragColumnStartX + columnWidth(dragColumnIndex)));
            repaint();
        }
        return false;
    }

    public boolean mouseExit(Event e, int x, int y)
    {
        setCursor(Frame.DEFAULT_CURSOR);
        if (cursorChanged) {
            cursorChanged = false;
            repaint();
        }
        return true;
    }

    public int getSizingCursor()
    {
   	    return Frame.W_RESIZE_CURSOR;
   	}

    public boolean mouseMove(Event e, int x, int y)
    {
	    int cx = -1;
	    int threshold = 3;
	    boolean couldChange = false;

		for(int c = 0; c < columns.size(); c++) {
			cx += columnWidth(c);
			if ((getCoord(x, y) > (cx - threshold)) && (getCoord(x, y) < (cx + threshold))) {
			    couldChange = true;
			    sizingPosition = cx;
			}
		}
		if (couldChange) {
		    if (!cursorChanged) {
		        cursorChanged = true;
		    //    repaint();
            	setCursor(getSizingCursor());
            }
        } else {
            if (cursorChanged) {
                cursorChanged = false;
   //             repaint();
        		setCursor(Frame.DEFAULT_CURSOR);
        	}
        }
		return false;
    }
}


