package ij.gui;

import java.awt.*;
import java.awt.image.*;
import ij.*;
import ij.process.*;
import ij.measure.Calibration;

/** Oval region of interest */
public class OvalRoi extends Roi {

    /** Creates a new OvalRoi.*/
    public OvalRoi(int x, int y, int width, int height) {
        super(x, y, width, height);
        type = OVAL;
    }

    /** Starts the process of creating a user-defined OvalRoi. */
    public OvalRoi(int x, int y, ImagePlus imp) {
        super(x, y, imp);
        type = OVAL;
    }

    /** Obsolete */
    public OvalRoi(int x, int y, int width, int height, ImagePlus imp) {
        this(x, y, width, height);
        setImage(imp);
    }

    protected void moveHandle(int sx, int sy) {
        if (clipboard!=null) return;
        int ox = ic.offScreenX(sx);
        int oy = ic.offScreenY(sy);
        //IJ.log("moveHandle: "+activeHandle+" "+ox+" "+oy);
        int x1=x, y1=y, x2=x1+width, y2=y+height;
        int w2 = (int)(0.14645*width);
        int h2 = (int)(0.14645*height);
        switch (activeHandle) {
            case 0: x=ox-w2; y=oy-h2; break;
            case 1: y=oy; break;
            case 2: x2=ox+w2; y=oy-h2; break;
            case 3: x2=ox; break;           
            case 4: x2=ox+w2; y2=oy+h2; break;
            case 5: y2=oy; break;
            case 6: x=ox-w2; y2=oy+h2; break;
            case 7: x=ox; break;
        }
        //if (x<0) x=0; if (y<0) y=0;
        if (x<x2)
           width=x2-x;
        else
          {width=1; x=x2;}
        if (y<y2)
           height = y2-y;
        else
           {height=1; y=y2;}
        if (constrain)
            height = width;
        updateClipRect();
        imp.draw(clipX, clipY, clipWidth, clipHeight);
        oldX=x; oldY=y;
        oldWidth=width; oldHeight=height;
        cachedMask = null;
    }

    public void draw(Graphics g) {
        if (ic==null) return;
        g.setColor(instanceColor!=null?instanceColor:ROIColor);
        mag = ic.getMagnification();
        int sw = (int)(width*mag);
        int sh = (int)(height*mag);
        int sw2 = (int)(0.14645*width*mag);
        int sh2 = (int)(0.14645*height*mag);
        int sx1 = ic.screenX(x);
        int sy1 = ic.screenY(y);
        int sx2 = sx1+sw/2;
        int sy2 = sy1+sh/2;
        int sx3 = sx1+sw;
        int sy3 = sy1+sh;
        g.drawOval(sx1, sy1, sw, sh);
        if (state!=CONSTRUCTING && clipboard==null) {
            int size2 = HANDLE_SIZE/2;
            drawHandle(g, sx1+sw2-size2, sy1+sh2-size2);
            drawHandle(g, sx3-sw2-size2, sy1+sh2-size2);
            drawHandle(g, sx3-sw2-size2, sy3-sh2-size2);
            drawHandle(g, sx1+sw2-size2, sy3-sh2-size2);
            drawHandle(g, sx2-size2, sy1-size2);
            drawHandle(g, sx3-size2, sy2-size2);
            drawHandle(g, sx2-size2, sy3-size2);
            drawHandle(g, sx1-size2, sy2-size2);
        }
        drawPreviousRoi(g);
        if (updateFullWindow)
            {updateFullWindow = false; imp.draw();}
        if (state!=NORMAL) showStatus();
    }

    /** Draws an outline of this OvalRoi on the image. */
    public void drawPixels(ImageProcessor ip) {
        Polygon p = getPolygon();
        if (p.npoints>0) ip.drawPolygon(p);
        if (Line.getWidth()>1)
            updateFullWindow = true;
    }       

    /** Returns this OvalRoi as a polygon. */
    public Polygon getPolygon() {
        ImageProcessor mask = getMask();
        Wand wand = new Wand(mask);
        wand.autoOutline(width/2,height/2, 255, 255);
        for (int i=0; i<wand.npoints; i++) {
            wand.xpoints[i] += x;
            wand.ypoints[i] += y;
        }
        return new Polygon(wand.xpoints, wand.ypoints, wand.npoints);
    }       

    /** Tests if the specified point is inside the boundary of this OvalRoi.
    @author Michael Schmid
    */
    public boolean contains(int x, int y) {
        // equation for an ellipse is x^2/a^2 + y^2/b^2 = 1
        if (!super.contains(x, y))
            return false;
        else {
            int twoDx = 2*x - (2*this.x+width-1);
            int twoDy = 2*y - (2*this.y+height-1);
            int twoRx = width;
            int twoRy = height;
            return (twoDx*twoDx/(double)(twoRx*twoRx)
                + twoDy*twoDy/(double)(twoRy*twoRy)) < 1;
        }
    }
        
    /** Returns a handle number if the specified screen coordinates are  
        inside or near a handle, otherwise returns -1. */
    public int isHandle(int sx, int sy) {
        if (clipboard!=null || ic==null) return -1;
        double mag = ic.getMagnification();
        int size = HANDLE_SIZE+3;
        int halfSize = size/2;
        int sx1 = ic.screenX(x) - halfSize;
        int sy1 = ic.screenY(y) - halfSize;
        int sx3 = ic.screenX(x+width) - halfSize;
        int sy3 = ic.screenY(y+height) - halfSize;
        int sx2 = sx1 + (sx3 - sx1)/2;
        int sy2 = sy1 + (sy3 - sy1)/2;
        
        int sw2 = (int)(0.14645*(sx3-sx1));
        int sh2 = (int)(0.14645*(sy3-sy1));
        
        if (sx>=sx1+sw2&&sx<=sx1+sw2+size&&sy>=sy1+sh2&&sy<=sy1+sh2+size) return 0;
        if (sx>=sx2&&sx<=sx2+size&&sy>=sy1&&sy<=sy1+size) return 1;     
        if (sx>=sx3-sw2&&sx<=sx3-sw2+size&&sy>=sy1+sh2&&sy<=sy1+sh2+size) return 2;     
        if (sx>=sx3&&sx<=sx3+size&&sy>=sy2&&sy<=sy2+size) return 3;     
        if (sx>=sx3-sw2&&sx<=sx3-sw2+size&&sy>=sy3-sh2&&sy<=sy3-sh2+size) return 4;     
        if (sx>=sx2&&sx<=sx2+size&&sy>=sy3&&sy<=sy3+size) return 5;     
        if (sx>=sx1+sw2&&sx<=sx1+sw2+size&&sy>=sy3-sh2&&sy<=sy3-sh2+size) return 6;
        if (sx>=sx1&&sx<=sx1+size&&sy>=sy2&&sy<=sy2+size) return 7;
        return -1;
    }

    public ImageProcessor getMask() {
        if (cachedMask!=null && cachedMask.getPixels()!=null)
            return cachedMask;
        ImageProcessor mask = new ByteProcessor(width, height);
        double a=width/2.0, b=height/2.0;
        double a2=a*a, b2=b*b;
        a -= 0.5; b -= 0.5;
        double xx, yy;
        int offset;
        byte[] pixels = (byte[])mask.getPixels();
        for (int y=0; y<height; y++) {
            offset = y*width;
            for (int x=0; x<width; x++) {
                xx = x - a;
                yy = y - b;   
                if ((xx*xx/a2+yy*yy/b2)<=1.0)
                    pixels[offset+x] = -1;
            }
        }
        cachedMask = mask;
        return mask;
    }

    /** Returns the perimeter length. */
    public double getLength() {
        double pw=1.0, ph=1.0;
        if (imp!=null) {
            Calibration cal = imp.getCalibration();
            pw = cal.pixelWidth;
            ph = cal.pixelHeight;
        }
        return Math.PI*(width*pw+height*ph)/2.0;
    }
    
    /** Returns Feret's diameter, the greatest distance between 
        any two points along the ROI boundary. */
    public double getFeretsDiameter() {
        double pw=1.0, ph=1.0;
        if (imp!=null) {
            Calibration cal = imp.getCalibration();
            pw = cal.pixelWidth;
            ph = cal.pixelHeight;
        }
        return width*pw>=height*ph?width*pw:height*ph;
    }
    
}