import java.lang.Math;
import java.applet.*;
import java.awt.*;
import java.net.*;
import GsharpCanvas;

//
// This applet demonstrates interactive zooming with a Gsharp server
// Only works for indentical x and y coordinate space (i.e. square)
//
public class GsharpApp extends Applet {

    // relative position of cgi directory to getCodeBase()
    private String _cgiDir="../../cgi-bin/";

    // The limits of the viewport in pixels as set in Gsharp GSL script
    public static int vpX[]={137,461};
    public static int vpY[]={30,354};
    private static int _vpWidth=vpX[1]-vpX[0];

    // The limits of the x and y coordinate space also form Gsharp
    public static int minX=0, maxX=100;
    public static int minY=0, maxY=100;

    // Original unzoomed image and current zoomed image
    private Image _resetImage, _currentImage;

    // Limist for current image
    private int _currentMinX, _currentMaxX;
    private int _currentMinY, _currentMaxY;

    // Draw canvas and dialog panel
    private GsharpCanvas _canvas;
    private GsharpDialogPanel _dialogPanel;

    // Constructor creates GUI
    public GsharpApp() {
        // Add title
        Label title = new Label("Rubberband Zooming with Gsharp");
        title.setFont(new Font("Helvetica ",Font.BOLD,23));
        add(title);

        // Put dialog and canvas into a layout panel for nice layout
        LayoutPanel layoutPanel = new LayoutPanel();
        add(layoutPanel);

        // Create dialog panel
        _dialogPanel = new GsharpDialogPanel();
        // Add and constrain
        layoutPanel.constrain(_dialogPanel,0,0,1,1);
        // Set limits for the IntField(s)
        _dialogPanel.setLimits(minX,maxX,minY,maxY);

        // Create draw canvas
        Panel drawPanel = new Panel();
        _canvas = new GsharpCanvas();
        drawPanel.add(_canvas);
        // Add and constrain
        layoutPanel.constrain(drawPanel,1,0,9,1);
    }
    // When URL is up then get original unzoomed image
    public void init() {
        resetZoom();
    }
    // Reset zoom
    public void resetZoom() {
        // Only request image if we dont have it already
        if (_resetImage==null) {
            _resetImage = getImage(getCodeBase(),
                                _cgiDir+"gs_zoom.cgi"+
                              "?xmin="+minX+ "&xmax="+maxX+
                              "&ymin="+minY+ "&ymax="+maxY);
        }
        // Set current limits
        _dialogPanel.setMinX(_currentMinX = minX);
        _dialogPanel.setMaxX(_currentMaxX = maxX);
        _dialogPanel.setMinY(_currentMinY = minY);
        _dialogPanel.setMaxY(_currentMaxY = maxY);
        // Draw image
        _canvas.drawImage(_resetImage);
    }
    // Perform a zoom
    public void zoomIn() {
        // Validate limits (really only needed if typed in)
        _validateLimits();
        // Update current limits
        _currentMinX = _dialogPanel.getMinX();
        _currentMaxX = _dialogPanel.getMaxX();
        _currentMinY = _dialogPanel.getMinY();
        _currentMaxY = _dialogPanel.getMaxY();
        // Check if just a reset of zoom
        if (_currentMinX == minX && _currentMinY == minY && 
            _currentMaxX == maxX && _currentMaxY == maxY) {
            resetZoom();
            return;
        }
        // Get image from Gsharp
        _currentImage = 
            getImage(getCodeBase(),_cgiDir+"gs_zoom.cgi"+
                     "?xmin="+_currentMinX+ "&xmax="+_currentMaxX+
                     "&ymin="+_currentMinY+ "&ymax="+_currentMaxY);
        // and draw it
        _canvas.drawImage(_currentImage);
    }

    // Called when doing rubberbanding to update shows limits
    public void recalcZoom(int startX, int startY, 
                           int currentX, int currentY) {
        // Find pixel coordinate diffs
        int xdiff = _currentMaxX-_currentMinX;
        int ydiff = _currentMaxY-_currentMinY;
        // Calculate the limtis based on the pixel coordinates
        // and update the IntField(s) at the same time to show values
        int newMinX = _currentMinX +
            Math.round(((float)Math.min(startX,currentX)-vpX[0])/
                       _vpWidth*xdiff);
        _dialogPanel.setMinX(newMinX);
        int newMaxX = _currentMinX +
            Math.round(((float)Math.max(startX,currentX)-vpX[0])/
                       _vpWidth*xdiff);
        // Must request at least 2 grids
        if (newMaxX == newMinX) newMaxX++;
        _dialogPanel.setMaxX(newMaxX);
        int newMinY = _currentMinY +
            Math.round(((float)vpY[1]-Math.max(startY,currentY))/
                       _vpWidth*ydiff);
        _dialogPanel.setMinY(newMinY);
        // Constrain limits to square
        _dialogPanel.setMaxY(newMinY+newMaxX-newMinX);
    }

    // Validate limits are inside range, min 2 grids and a square
    private void _validateLimits() {
        int newMinX = _dialogPanel.getMinX();
        int newMaxX = _dialogPanel.getMaxX();
        int newMinY = _dialogPanel.getMinY();
        int newMaxY = _dialogPanel.getMaxY();
        // min must be < max
        if (newMinX > newMaxX) {
            int swap = newMinX;
            newMinX = newMaxX;
            newMaxX = swap;
        }
        if (newMinY > newMaxY) {
            int swap = newMinY;
            newMinY = newMaxY;
            newMaxY= swap;
        }
        // must be same number of grids in X and Y (i.e. a square)
        if (newMaxX-newMinX != newMaxY-newMinY) {
            // use smallest range
            int xdiff = newMaxX-newMinX;
            int ydiff = newMaxY-newMinY;
            if (xdiff < ydiff) {
                newMaxY = newMinY+xdiff;
            } else {
                newMaxX = newMinX+ydiff;
            }
        }
        if (newMinX == newMaxX) {
            if (newMinX == maxX) {
                newMinX--;
            } else {
                newMaxX++;
            }
            if (newMinY == maxY) {
                newMinY--;
            } else {
                newMaxY++;
            }
        }
        // Set values back
        _dialogPanel.setMinX(newMinX);
        _dialogPanel.setMaxX(newMaxX);
        _dialogPanel.setMinY(newMinY);
        _dialogPanel.setMaxY(newMaxY);
    }
}

