//
//This program is called by the spectrum.php program. It takes 
//a spreadsheet (created by Hread.out) of one days worth of 
//Cambridge data and performs a Fast Fourier Transform on intervals 
//of that data. It then takes the transform results from each 
//of the intervals and stacks them vertically next to each other 
//using a rainbow color scale to distinquish high values from low values.
//

import java.awt.*;
import java.io.*;
import java.awt.image.*;
import javax.imageio.*;
import java.util.StringTokenizer;
import java.text.*;


public final class FastFourierTransform {

  private int n, nu;

	public static void main(String [] args)throws IOException
        {
		BufferedReader in;
		String line, a, b;
		StringTokenizer st;
		int WIDTH=895;//1070
		int HEIGHT=1030;
		int TBORDER=80;
 	       int BBORDER=20;
 	       int RBORDER=120;
 	       int LBORDER=100;
		int GAP=50;
		int x, y=0, xspan, yspan=0;
		int freqSpan=500;//at 1 sample/second the folding freq. is 500 mHz
		int range, divisions, timeInterval, count=0, numImages, incr, colorVal,xInt,yInt, colorCount=0;
		double fL;
		Font BIGFONT=new Font("TimesRoman", Font.BOLD, 20);
		Font MEDFONT=new Font("TimesRoman", Font.BOLD, 15);
		Font SMAFONT=new Font("TimesRoman", Font.BOLD, 12);
		String freqLabel, yLabel="Frequency (mHz)";
		DecimalFormat one=new DecimalFormat("#0.0");
		double[][] data;
		double[][] data2;
		String inFile, year, day, hour, min, topLine="", sTime, tmpHour;
		int[][] colors=new int[15][3];

		colors[0][0]=255;colors[0][1]=255;colors[0][2]=255;//white
		colors[1][0]=245;colors[1][1]=204;colors[1][2]=179;
		colors[2][0]=255;colors[2][1]=00;colors[2][2]=00;//red
		colors[3][0]=255;colors[3][1]=55;colors[3][2]=00;//red/orange
		colors[4][0]=255;colors[4][1]=116;colors[4][2]=00;//orange
		colors[5][0]=255;colors[5][1]=195;colors[5][2]=00;//orange/yellow
		colors[6][0]=255;colors[6][1]=255;colors[6][2]=00;//yellow
		colors[7][0]=155;colors[7][1]=255;colors[7][2]=00;//yellow/green
		colors[8][0]=00;colors[8][1]=255;colors[8][2]=00;//green
		colors[9][0]=00;colors[9][1]=176;colors[9][2]=78;//green/blue
		colors[10][0]=00;colors[10][1]=00;colors[10][2]=255;//blue
		colors[11][0]=00;colors[11][1]=00;colors[11][2]=192;
		colors[12][0]=00;colors[12][1]=00;colors[12][2]=129;
		colors[13][0]=00;colors[13][1]=00;colors[13][2]=66;
		colors[14][0]=00;colors[14][1]=00;colors[14][2]=00;//black

		//get the input from the passed parameters
		if(args.length!=6)
		{
			System.out.println("ERROR usage: java FastFourierTransform2 <1,2> <1,2> <year> <day> <hour> <inputFile>");
			System.exit(1);
		}
		range=Integer.parseInt(args[0]);
		divisions=range*2;
		freqSpan=freqSpan/divisions;
		timeInterval=Integer.parseInt(args[1])+7;
		if(timeInterval==10)
			numImages=4;
		else if(timeInterval==11)
			numImages=2;
		else
			numImages=1;
		inFile=args[5];
		in=new BufferedReader(new FileReader(inFile));
		year=args[2];
		day=args[3];
		if(day.length()==1)
			day="00"+day;
		if(day.length()==2)
			day="0"+day;
		hour=args[4];
		min="00";
		if(Integer.parseInt(hour)<10)
			sTime="0"+hour+":"+min;
		else
			sTime=""+hour+":"+min;
		topLine="Year=20"+year+"        Julian Day="+day+"        UTC";

		//read past the header information for the data files
		line=in.readLine();
		line=in.readLine();
		line=in.readLine();

	BufferedImage img=new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
      	Graphics2D img_graphics=img.createGraphics();
	xspan=WIDTH-RBORDER-LBORDER;
	yspan=256;
	//set background
        img_graphics.setColor(Color.black);
        img_graphics.fillRect(0, 0, WIDTH, HEIGHT);
	img_graphics.setColor(Color.white);
        img_graphics.drawRect(LBORDER-1, TBORDER-3, WIDTH-RBORDER-LBORDER+5,yspan+8);
        img_graphics.drawRect(LBORDER-1, TBORDER-3+yspan+GAP, WIDTH-RBORDER-LBORDER+5,yspan+8);
        img_graphics.drawRect(LBORDER-1, TBORDER-3+2*yspan+2*GAP, WIDTH-RBORDER-LBORDER+5,yspan+8);
	//put label on top of image
	img_graphics.setColor(Color.white);
	img_graphics.setFont(BIGFONT);
	x=WIDTH/2-topLine.length()*5;
	y=45;
	img_graphics.drawString(topLine,x,y);
	//label the y-axis in mHz
	img_graphics.setColor(Color.white);
	img_graphics.setFont(SMAFONT);
	for(int j=0; j<3; j++)
	{
		for(int i=0; i<9; i++)
		{
			x=LBORDER-7;
			y=TBORDER+((j+1)*yspan+j*GAP)-(int)(((double)i/8)*yspan);
			img_graphics.fillRect(x-5,y,10,1);
			fL=((double)i/8)*freqSpan;
			freqLabel=one.format(fL);
			x-=freqLabel.length()*8;
			y+=5;
			if(i%4==0)
				img_graphics.drawString(freqLabel,x,y);
		}
		x=LBORDER+(xspan/2)-(yLabel.length()/2*10);
		y=y+25;
		img_graphics.drawString(yLabel,x,y);
	}
	//label the y-axis (X,Y,Z)
	img_graphics.setColor(Color.white);
	img_graphics.setFont(BIGFONT);
	x=LBORDER/2;
	y=TBORDER+(yspan/3)-15;
	img_graphics.drawString("X",x,y);
	y=TBORDER+yspan+GAP+(yspan/3)-15;
	img_graphics.drawString("Y",x,y);
	y=TBORDER+yspan+GAP+yspan+GAP+(yspan/3)-15;
	img_graphics.drawString("Z",x,y);
	x=15;
	y=WIDTH/2-117;
	img_graphics.setFont(MEDFONT);
	img_graphics.drawString("F",x,y);
	img_graphics.drawString("R",x,y+18*1);
	img_graphics.drawString("E",x,y+18*2);
	img_graphics.drawString("Q",x,y+18*3);
	img_graphics.drawString("U",x,y+18*4);
	img_graphics.drawString("E",x,y+18*5);
	img_graphics.drawString("N",x,y+18*6);
	img_graphics.drawString("C",x,y+18*7);
	img_graphics.drawString("Y",x,y+18*8);
	img_graphics.drawString("m",x,y+18*10);
	img_graphics.drawString("H",x,y+18*11);
	img_graphics.drawString("z",x,y+18*12);
	//put time label on the x-axis
	img_graphics.setColor(Color.white);
	img_graphics.setFont(SMAFONT);
	for(int i=0; i<3; i++)
	{
		y=TBORDER+(i+1)*yspan+i*GAP;
		for(int j=0; j<=24; j++)
		{
			x=LBORDER+j*xspan/24;
			img_graphics.fillRect(x,y,1,15);
			if(Integer.parseInt(hour)>23)
				tmpHour=Integer.toString(Integer.parseInt(hour)-24);
			else
				tmpHour=Integer.toString(Integer.parseInt(hour));
			if(Integer.parseInt(hour)<10)
				sTime="0"+tmpHour+":"+min;
			else
				sTime=""+tmpHour+":"+min;
			hour=Integer.toString(Integer.parseInt(hour)+1);
			x-=sTime.length()*3;
			if(j%3==0)
				img_graphics.drawString(sTime,x,y+27);
		}
		hour=Integer.toString(Integer.parseInt(hour)-25);
	}
	img_graphics.setFont(MEDFONT);
	x=WIDTH/2-11*5;
	y=TBORDER+3*yspan+2*GAP+50;
	img_graphics.drawString("Time (hours)",x,y);
	//put the color index on the plot
	img_graphics.setFont(SMAFONT);
	x=WIDTH-RBORDER/2-10;
	y=HEIGHT/4;
	incr=y*2/colors.length;
	for(int i=0; i<colors.length; i++)
	{
		img_graphics.setColor(new Color(colors[i][0],colors[i][1],colors[i][2]));
		img_graphics.fillRect(x,y,20,incr);
		if(i%2==1)
		{
			img_graphics.setColor(Color.white);
			img_graphics.drawString(Integer.toString(colorCount),x+25,y+5);
			colorCount--;
		}
		y+=incr;
	}
	img_graphics.setColor(Color.white);
	img_graphics.drawString("LOG",x+24,y);
	img_graphics.drawString("POWER",x+15,y+15);

	//read the data from the spreadsheet, convert it, and graph it
	    for(int k=0; k<(24*60)/(Math.pow(2,timeInterval)/60); k++)
	    {
		data=new double[3][(int)Math.pow(2,timeInterval)];
		data2=new double[3][(int)Math.pow(2,timeInterval-1)];

		xInt=(int)(xspan/((24*60)/(Math.pow(2,timeInterval)/60)));
		yInt=yspan/(data2[0].length/divisions);

		for(int i=0; i<data[0].length; i++)
		{
			line=in.readLine();
			if(line==null)
			{
				data[0][i]=data[0][i-1];
                                data[1][i]=data[1][i-1];
                                data[2][i]=data[2][i-1];
			}
			else
			{
				st=new StringTokenizer(line, " \t");
				a=st.nextToken();
				data[0][i] = Double.parseDouble(st.nextToken());
				data[1][i] = Double.parseDouble(st.nextToken());
				data[2][i] = Double.parseDouble(st.nextToken());
			}
		}

		for(int i=0; i<data[0].length-1; i++)
		{
			data[0][i]=data[0][i]-data[0][i+1];
			data[1][i]=data[1][i]-data[1][i+1];
			data[2][i]=data[2][i]-data[2][i+1];
		}
		data[0][data[0].length-1]=0.0;
		data[1][data[1].length-1]=0.0;
		data[2][data[2].length-1]=0.0;
		data2[0]=new FastFourierTransform().fftMag(data[0]);
		data2[1]=new FastFourierTransform().fftMag(data[1]);
		data2[2]=new FastFourierTransform().fftMag(data[2]);

		//graph the specturm values
		img_graphics.setColor(Color.green);
		for(int j=0; j<3; j++)
		{
			x=LBORDER+1+xInt*k;
			for(int i=0; i<data2[j].length/divisions; i++)
			{
				y=TBORDER+(j+1)*yspan+j*GAP-i*yInt-3;
				//y=(int)(data2[j][i]*yspan)*50;
				//if(y>yspan)
				//	y=yspan;

				if(Math.log(data2[j][i])>=0)
					colorVal=0;
				else if(Math.log(data2[j][i])>=-0.5)
					colorVal=1;
				else if(Math.log(data2[j][i])>=-1.0)
					colorVal=2;
				else if(Math.log(data2[j][i])>=-1.5)
					colorVal=3;
				else if(Math.log(data2[j][i])>=-2.0)
					colorVal=4;
				else if(Math.log(data2[j][i])>=-2.5)
					colorVal=5;
				else if(Math.log(data2[j][i])>=-3.0)
					colorVal=6;
				else if(Math.log(data2[j][i])>=-3.5)
					colorVal=7;
				else if(Math.log(data2[j][i])>=-4.0)
					colorVal=8;
				else if(Math.log(data2[j][i])>=-4.5)
					colorVal=9;
				else if(Math.log(data2[j][i])>=-5.0)
					colorVal=10;
				else if(Math.log(data2[j][i])>=-5.5)
					colorVal=11;
				else if(Math.log(data2[j][i])>=-6.0)
					colorVal=12;
				else if(Math.log(data2[j][i])>=-6.5)
					colorVal=13;
				else
					colorVal=14;
				img_graphics.setColor(new Color(colors[colorVal][0],colors[colorVal][1],colors[colorVal][2]));
				img_graphics.fillRect(x,y,xInt,yInt);//+(yspan-y),2,y);
			}
		}
	    }//end of for k

        String fname="../tmp/this";
	fname+=Integer.toString(count);
	fname+=".jpeg";
        File out_file=new File(fname);
        ImageIO.write(img, "jpeg", out_file);
  }

  private int bitrev(int j) {

    int j2;
    int j1 = j;
    int k = 0;
    for (int i = 1; i <= nu; i++) {
      j2 = j1/2;
      k  = 2*k + j1 - 2*j2;
      j1 = j2;
    }
    return k;
  }

  public final double[] fftMag(double[] x) {
    // assume n is a power of 2
    n = x.length;
    nu = (int)(Math.log(n)/Math.log(2));
    int n2 = n/2;
    int nu1 = nu - 1;
    double[] xre = new double[n];
    double[] xim = new double[n];
    double[] mag = new double[n2];
    double tr, ti, p, arg, c, s;
    for (int i = 0; i < n; i++) {
      xre[i] = x[i];
      xim[i] = 0.0f;
    }
    int k = 0;

    for (int l = 1; l <= nu; l++) {
      while (k < n) {
        for (int i = 1; i <= n2; i++) {
          p = bitrev (k >> nu1);
          arg = 2 * (double) Math.PI * p / n;
          c = (double) Math.cos (arg);
          s = (double) Math.sin (arg);
          tr = xre[k+n2]*c + xim[k+n2]*s;
          ti = xim[k+n2]*c - xre[k+n2]*s;
          xre[k+n2] = xre[k] - tr;
          xim[k+n2] = xim[k] - ti;
          xre[k] += tr;
          xim[k] += ti;
          k++;
        }
        k += n2;
      }
      k = 0;
      nu1--;
      n2 = n2/2;
    }
    k = 0;
    int r;
    while (k < n) {
      r = bitrev (k);
      if (r > k) {
        tr = xre[k];
        ti = xim[k];
        xre[k] = xre[r];
        xim[k] = xim[r];
        xre[r] = tr;
        xim[r] = ti;
      }
      k++;
    }

    mag[0] = (double) (Math.sqrt(xre[0]*xre[0] + xim[0]*xim[0]))/n;
    for (int i = 1; i < n/2; i++)
      mag[i]= 2 * (double) (Math.sqrt(xre[i]*xre[i] + xim[i]*xim[i]))/n;
    return mag;
  }
}

