package ij.io;
import ij.gui.*;
import java.io.*;
import java.util.*;
import java.net.*;

/*	ImageJ/NIH Image 64 byte ROI outline header
	2 byte numbers are big-endian signed shorts
	
	0-3		"Iout"
	4-5		version (>=217)
	6-7		roi type
	8-9		top
	10-11	left
	12-13	bottom
	14-15	right
	16-17	NCoordinates
	18-33	x1,y1,x2,y2 (straight line)
	34-35	line width (unused)
	36-39   ShapeRoi size (type must be 1 if this value>0)
	40-63	reserved (zero)
	64-67   x0, y0 (polygon)
	68-71   x1, y1 
	etc.
	
*/

/** Decodes an ImageJ, NIH Image or Scion Image ROI. */
public class RoiDecoder {

	private final int polygon=0, rect=1, oval=2, line=3, freeline=4, polyline=5, noRoi=6, freehand=7, traced=8, angle=9, point=10;
	private byte[] data;
	private String path;
	private InputStream is;
	private String name;
	private int size;

	/** Constructs an RoiDecoder using a file path. */
	public RoiDecoder(String path) {
		this.path = path;
	}

	/** Constructs an RoiDecoder using a byte array. */
	public RoiDecoder(byte[] bytes, String name) {
		is = new ByteArrayInputStream(bytes);	
		this.name = name;
		this.size = bytes.length;
	}

	/** Returns the ROI. */
	public Roi getRoi() throws IOException {
		if (path!=null) {
			File f = new File(path);
			size = (int)f.length();
			if (size>500000)
				throw new IOException("This is not an ImageJ ROI");
			name = f.getName();
			is = new FileInputStream(path);
		}
		data = new byte[size];

		int total = 0;
		while (total<size)
			total += is.read(data, total, size-total);
		is.close();
		if (getByte(0)!=73 || getByte(1)!=111)  //"Iout"
			throw new IOException("This is not an ImageJ ROI");
		int type = getByte(6);
		int top= getShort(8);
		int left = getShort(10);
		int bottom = getShort(12);
		int right = getShort(14);
		int width = right-left;
		int height = bottom-top;
		int n = getShort(16);
		
		if (name.endsWith(".roi"))
			name = name.substring(0, name.length()-4);
		boolean isComposite = getInt(36)>0;		
		if (isComposite)
			return getShapeRoi();

		Roi roi = null;
		switch (type) {
		case rect:
			roi = new Roi(left, top, width, height);
			break;
		case oval:
			roi = new OvalRoi(left, top, width, height);
			break;
		case line:
			int x1 = (int)getFloat(18);		
			int y1 = (int)getFloat(22);		
			int x2 = (int)getFloat(26);		
			int y2 = (int)getFloat(30);
			roi = new Line(x1, y1, x2, y2);		
			//IJ.write("line roi: "+x1+" "+y1+" "+x2+" "+y2);
			break;
		case polygon: case freehand: case traced: case polyline: case freeline: case angle: case point:
				//IJ.write("type: "+type);
				//IJ.write("n: "+n);
				//IJ.write("rect: "+left+","+top+" "+width+" "+height);
				if (n==0) break;
				int[] x = new int[n];
				int[] y = new int[n];
				int base1 = 64;
				int base2 = base1+2*n;
				int xtmp, ytmp;
				for (int i=0; i<n; i++) {
					xtmp = getShort(base1+i*2);
					if (xtmp<0) xtmp = 0;
					ytmp = getShort(base2+i*2);
					if (ytmp<0) ytmp = 0;
					x[i] = left+xtmp;
					y[i] = top+ytmp;
					//IJ.write(i+" "+getShort(base1+i*2)+" "+getShort(base2+i*2));
				}
				if (type==point) {
					roi = new PointRoi(x, y, n);
					break;
				}
				int roiType;
				if (type==polygon)
					roiType = Roi.POLYGON;
				else if (type==freehand)
					roiType = Roi.FREEROI;
				else if (type==traced)
					roiType = Roi.TRACED_ROI;
				else if (type==polyline)
					roiType = Roi.POLYLINE;
				else if (type==freeline)
					roiType = Roi.FREELINE;
				else if (type==angle)
					roiType = Roi.ANGLE;
				else
					roiType = Roi.FREEROI;
				roi = new PolygonRoi(x, y, n, roiType);
				break;
		default:
			throw new IOException("Unrecognized ROI type: "+type);
		}
		roi.setName(name);
		return roi;
	}
	
	public Roi getShapeRoi() throws IOException {
		int type = getByte(6);
		if (type!=rect)
			throw new IllegalArgumentException("Invalid composite ROI type");
		int top= getShort(8);
		int left = getShort(10);
		int bottom = getShort(12);
		int right = getShort(14);
		int width = right-left;
		int height = bottom-top;
		int n = getInt(36);

		ShapeRoi roi = null;
		float[] shapeArray = new float[n];
		int base = 64;
		for(int i=0; i<n; i++) {
			shapeArray[i] = getFloat(base);
			base += 4;
		}
		roi = new ShapeRoi(shapeArray);
		roi.setName(name);
		return roi;
	}

	int getByte(int base) {
		return data[base]&255;
	}

	int getShort(int base) {
		int b0 = data[base]&255;
		int b1 = data[base+1]&255;
		int n = (short)((b0<<8) + b1);
		if (n<-5000)
			n = (b0<<8) + b1; // assume n>32767 and unsigned
		return n;		
	}
	
	int getInt(int base) {
		int b0 = data[base]&255;
		int b1 = data[base+1]&255;
		int b2 = data[base+2]&255;
		int b3 = data[base+3]&255;
		return ((b0<<24) + (b1<<16) + (b2<<8) + b3);
	}

	float getFloat(int base) {
		return Float.intBitsToFloat(getInt(base));
	}

}