package utilities;

import java.awt.*;
import java.awt.image.*;

public class utils {

	private static final double kLog10 = Math.log(10.0);
	private static final double gBadResult = -1.234567;
	
	public static String num2strOld(double x) {
		String 	s,s2;
		int		l,i,eLoc;
		String	sign = "";
		
		if (x==gBadResult) 
			return "";
		
		if (x<0) {
			sign = "-";
			x = -x;
		}
		
		x = round(x,2);
			
		if (x==0) 
			s = "0";  // for some reason, MW Java return infinity
		else
			s = String.valueOf(x);
		l = s.length();
		
		s2 = "error";
		
		for (i=0; i<l; i++)
			if (s.charAt(i)=='.') {
				// Must be careful here because the number could be 
				// of the form 1.0335E-2, for example
				if (i==l-2) 
					s2 = s+"0";
				else {
					s2 = "";
					
					eLoc = -1;
					// now check to see if there is an E or e
					for (int j=i+1; j<l; j++) {
						if (s.charAt(j) == 'e' || s.charAt(j) == 'E') {
							eLoc = j;
							break;
						}
					}
					
					if (eLoc == -1) {	// i.e., no E
						for (int j=0;j<=i+2;j++)
							s2 += s.charAt(j);
					}
					else if (eLoc == i+2) { // i.e. 1.3E42
						for (int j=0; j<i+2; j++)
							s2 += s.charAt(j);
						s2 += '0';
						for (int j=eLoc; j<l; j++)
							s2 += s.charAt(j);
					}
					else if  (eLoc>i+2) { // i.e. 1.3536E23
						for (int j=0; j<=i+2; j++)
							s2 += s.charAt(j);
						for (int j=eLoc; j<l; j++)
							s2 += s.charAt(j);
					}
						
				}
				break;
			}
		if (i==l) s2 = s + ".00";
		return(sign+s2);
	}
	
	public static String num2str(double x) {
		int power;
		
		if (x==0) return "0";
		if (x == (int)x) return String.valueOf((int)x);
		if (x==Math.E) return "e";
		
		power = getPower(x);
		
		if (power>=-2 && power <= 4) {
			return num2strOld(x);
		}
		
		double factor = getMultFactor(x);
		
		return num2strOld(factor) + "E" + String.valueOf(power);
	}
	
	// finds the intercept of the line y=x0, and y = (y2-y1)/(x2-x1) (x - x1) + y1
	// but only if x0 lies between x1 and x2, else returns -1000
	public static int findIntercept(int x0, int x1, int y1, int x2, int y2) {
		if (x2 == x1) return -1000;
		if (x2<x1) {
			int h1 = x1;
			int h2 = y1;
			x1 = x2;
			y1 = y2;
			x2 = h1;
			y2 = h2;
		}
		if (x0<x1 || x0>x2) return -1000;
		return (y2-y1)*(x0-x1)/(x2-x1) + y1;
	}
	
	public static String num2strwsign(double x) {
		if (x<0) return num2str(x);
		return "+"+num2str(x);
	}
	
	public static void setCharAt(StringBuffer in, int at, char val) {
		try {
			in.setCharAt(at,val);
		}
		catch(StringIndexOutOfBoundsException e) {;}
	}
	
	public static String stripSpaces(String input) {
		return stripChars(input,' ');
	}
	
	public static String stripChars(String input, char ch) {
		int i,j,len;
	
		StringBuffer in = new StringBuffer(input);
		len = in.length();
		for (i=0; i<len; i++) {
			if (in.charAt(i)==ch) {
				for (j=i; j<len-1; j++)
					setCharAt(in,j,in.charAt(j+1));
				len--;
				try{in.setLength(len);}
				catch (StringIndexOutOfBoundsException e) {;}
				i--;
			}
		}
		
		return in.toString();
	}
	
	public static double round(double x, int numplaces) {
		double d;
		long i;
		int factor;
		
		factor = (int)Math.pow(10,numplaces);
		i = Math.round(factor * x);
		d = (double)i/(double)factor;
		return d;
	}
	
	/**
	 * Converts the specified floating point number to a string
	 * in scientific notation truncated at the hundreth's place.
	 * It appends a decimal point and zeros, if necessary.
	 * For example, 1.243 returns "1.24" while 1.2 returns "1.20"
	 * and 1 returns "1.00".
	 * @param x the floating point number to convert
	 */
	public static String num2scistr(double x, int numplaces) {
		String 	s="";
		int		log10;
		double	remainder;
		
		// get the sign of the number, and then make it positive
		if (x<0) s="-";
		x = Math.abs(x);
		
		// find the log
		if (x==0) {
			log10 = 0;
			remainder = 0;
		}
		else {
			try {
				log10 = (int)(Math.log(x)/Math.log(10));
				if (x<1) log10--;
				remainder = x/(double)(Math.pow(10,log10));
			}
			catch (ArithmeticException e) {
				return "";
			}
		}
				
		s += num2str(remainder,numplaces);
		s += "e";
		if (log10>=0) s+= "+";
		s += String.valueOf(log10);
		return s;
	}	
	/**
	 * Converts the specified floating point number to a string
	 * truncated at the hundreth's place.  It appends a decimal
	 * point and zeros, if necessary.
	 * For example, 1.243 returns "1.24" while 1.2 returns "1.20"
	 * and 1 returns "1.00".
	 * @param x the floating point number to convert
	 */
	public static String num2strwith0s(double x, int numplaces) {
		String 	s,s2;
		int		l,i;
		
		x = round(x,numplaces);
		s = String.valueOf(x);
		
		if (numplaces == 0) return s;
		
		l = s.length();
		
		s2 = "error";
		
		for (i=0; i<l; i++)
			if (s.charAt(i)=='.') {
				if (i<l-numplaces) {
					s2 = "";
					for (int j=0;j<=i+numplaces;j++)
						s2 += s.charAt(j);
					break;
				}
				else {
					for (int j=1; j<=numplaces; j++) {
						if (i==l-j) {
							s2 = new String(s);
							for (int k=1; k<=numplaces - j + 1; k++)
								s2 += "0";
							i=l+1;
							break;
						}
					}
				}
			}
		if (i==l) {
			s2 = s + ".";
			for (int k=0; k<numplaces; k++)
				s2 += "0";
		}
		return(s2);
	}
	
	public static double str2num(String s) {
	
		s = stripSpaces(s);
		if (s.equals("e")) return Math.E;
		if (s.equals("P")) return Math.PI;
		if (s.length()==0) return gBadResult;
		
		boolean pi = false;
		if (s.charAt(s.length()-1) == 'P') {
			pi = true;
			s = s.substring(0,s.length()-1);
		}
		double d;
		try { d = (Double.valueOf(s)).doubleValue(); }
		catch (NumberFormatException ex) {d = gBadResult;}
		if (pi)
			return d*Math.PI;
		return d;
	}
	
	public static void drawSpecialString(Graphics g, String s, int left, int y, FontMetrics fm) {
		int x = left;
		
		for (int i=0; i<s.length(); i++) {
			char		ch = s.charAt(i);
			Character	Ch = new Character(ch);
				
			switch (ch) {
				case '^':	g.drawString(Ch.toString(),x,y-3);
							break;
				case '_':	g.drawString(Ch.toString(),x,y-8);
							break;
				case 'M':	drawMu(g,x,y);
							x += 8;
							break;
				case 'P':	drawPi(g,x,y);
							x += fm.charWidth(ch);
							break;
				case 'T':	drawTheta(g,x,y);
							x += 8;
							break;
				case 'S':	drawSigma(g,x,y);
							x += 8;
							break;
				default:	g.drawString(Ch.toString(),x,y);
							x += fm.charWidth(ch);
			}
		}
	}
	
	
	/** Draws a Mu symbol at the specified location */
	public static void drawMu(Graphics g, int x, int y) {
		y+=1;
		
		g.drawLine(x,y-6,x,y);
		
		g.drawLine(x,y-3,x+3,y-2);
		g.drawLine(x+4,y-2,x+5,y-6);
		g.drawLine(x+5,y-6,x+5,y-2);
	}
	
	/** Draws a Theta symbol at the specified location */
	public static void drawTheta(Graphics g, int x, int y) {
//		g.drawOval(x,y-7,7,7);
		g.drawOval(x,y-8,6,8);
		g.drawLine(x,y-4,x+6,y-4);
	}
	
	/** Draws a PI symbol at the specified location */
	public static void drawPi(Graphics g, int x, int y) {
		g.drawLine(x+2,y-1,x+2,y-6);
		g.drawLine(x+1,y-6,x+6,y-6);
		g.drawLine(x+5,y-6,x+5,y-1);
	}
	public static double getDouble(TextField tf) {
		return str2num(tf.getText());
	}
	
	
	/**
	 * Truncates the specified floating point number to the hundreth's place
	 */ 
	 public static double truncate(double x) {
	 	return (double)(((int)(x*100))/100.0);
	 }
	 
	 public static boolean ptInRect(int x, int y, Rectangle r) {
	 	if (r.width<0) {
	 		if (x<r.x + r.width) return false;
	 		if (x>r.x) return false;
	 	}
	 	else {
	 		if (x<r.x) return false;
	 		if (x>r.x + r.width) return false;
	 	}
	 	if (r.height<0) {
	 		if (y<r.y + r.height) return false;
	 		if (y>r.y) return false;
	 	}
	 	else {
	 		if (y<r.y) return false;
	 		if (y>r.y + r.height) return false;
	 	}
	 	return true;
	 }
	 
	 private static final int kSensitivity = 8;
	 
	 public static boolean ptInStretch(int x, int y, Rectangle r) {
	 	if (r.width<0) {
	 		if (x>r.x+r.width+kSensitivity) return false;
	 		if (x<r.x+r.width) return false;
	 	}
	 	else {
	 		if (x>r.x + r.width) return false;
	 		if (x<r.x + r.width-kSensitivity) return false;
	 	}
	 	if (r.height<0) {
	 		if (y>r.y+r.height+kSensitivity) return false;
	 		if (y<r.y+r.height) return false;
	 	}
	 	else {
	 		if (y>r.y + r.height) return false;
	 		if (y<r.y + r.height-kSensitivity) return false;
	 	}
	 	return true;
	 }
	 
	 public static boolean ptInUpperLeft(int x, int y, Rectangle r) {
	 	if (r.width<0) {
	 		if (x>r.x) return false;
	 		if (x<r.x-kSensitivity) return false;
	 	}
	 	else {
	 		if (x>r.x + kSensitivity) return false;
	 		if (x<r.x) return false;
	 	}
	 	if (r.height<0) {
	 		if (y>r.y) return false;
	 		if (y<r.y - kSensitivity) return false;
	 	}
	 	else {
	 		if (y>r.y + kSensitivity) return false;
	 		if (y<r.y) return false;
	 	}
	 	return true;
	 }
	 
	 public static boolean ptInLowerRight(int x, int y, Rectangle r) {
	 	return ptInStretch(x,y,r);
	 }
	 
	 public static Image loadImage(String filename, Component obs) {
		Image anImage;
		MediaTracker	track = new MediaTracker(obs);
		
		anImage=Toolkit.getDefaultToolkit().getImage("Images/" + filename);
    	
    	track.addImage(anImage,0);
    	
    	try {
    		track.waitForAll();
    	}
    	catch (InterruptedException e) {
    		System.out.println("Error loading image.");
    	}
    	
    	
    	return anImage;
    }
		
	public static Image loadImageFile(String filename) {
		Image anImage;
		anImage=Toolkit.getDefaultToolkit().getImage(filename);
    	return anImage;
    	
    }
    
	public static void pause(int time) {
		try { Thread.sleep(time); }
		catch (InterruptedException e) {}
	}

	public static double log10(double x) { 
		if (x<0) return gBadResult;
		return Math.log(x)/kLog10; 
	}
	
	public static double log(double x, double base) { 
		if (x<0) return gBadResult;
		return Math.log(x)/Math.log(base); 
	}
	
	
	public static double exp10(double x) { return Math.pow(10,x);}
	
	/**
	 * Returns the value v, such that v is the minimum multiple of
	 * tickspacing greater then absmin.
	 */
	public static double findMin(double absmin, double tickspacing) {
		double minMin = 0;
		if (tickspacing<0) return 0;
		if (absmin>0) {
			while (minMin<absmin) minMin += tickspacing;
		}
		else {
			while (minMin>=absmin) minMin -= tickspacing;
			minMin += tickspacing;
		}
		
		return minMin;
	}
	
	public static int getNextSpace(String s, int pos) {
		for (int i = pos; i<s.length(); i++) {
			try {
				if (s.charAt(i) == ' ') return i;
			}
			catch(StringIndexOutOfBoundsException e) {;}
		}
		return s.length();
	}


	public static String firstEq(String s) {
		
		try {
		for (int i = 0; i<s.length(); i++) {
				if (s.charAt(i) == ';') 
					return s.substring(0,i);
			}
		}
		catch(StringIndexOutOfBoundsException e) {;}
		
		return null;
	}
	
	public static String secondEq(String s) {
		
		try {
		for (int i = 0; i<s.length(); i++) {
				if (s.charAt(i) == ';') 
					return s.substring(i+1,s.length());
			}
		}
		catch(StringIndexOutOfBoundsException e) {;}
		
		return null;
	}

	/** 
	 * this method returns the distance of point (alpha,beta) to
	 * the line segment (x0,y0) -> (x1,y1).
	 */
	public static int	distance(int x0, int y0, int x1, int y1, int alpha, int beta) {
		int	max = 1000;
		int	d;
		
		if (x0 == x1) {
			if ((beta<y0)&&(beta<y1)) return max;
			if ((beta>y0)&&(beta>y1)) return max;
			return Math.abs(alpha - x0);
		}
		if (y0 == y1) {
			if ((alpha<x0)&&(alpha<x1)) return max;
			if ((alpha>x0)&&(alpha>x1)) return max;
			return Math.abs(beta - y0);
		}
		double	m = (double)(y1 - y0)/(double)(x1-x0);
		double  p = (beta + alpha/m - y0 + m*x0)/(m+1/m);
		double	q = m*p + y0 - m*x0;
		
		if ((p<x0) && (p<x1)) return max;
		if ((p>x0) && (p>x1)) return max;
		if ((q<y0) && (q<y1)) return max;
		if ((q>y0) && (q>y1)) return max;
		 
		return (int)(Math.sqrt((alpha-p)*(alpha-p) + (beta-q)*(beta-q)));
		
	}
	
	
	public static synchronized void drawDashedLine(Graphics g, int x0, int y0, int x1, int y1) {
		double L = Math.sqrt((x1-x0)*(x1-x0)+(y1-y0)*(y1-y0));
		double dashlen = 6;
		double spacelen = 4;
		
		double xold=x0,xnew;
		double yold=y0,ynew;
		
		double xFactor = (x1-x0)/L;
		double yFactor = (y1-y0)/L;
		int numsteps = (int)(L/(dashlen+spacelen));
		
		for (int i=0; i<numsteps; i++) {
			xnew = xold + dashlen * xFactor;
			ynew = yold + dashlen * yFactor;
			g.drawLine((int)xold,(int)yold,(int)xnew,(int)ynew);
			xold = xnew + spacelen * xFactor;
			yold = ynew + spacelen * yFactor;
		}
	}
	
	public static void fancyBorder(Graphics g, int width, int height) {
		Color c = Color.lightGray;
		g.setColor(c);
		g.fillRect(0,0,width,height);
		for (int i=4; i>0; i--) {
			c = c.darker();
			g.setColor(c);
			g.drawRect(i,i,width-2*i,height-2*i);
		}
	}
	
	public static void showErrorDialog(Frame F, String msg) {
//		Dialog D = new YesNoDialog(F,"Error",msg,"OK",null,null);
//		D.show();
	}
	
	// if the number is 1.3e4, this returns the 4
	public static int getPower(double x) {
		if (x==0) return -7;
		if (x<0) x = -x;
		
		return (int)Math.floor(log10(x));
	}
	
	
	// if the number is 1.3 x base^4, this returns the 4
	public static int getPower(double x, double base) {
		if (x==0) return -10;
		if (x<0) x = -x;
		
		return (int)Math.floor(log(x,base));
	}
	
	
	// if the number is 1.3e11, this returns the 1.3
	public static double getMultFactor(double x) {
		int powr;
		
		powr = getPower(x);
		
		return x/exp10(powr);
	}
	
	
	// if the number is 1.3e^powr, this returns the 1.3
	public static double getMultFactor(double x,int powr) {
		return x/exp10(powr);
	}
	
	
	public static String getTickString(double x, int powr) {
		if (powr>2 || powr <=-2) 
			return num2str(getMultFactor(x,powr),1);
			
		return num2str(x,2);
	}
		
	public static String getTickString(double x) {
		return getTickString(x,getPower(x));
	}
	
	public static double getTickSpacingEstimate(double minv, double maxv) {
		double 	diff;
		int 	powrMin;
		int		powrMax;
		
		diff = (maxv - minv);
		powrMin = (int)Math.floor(log10(diff/10.0));
		powrMax = (int)Math.floor(log10(diff/3.0));
		if (powrMin != powrMax) {
			return (double) exp10(powrMin+1);
		}
		
		return (double) 4.0*exp10(powrMin);
	}
	
	
	public static String num2str(double x,int numplaces) {
		String 	s;
		String sign="";
		
		if (x<0) {
			x = -x;
			sign = "-";
		}
		if (x==Math.floor(x))
			s = String.valueOf((int)x);	// takes care of integers without adding .0
		else {
			x = round(x,numplaces);
			s = String.valueOf(x);
		}
		return sign+s;
	}
	
    public static int[] rot90Pixels (int pixels[], int width, int height) {
        int newPixels[] = null;
        if ((width*height) == pixels.length) {
            newPixels = new int [width*height];
            int newIndex=0;
            for (int x=width-1;x>=0;x--)
                for (int y=0;y<height;y++)
                    newPixels[newIndex++]=pixels[y*width+x];
        }
        return newPixels;
    }
    
    /** given two points x1,y1 and x2,y2 and a value x3, this
	    method returns y3 where y3 lies on the line defined by
	    the two points at position x3. */
	public static double interp(double x1, double y1, double x2, double y2, double x3) {
		double value;
		
		value = y1 + (x3-x1)/(double)(x2-x1) * (y2 - y1);
	
		return value;
	}
	
    /** given two points x1,y1 and x2,y2 and a value x3, this
	    method returns y3 where y3 lies on the line defined by
	    the two points at position x3. */
	public static int interp(int x1, int y1, int x2, int y2, int x3) {
		double value;
		
		value = y1 + (x3-x1)/(double)(x2-x1) * (y2 - y1);
	
		return (int)value;
	}
	
	
	protected static final Color	kTextFieldColor = new Color(245,240,240);
	protected static final Color	kTextForeColor = new Color(0,0,0);
	protected static final Color	kButtonColor = new Color(150,150,150);
	
	// makes the specified textfield have the default background and foreground colors.
	// Also does a "select all" for the textfield.
	public static void setTextFieldColors(Component tf) {
		tf.setBackground(kTextFieldColor);
		tf.setForeground(kTextForeColor);
/*		if (tf instanceof TextField)
			((TextField)tf).selectAll();*/
		if (tf instanceof TextArea)
			((TextArea)tf).selectAll();
	}
		
	public static void setButtonColors(Button b) {
		b.setBackground(kButtonColor);
	}
		
	public static void center(Frame f) {
		Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
		int newx, newy;
		int w = f.size().width;
		int h = f.size().height;
		newx = (screenDim.width - w)/2;
		newy = (screenDim.height - h)/2;
		f.reshape(newx,newy,w,h);
	}
	
	
	public static String replace(String in, String what, String with) {
		StringBuffer outBuffer = new StringBuffer();
		String		tempString;
		
		int 	i;
		String 	op;
		
		try{
		for (i=0; i<=in.length()-what.length(); i++) {

			tempString = in.substring(i,i+what.length());
			if (what.equals(tempString)) {
				outBuffer.append(with);
				i+=what.length()-1;
			}
			else {
				outBuffer.append(in.charAt(i));
			}
		}
		while (i<in.length())
			outBuffer.append(in.charAt(i++));
		}
		catch(StringIndexOutOfBoundsException e) {;}
		
		return outBuffer.toString();
	}
	
	public static void drawCentered(Graphics g, String s, Rectangle r) {
		FontMetrics fm = g.getFontMetrics(g.getFont());
		int width = fm.stringWidth(s);
		int height = fm.getAscent() + fm.getDescent();
		g.drawString(s,r.x + r.width/2 - width/2, r.y + r.height/2 + height/2 - fm.getDescent());
	}
	
	/** Draws a Theta symbol at the specified location */
	public static void drawSigma(Graphics g, int x, int y) {
		g.drawOval(x+1,y-5,4,4);
		g.drawLine(x+3,y-5,x+6,y-5);
	}
}