package cnp.ew.scrolling;

import java.awt.*;
import cnp.ew.util.*;
import cnp.ew.lightweight.*;

public class CpScroller extends CpAbstractLc
{
    CpScrollable scrollable;

    public void setScrollable(CpScrollable newScrollable)
    {
        scrollable = newScrollable;
        scrollable.setScroller(this);
        add(scrollable);
        scrollable.move(0, 0);
    }

    public void scrollTo(int x, int y)
    {

        Point scrollableLocationInCells = scrollable.getLocationInCells();

        // Bounds Checking
        Point max = scrollable.getMaximumScrollPosition(size());
        if (x < 0 || x > max.x) {
            x = scrollableLocationInCells.x;
        }
        if (y < 0 || y > max.y) {
            // Hmmm... last minute change by ted 10/25
            if (y > max.y) {
                y = max.y;
            } else {
                y = scrollableLocationInCells.y;
            }
        }

        if (x == scrollableLocationInCells.x && y == scrollableLocationInCells.y) {
            // No change
            return;
        }

        scrollable.setLocationInCells(new Point(x, y));
        primitiveScrollBy(scrollable.deltaBetween(scrollableLocationInCells, new Point(x, y)));

        // Update the scroll bars
        notifyObservers(CpEvent.SCROLLER_PARAMETERS_CHANGED);
    }

     /**
     * Change our offsets, and tell the scrollable to repaint.  If the scroll
     * was less than a full page, and only in the horizontal or vertical direction,
     * this method blits as much as it can, then damages the area that needs to be repainted.
     */
    void primitiveScrollBy(Dimension delta)
    {
        if (!isDamageRepairEnabled()) {
            scrollable.moveBy(delta.width, delta.height);
            return;
        }

        int scrollDeltaX = delta.width;
        int scrollDeltaY = delta.height;

        // Must grab the global graphics for blitting...
        Graphics tempGraphics = getComponent().getGraphics();

        Dimension size = size();

        int blitWidth, blitHeight, paintOffset, blitOffset;

        // Issue:   I want to move everything but not damage it, since I'm blitting to repair.
        //          This seems kind of clunky.
        scrollable.enableDamageRepair(false);
        scrollable.moveBy(scrollDeltaX, scrollDeltaY);
        scrollable.clearDamagedRect();
        scrollable.enableDamageRepair(true);

        Point glob = locationGlobal();
        // Blit horizontally?
        if ((Math.abs(scrollDeltaX) < size.width) && scrollDeltaY == 0) {
            blitWidth = size.width - Math.abs(scrollDeltaX);
            if (scrollDeltaX > 0) {
                // scrolling to right
                blitOffset = 0;
                paintOffset = 0;
            } else {
                // scrolling to left
                blitOffset = -scrollDeltaX;
                paintOffset = blitWidth;
            }
            tempGraphics.copyArea(glob.x + blitOffset, glob.y, blitWidth, size.height, scrollDeltaX, 0);
            damage(new Rectangle(paintOffset, 0, size.width - blitWidth, size.height));

        // Blit vertically?
        } else if ((Math.abs(scrollDeltaY) < size.height) && scrollDeltaX == 0) {
            blitHeight = size.height - Math.abs(scrollDeltaY);
            if (scrollDeltaY > 0) {
                blitOffset = 0;
                paintOffset = 0;
            } else {
                blitOffset = -scrollDeltaY;
                paintOffset = blitHeight;
            }
            tempGraphics.copyArea(glob.x, glob.y + blitOffset, size.width, blitHeight, 0, scrollDeltaY);
            damage(new Rectangle(0, paintOffset, size.width, size.height - blitHeight));

        } else {
            damage();
        }
        repairDamage();
        tempGraphics.dispose();
    }



    /*********  These are used by Scrolling Controller ********/

    public Dimension getSizeInCells()
    {
		if (scrollable == null) {
		    return new Dimension(0, 0);
		}
        return scrollable.getScrollerSizeInCells(size());
    }

    public Dimension getScrollableSizeInCells()
    {
		if (scrollable == null) {
		    return new Dimension(0, 0);
		}
		return scrollable.getSizeInCells();
    }

    public Point getScrollableLocation()
    {
        return scrollable.location();
    }

    public Point getScrollableLocationInCells()
    {
		if (scrollable == null) {
		    return new Point(0, 0);
		}
		return scrollable.getLocationInCells();
    }

    /************************************************************/

    public void scrollableSized()
    {
        // Sent by the scrollable when it sizes.  // SHOULD ALSO BE SENT ON A CELL HEIGHT?WIDHT CHANGE
        scrollIfNecessary();
        repaint();
        notifyObservers(CpEvent.SCROLLER_PARAMETERS_CHANGED);
    }

    // This fills the remaining area where the scrollable isn't.  Useful to cleanup blitting (i.e. the end of a list box after a scroll)
    public void paint(Graphics g, Rectangle clip)
    {
        g.setColor(getBackground());
        g.fillRect(0, 0, size().width, size().height);
    }

    /*************  UTILITY ROUTINES  **********/


    public void makeVisible(int x, int y)
    {
        Point topLeft = scrollable.getLocationInCells();

        int scrollToY = topLeft.y;
        if (y > size().height) {
            scrollToY = topLeft.y + 1; // Add Ken's accelerator
        } else if (y < 0) {
            scrollToY = topLeft.y - 1;
        }

        int scrollToX = topLeft.x;
        if (x > size().width) {
            scrollToX = topLeft.x + 1; // Add Ken's accelerator
        } else if (x < 0) {
            scrollToX = topLeft.x - 1;
        }

        if (scrollToY != topLeft.y  ||  scrollToX != topLeft.x) {
          //  System.out.println("Scrolling to: " + scrollToY);
            scrollTo(scrollToX, scrollToY);
        }
    }

    void scrollIfNecessary()
    {
        Point maxPosition = scrollable.getMaximumScrollPosition(size());
        Point cellLoc = scrollable.getLocationInCells();

        scrollTo(Math.min(maxPosition.x, cellLoc.x), Math.min(maxPosition.y, cellLoc.y));
    }

    public void layout()
    {
        // Component sizes have changed, notify scrolling controller...

        if (scrollable != null) {
            // Issue:  Should we report that the scroller sized to the scrollable?
            scrollable.scrollerSized(size());

            scrollIfNecessary();
            notifyObservers(CpEvent.SCROLLER_PARAMETERS_CHANGED);
        }
    }

}