package cnp.ew.scrolling;

import java.awt.*;
import cnp.ew.util.*;
import cnp.ew.lightweight.*;

public abstract class CpAbstractScrollable extends CpAbstractLc implements CpScrollable
{
    CpScroller scroller;

    /*****************  GET/SET **********************/

    // Issue:  Should scrollable know scroller?  Used for making items visible, computing visible bounds, etc.
    public CpScroller getScroller()
    {
        return scroller;
    }

    public void setScroller(CpScroller newScroller)
    {
        scroller = newScroller;
    }

    /****************  Subclassable methods  *******************/

    public int getCellHeight(int i)
    {
        return getCellSize().height;
    }

    public int getCellWidth(int i)
    {
        return getCellSize().width;
    }

    public Dimension getScrollerSizeInCells(Dimension scrollerSize)
    {
        // Only count fully visible cells
        return new Dimension(scrollerSize.width / getCellSize().width, scrollerSize.height / getCellSize().height);
    }

    public Dimension getSizeInCells()
    {
        // In columnar list, we set the width to at least the visible width (for drawing the full selection rect).  This can mean that the width
        // can be set to a not quantized value (i.e. cellWidth = 10, width = 35).  By quantizing it, the calculations will be correct if
        // width <= visible width.  Probably should do this for height too.  Yes.

        int w = size().width;
        int mod;
        if ((mod = w % getCellSize().width) != 0) {
            w = w - mod;
        }
        return new Dimension((w - 1) / getCellSize().width + 1, (size().height - 1) / getCellSize().height + 1);
    }

    public Point getLocationInCells()
    {
        return new Point(-location().x / getCellSize().width, -location().y / getCellSize().height);
    }

    public void setLocationInCells(Point newLocation)
    {
        // Ignored, since this can be computed based on pixel location
    }

    // This changes when the scrollable sizes, or a particular cell sizes
    public Point getMaximumScrollPosition(Dimension scrollerSize)
    {
        Dimension scrollerSizeInCells = getScrollerSizeInCells(scrollerSize);
        Dimension sizeInCells = getSizeInCells();

        return new Point(sizeInCells.width - scrollerSizeInCells.width, sizeInCells.height - scrollerSizeInCells.height);
    }


    public Dimension deltaBetween(Point from, Point to)
    {
        return new Dimension((from.x - to.x) * getCellSize().width, (from.y - to.y) * getCellSize().height);
    }

    abstract public Dimension getCellSize();

    public void scrollerSized(Dimension newSize)
    {
    }

    public Point cellToPoint(int cellX, int cellY)
    {
        Dimension cellSize = getCellSize();
        return new Point(cellX * cellSize.width, cellY * cellSize.height);
    }

    public Point pointToCell(int px, int py)
    {
        Dimension cellSize = getCellSize();
        return new Point(px / cellSize.width, px / cellSize.height);
    }

    public Point getFirstVisibleCell()
    {
        Rectangle visRect = getVisibleRect();
        return pointToCell(visRect.x, visRect.y);
    }

    public Point getLastVisibleCell()
    {
        Rectangle visRect = getVisibleRect();
        return pointToCell(visRect.x + visRect.width, visRect.y + visRect.height);
    }
/*
    public Point getLastFullyVisibleCell()
    {
        Point cell = getFirstVisibleCell();
        Dimension sizeInCells = getScrollerSizeInCells();
        cell.x += sizeInCells.width;
        cell.y += sizeInCells.height;
        return cell;
    }
*/

    /****************  Cell-based helper routines *******************/

    public void makeCellVisible(int cellX, int cellY)
    {
        //System.out.println("making cell visible " + cellX + ", " + cellY);
       // System.out.println("firstVis: " + getFirstVisibleCell() + " lastVis: " + getLastFullyVisibleCell());
        // Ted added + 1s, 10/3
        Point pix = cellToPoint(cellX, cellY);
        // Convert to scroller's coords by adding in location()
        getScroller().makeVisible(pix.x + location().x, pix.y + location().y);
    }

    public void damageCell(int cellX, int cellY)
    {
        Point pix = cellToPoint(cellX, cellY);
        Dimension cellSize = getCellSize();
        damage(new Rectangle(pix.x, pix.y, cellSize.width, cellSize.height));
    }

    public void damageLine(int cellY)
    {
        Point pix = cellToPoint(0, cellY);
        Dimension cellSize = getCellSize();
        damage(new Rectangle(0, pix.y, size().width, cellSize.height));
    }


    /****************  Misc *******************/

    // This is subclassed to intersect the damaged region with the visible rect, to avoid unnecessary drawing calculations
    public void damageGlobal(Rectangle r)
    {
        if (getScroller() != null) {
            super.damageGlobal(r.intersection(getScroller().boundsGlobal()));
        } else {
            super.damageGlobal(r);
        }
    }

    public Rectangle getVisibleRect()
    {
        CpScroller s = getScroller();
        Point loc = location();
        return new Rectangle(-loc.x, -loc.y, s.size().width, s.size().height);
    }

    public void scrollTo(int x, int y)
    {
        if (getScroller() != null) {
            getScroller().scrollTo(x, y);
        }
    }
    // This doesn't really fit here, but it's handy for things with headers that can be sized (CLB, Grid)
    // ISSUE:  Should we grab the Graphics?  Or instead set some mode and repaint?

    public void drawXORLine(int x, int y, int x2, int y2)
    {
        Point glob = locationGlobal();
        Graphics g = getComponent().getGraphics();
        g.setColor(Color.white);
        g.setXORMode(Color.black);
        g.drawLine(glob.x + x, glob.y + y, glob.x + x2, glob.y + y2);
        g.setPaintMode();
        g.dispose();
    }

    public void drawVSizingLine(int x)
    {
        Rectangle r = getVisibleRect();
        drawXORLine(x - 1, r.y, x - 1, r.y + r.height);
    }

    public void drawHSizingLine(int y)
    {
        drawXORLine(0, y - 1, 10000, y - 1);
    }
}



