import java.awt.Color;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.Iterator;

import javax.media.opengl.GL;

import processing.core.PApplet;
import processing.core.PFont;
import processing.core.PGraphics;
import processing.opengl.PGraphicsOpenGL;


/*Pehr Hovey 
 * DeweyForest.java
 * MAT259 Data Visualization W09
 * 
 * A grid displays 'trees' representing days of library data. Trees display basic information about the distribution of dewey categories in library checkouts.
 * Clicking on a 'tree' will select it and display detailed information about that day.
 * * 
 */
public class DeweyForest extends PApplet {
	
	//The screen
	int WIDTH = 800;
	int HEIGHT = 600;
	
//	int WIDTH = 1280;
//	int HEIGHT = 1024;

	long initTimeStamp; //for saving screenshots with unique filenames between program executions
	
	//data information
	String data_filename = "dewey_checkins.txt"; //a whole year
//	String data_filename ="dewey_checkins - june.txt";
//	String data_filename = "dewey_checkins - incomplete.txt";
	String small_font_path = "Calibri-Bold-50.vlw";
	String big_font_path = "Calibri-Bold-100.vlw";
	String smallest_font_path = "text.vlw";
	
	//Text stuff
	PFont font100, font50,font24;
	float text_height = 50;
	int label_color = Color.white.getRGB();
	
	String screenshotspath = "/screenshots/";

	int NUM_DAYS = 40;
	int MAX_COLS = 7; //wrap to next row...
	
	int MAX_OBJ_WIDTH = 200; //grid cells use this
	int MAX_OBJ_HEIGHT = 800; //height of total tree object
	int MAX_OBJ_HEIGHT_SEL = (int) (1.5*800); //height of total tree object
	AutoLerp height_AL,prev_height_AL;
	int OBJ_RADIUS = 65; //trees use this for their width/
	
	int ROWS; //derived from max_cols and # of trees 
	
	//for aligning things to a grid...	
	float xstart=0, ystart=0;	
	float grid_x_space = MAX_OBJ_WIDTH;
	float grid_y_space = MAX_OBJ_WIDTH;
	float grid_x_pad = 250; //a gutter between grid cells
	float grid_y_pad = 250; //a gutter between grid cells
	float grid_line_weight = 1.5f;
	
	float dewey_line_weight = 3f;
	float MONTH_LABEL_PAD = 200;
	
	//For displaying a calendar header
	String current_month_label;
	int current_month_index=0;
	int single_month_index = 0;
	int current_year=0;
	int current_month = 0;
	
	//color IDs for figuring out if we clicked the buttons...
	int next_month_button_id = 35;
	int prev_month_button_id = 36;
	
	
	String[] month_names = { "January", "February", "March", "April", "May",
			"June", "July", "August", "September", "October", "November",
			"December" };

	String[] month_names_abbrev = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
			"Jul", "Aug", "Sept", "Oct", "Nov", "Dec" };
	
	static String[] day_names = { "Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
			"Saturday"}; //static so deweyTree can access it
	
	String[] day_names_abbrev = { "Sun","Mon", "Tues", "Wed", "Thurs", "Fri",
			"Sat", "Sun"};
	
	String[] dewey_names={ "Generalities","Philosophy & Psychology", "Religion", "Social Sciences", "Language", "Nat. Sciences & Math",
			"Technology", "Arts","Literature", "Geography & History"		
	};
	
	
	
	boolean draw_all_months = false;
	boolean simple_grid = false; //simple grid or one with padding between each cell?
	boolean min_grid = true; //only draw necessary cells?
	boolean noisy_grid = false;
	boolean number_the_grid = false; //cell numbers, not day_of_month numbers
	boolean draw_axes = false;
	
	boolean do_detailed_view = true; //enable drawing all 100 cats
	
	//For scaling the trees 
	boolean relative_heights = true;
	int largest_grand_total; 
	
	//For displaying the trees
	int[][] slice_colors; //a set of slice colors, potentially different for each month
	int faded_alpha = 60;
	int full_alpha = 220;
	
	
	AutoLerp forest_alpha_AL, highlight_alpha_AL, prev_highlight_alpha_AL;
	//these get changed by the autolerp
	private int forest_alpha; //alpha for most of the forest
	private int highlight_alpha;
	private int previous_highlight_alpha;
	
	int auto_lerp_time = 30; //number of frames
	int tree_selected_color = color(255,0,0);
	private int CYLINDER_SIDES = 4;
	
	boolean connect_the_slices = true;
	boolean connect_by_week = true; //if true, go down the week with the lines, if false, connect same days of weeks instead (see trend for all Sundays, for ex)
	boolean draw_trees = true;
	boolean draw_date_labels = true;
	boolean show_fps = false;
	
	//Camera and environment parameters

	float lightX = 20f;
	float lightY = 20f;
	float lightZ = 20f;

	float rotCamX = 0f;
	float rotCamY = 0f;
	float rotCamZ = 0f;
	//Defaults...
//	float camX = 200f;
//	float camY = -200f;
//	float camZ = 1500f;
//	float rotObjX = 75f;
//	float rotObjY = 0f;
//	float rotObjZ = 45f;

	//Hand picked
	float camX = 550f;
	float camY = -175f;
	float camZ = 1950f;

	float rotObjX = 56f;
	float rotObjY = 0f;
	float rotObjZ = 20f;
	
	
	ArrayList<DeweyTree> current_trees; //a set of trees that belong to the same month
	ArrayList<ArrayList<DeweyTree>> all_months; //array of arraylists of DeweyTrees. Each Arraylist is trees for one month. 
	
	//Stuff for selecting trees
	//store last mouseXY here and flag draw method to process
	PGraphics color_buffer;   // buffer
	int[] lastScreenXY;
	boolean process_color_buffer = false;
	
	int tree_selected_id=-1; //which tree (index into trees) is selected/highlighted?
	int previous_tree_selected_id = -1; //save the last one too 
	int tree_selected_wedge = -1;
	
	ArrayList colors_by_id; //int colors indexed by an id# that corresponds to an object we want to be able to select using a color buffer
	
	private boolean run_anim=true;
	
	ArrayList holidays;//An arraylist of arraylists of grid cell numbers, indexed by month_index
	int holiday_color = color(0,0,255);
	
	private boolean debug; //print lots of debugging info?

	
	///for 06/2008 what grid-cells corespond to holidays?
	//month is 1-based (05 is May) because it comes from a ckidate string
	//based off code at http://www.javaworld.com/javaworld/javatips/jw-javatip44.html
	private ArrayList calculate_holidays(int month, int year){
		ArrayList holidays = new ArrayList();
		 Calendar cal = new GregorianCalendar(year, month-1, 1);  //what day of week is first day of the month? 		 
		 int offset = cal.get(Calendar.DAY_OF_WEEK)-1;  //First day of week is returned as 1
		int week_offset;
		int day_of_month=0;
		
		//Ten federal holidays: 
		 if(month == 1){
			   // New Years Day (Jan. 01)
			 holidays.add(offset); //first day of the month
		
			 //* Martin Luther King Jr. Day (3rd Monday in January)
//			 
		 	switch(offset){	 //based on the day of the week the month starts on here is the day of the month
				  case 1 : // Monday
					  day_of_month = 15;
					  break;
				 case 0 : // Sunday
					 day_of_month = 16;
					 break;
				 
				 case 5 : // Friday
					 day_of_month =  18;
					 break; 
				 case 4 : // Thursday
					 day_of_month =  19;
					 break;
				 case 3 : // Wednesday
					 day_of_month =  20;
					 break;
				 case 2 : // Tuesday
					 day_of_month =  21;
					 break;
				 default : // Saturday
					 day_of_month =  17;
		 	}
		 	
		      holidays.add(offset+day_of_month-1);
		      
		    
		}else if(month == 02){
			// Third Monday in February
			 
			 switch(cal.get(Calendar.DAY_OF_WEEK)-1)
             {
		       case 0 : // Sunday
		       	day_of_month = 16;
		       	break;
		       case 1 : // Monday
		       	day_of_month = 15;
		       	break;
		       case 2 : // Tuesday
		       	day_of_month =  21;
		       	break;
		       case 3 : // Wednesday
		       	day_of_month =  20;
		       	break;
		       case 4 : // Thursday
		       	day_of_month =  19;
		       	break;
		       case 5 : // Friday
		       	day_of_month =  18;
		       	break;
		       default : // Saturday
		    
		       	day_of_month =  17;
		       }
	 holidays.add(offset+day_of_month-1);
	 
			      
	    }else if(month == 05){
	//		    * Memorial Day (Last Monday in May)
	    	
	        Calendar may = new GregorianCalendar(year, month-1, 31);  //what day of week is last day? 		 
			
			 switch(may.get(Calendar.DAY_OF_WEEK))
	                  {
	            case 0 : // Sunday
	            	day_of_month = 25;
	            	break;
	            case 1 : // Monday
	            	day_of_month = 31;
	            	break;
	            case 2 : // Tuesday
	            	day_of_month =  30;
	            	break;
	            case 3 : // Wednesday
	            	day_of_month =  29;
	            	break;
	            case 4 : // Thursday
	            	day_of_month =  28;
	            	break;
	            case 5 : // Friday
	            	day_of_month =  27;
	            	break;
	            default : // Saturday
	            	day_of_month =  26;
	            break;
	            }
			 holidays.add(offset+day_of_month-1);
	    }else if(month == 07){
//		    * Independence Day (July 04th)
	    	holidays.add(offset+04 -1);
	    	
	    }else if(month == 9){
//		    * Labor Day (1st Monday in September)

			switch(cal.get(Calendar.DAY_OF_WEEK)-1)
	            {
	            case 0 : // Sunday
	            	day_of_month =  2;
	            	break;
	            case 1 : // Monday
	            	day_of_month =  1;
	            	break;
	            case 2 : // Tuesday
	            	day_of_month =  7;
	            	break;
	            case 3 : // Wednesday
	            	day_of_month =  6;
	            	break;
	            case 4 : // Thursday
	            	day_of_month =  5;
	            	break;
	            case 5 : // Friday
	            	day_of_month =  4;
	            	break;
	            default : // Saturday
	            	day_of_month =  3;

	            }

			 holidays.add(offset+day_of_month-1);
			 
	    }else if(month == 10){
//		    * Columbus Day (2nd Monday in October)
	        // Second Monday in October 		 
	        
	        switch(cal.get(Calendar.DAY_OF_WEEK)-1)
	            {
	            case 0 : // Sunday
	            	day_of_month = 9;
	            	break;
	            case 1 : // Monday
	            	day_of_month =  15;
	            	break;
	            case 2 : // Tuesday
	            	day_of_month = 14;
	            	break;
	            case 3 : // Wednesday
	            	day_of_month =  13;
	            	break;
	            case 4 : // Thursday
	            	day_of_month =  12;
	            	break;
	            case 5 : // Friday
	            	day_of_month =  11;
	            	break;
	            default : // Saturday
	            	day_of_month =  10;
	            break;
	            }

	        holidays.add(offset+day_of_month-1);
	    }else if(month == 11){
//		    * Veterans Day (November 11)
	    	holidays.add(offset + 11-1);
	    	
//		    * Thanksgiving Day (4th Thursday of November)
        		 
		        switch(cal.get(Calendar.DAY_OF_WEEK)-1)
		            {
		            case 0 : // Sunday
		            	day_of_month = 26;
		            	break;
		            case 1 : // Monday
		            	day_of_month =  25;
		            	break;
		            case 2 : // Tuesday
		            	day_of_month = 24;
		            	break;
		            case 3 : // Wednesday
		            	day_of_month =  23;
		            	break;
		            case 4 : // Thursday
		            	day_of_month =  22;
		            	break;
		            case 5 : // Friday
		            	day_of_month =  28;
		            	break;
		            default : // Saturday
		            	day_of_month =  27;
		            break;
		            }

		        holidays.add(offset+day_of_month-1);
	    	
	    }else if(month == 12){

//		    * Christmas Day (Dec. 25th)
	    	holidays.add(offset + 25-1);
		
	    }
		 println("o d y,m,h "+offset+" "+day_of_month+" "+year+" "+month+" -> "+holidays );
		return holidays;
	}
	//Return ArrayList of of ArrayList<DeweyTree>
	private ArrayList<ArrayList<DeweyTree>> loadTransactions(String filename){
		ArrayList<ArrayList<DeweyTree>> all = new ArrayList<ArrayList<DeweyTree>>(); //and arrayList of arraylists
		
		ArrayList one_month = new ArrayList();
		ArrayList hdays;
		
		println("loading file... \"" + filename + "\"");
		String[] lines = loadStrings(filename);
		

		String[] fields = { "" };
		DeweyTree tree;
			
		String sections[];

			println("This file has format version "+lines[0]);
	
			fields = PApplet.split(lines[1], ",");
			println("This file has these fields: "
					+ Arrays.toString(fields));
			
			//Sort ALL counts by date so we only encounter one month at a time
			Arrays.sort(lines); //this will put non-numerical data at the end so do not go to the end of the file
			
			String current_month_string=""; //string of format: 2008-03  so we know when we need to make a new month
			
			int tree_num=-1;
			holidays = new ArrayList(); //init holidays array
		for (int line_num = 0; line_num < lines.length-2; line_num++) {
//				println(line_num);
				 sections = split(lines[line_num], ",");
	
				 //Create a new month?
				 String this_one = sections[0].substring(0,7);
				 if(!this_one.equals(current_month_string)){
					 one_month = new ArrayList(); //arraylist of DeweyTrees
					 tree_num=0; //reset id
					 all.add(one_month);
					 current_month_string = this_one;
				if(debug)	 System.out.println("Found a new month: "+current_month_string+" all.size() = "+all.size());
					
					 //init holidays for this month/year
					 int month = Integer.parseInt(sections[0].substring(5,7)); 
					 int year =  Integer.parseInt(sections[0].substring(0,4));
							
					 hdays = calculate_holidays(month,year);
					 holidays.add(hdays);
					 
					 
				 }else{
					 tree_num++;
				 }
				 
				 tree = new DeweyTree(tree_num,sections);
				 
				 if(tree.grand_total > largest_grand_total)
					 largest_grand_total = (int) tree.grand_total;
				 
				 one_month.add(tree);
				 
//				 println("Added: "+tree);
			
		}
		
		return all;
		
	}
	
//	PGraphicsOpenGL pgl;
//	GL gl;
	public void setup() 
	{
		//size(WIDTH,HEIGHT, OPENGL);
		size(800,600, OPENGL);
		
		hint(ENABLE_OPENGL_4X_SMOOTH);

		smooth();
		color_buffer = createGraphics(WIDTH,HEIGHT, P3D);//must use P3D for this, cant use an offscreen buffer with OpenGL
		  
	  initTimeStamp = System.currentTimeMillis();
	  
	  if(dataPath("").contains("bin")){
		  screenshotspath = ".."+screenshotspath; //keep it out of the bin folder...
		  data_filename = "../../data/"+data_filename;
		  big_font_path = "../../data/"+big_font_path;
		  small_font_path = "../../data/"+small_font_path;
		  smallest_font_path = "../../data/"+smallest_font_path;
	  }
			  
	    all_months = loadTransactions(data_filename);
	    Iterator<ArrayList<DeweyTree>> itr = all_months.iterator();
	    while(itr.hasNext()){
	    	ArrayList<DeweyTree> month = (ArrayList<DeweyTree>) itr.next();
	    	month = sortTreesByDate(month);
	    	
	    }
	    
	    changeMonth(0); //go to first month must do this right away so current_trees is not null

	    adjustRows(); //inits ROWS based on number of trees in the current_month and MAX_COLS
	    forest_alpha_AL = new AutoLerp(255,1); //fade the forest in and out
	    highlight_alpha_AL = new AutoLerp(255,1); //fade the selected tree in and out
	    prev_highlight_alpha_AL = new AutoLerp(255,1); //fade the old selected tree out
	    height_AL = new AutoLerp(MAX_OBJ_HEIGHT_SEL,1);
	    prev_height_AL = new AutoLerp(MAX_OBJ_HEIGHT_SEL,1);
	   
	    println("We have "+all_months.size()+" months");
	
			  
	 lastScreenXY = new int[2];
	 
	  background(0); 
	  //stroke(255);
	  lightX = width/2f + 0;
	  lightY = height/2f + 0;
	  lightZ = 400f;
//	  
	  font50 = loadFont(small_font_path);
	  font100 = loadFont(big_font_path);
	  font24 = loadFont(smallest_font_path);
	  textFont(font50, 50);
	 

	  
//	 initRandomSliceColors();
	 initHSBSliceColors();
	 initColorsByID();
	 setAllUnselected();
	  
//	  initializeCells();
	}

	
	 private void initColorsByID() {
		// TODO Auto-generated method stub
		 colors_by_id = new ArrayList();
		  
		  //assign color buffer colors and make sure IDs are current since trees might have been sorted
		  int r=200,g=0,b=0;
		  Integer color;
		  DeweyTree tree;
		  for(int tt=0; tt<50;tt++){ //only one month will be clickable at a time so we do not need very many colors
			  color =  new Integer(color(r,g,b));
		
			colors_by_id.add(color); //index is ID
//			println("Tree#"+tt+" has color "+new Color(color)+" for color_buffer");
			r+=20;
			boolean reset = false;
			if(r > 255){
				r=255;
				g+=20;
				if(g > 255){
					g = 255;
					b+=20;
					if(b>255){
						if(reset){
							b=255;
							System.err.println("Err: Out of Colors for color buffer! Change the algorithm");
							System.exit(-1);
						}else{
							reset=true;
							b=10;//reset but offset
							r=10;
							g=10;
						}
//						
						
					}
				}
			}
		  }
		  //process_color_buffer = true; //make sure its drawn once
	}


	private void initRandomSliceColors() {
		// TODO Auto-generated method stub
		 
		  slice_colors = new int[all_months.size()][10]; //[month_index][slice]
		 for(int m=0; m < all_months.size(); m++){
		  for(int c=0; c<10; c++){
				 // slice_colors[c] = 	color(random(110,255), random(110,255), random(110,255));
				  float r = random(100,200);
				  float g = random(100,200);
				  float b =random(100,200);
				
				  float brightness = 0.99f;
				  float saturation = 0.6f;
				  float hue = random(0,200);
			
				  slice_colors[m][c] =  Color.HSBtoRGB(hue, saturation, brightness);//color(r,g,b);
				
			  }
		 }
	}

	 private void initHSBSliceColors() {
			// Pick a random hue for each month and pick 9 more colors for each month based on that
			 
			  slice_colors = new int[all_months.size()][10]; //[month_index][slice]
			  float main_hue = 300;
			  
			 for(int m=0; m < all_months.size(); m++){
//				  float brightness = 0.85f;
				 float brightness = 0.6f;
				  float saturation = 0.5f;
			  for(int c=0; c<10; c++){

					
					
					  float hue = (main_hue+(c+1)*55) % 360; //next hue
//					  System.out.print(m+"-"+c+" hue = "+hue);
					  			
					  slice_colors[m][c] =  Color.HSBtoRGB(hue/360f, saturation, brightness);//color(r,g,b);
//					  System.out.println("  "+slice_colors[m][c]);
					  brightness += ((1-0.6)/10);
//					  saturation += ((1-0.5)/10);
					
				  }
			  //pick a new starting hue
			  main_hue+=55;println("m"+m+"->"+main_hue);
			 }
		}
	 
//Make sure trees within a month are properly sorted
	public ArrayList<DeweyTree> sortTreesByDate(ArrayList<DeweyTree> trees)
	  {
		  Collections.sort(trees, new Comparator(){
		 
	      public int compare(Object o1, Object o2)
	      {
	    	 DeweyTree e1 = (DeweyTree) o1;
	    	 DeweyTree e2 = (DeweyTree) o2;
	  
	        return e1.ckidate.compareTo(e2.ckidate); 
	      }
		  }
	    );
		  return trees;
	  }
	//Make sure all_months[0] is the earliest month, and so on
	public ArrayList<ArrayList<DeweyTree>>sortMonthsByDate(ArrayList<ArrayList<DeweyTree>> all_months)
	  {
		  Collections.sort(all_months, new Comparator(){
		 
	      public int compare(Object o1, Object o2)
	      {
	    	  ArrayList<DeweyTree> e1 = (ArrayList<DeweyTree>) o1;
	    	  ArrayList<DeweyTree> e2= (ArrayList<DeweyTree>) o2;
	    	  DeweyTree t1 = e1.get(0);
	    	  DeweyTree t2 = e2.get(0);
	        return t1.ckidate.compareTo(t2.ckidate); 
	      }
		  }
	    );
		  return all_months;
	  }
	
	 
	 public void printTrees(ArrayList trees){
		 Iterator itr = trees.iterator();
		 while(itr.hasNext()){
			 println(itr.next().toString());
		 }
	 }

	public void draw()
	{
	 
		
		background(0);
	
	  /** 
	   * Setup Camera :
	   * convenience method that initializes PROJECTION matrix & MODELVIEW matrix
	   **/
	  beginCamera();
	  {
	    //position camera at default position

	    camera(); 
//	    ortho(0, width, 0, height, -10, 10);
//	   perspective();]
//	    ortho(0,width,0,height,-100,200);
	    perspective(PI/3.0f, width/(float)height, camZ/10.0f, camZ*10.0f);
	    //move camera
	    translate(camX, camY, camZ);

	    //rotate camera in place
	    rotateX(radians(rotCamX));
	    rotateY(radians(rotCamY));
	    rotateZ(radians(rotCamZ));
	  }
	  endCamera();
	 if(process_color_buffer){ //mirror the transformations into the colorbuffer so they match
		 color_buffer.beginDraw();
		 
		 color_buffer.beginCamera();
		 color_buffer.camera(); 

		   //move camera
		 color_buffer.translate(camX, camY, camZ);

		    //rotate camera in place
		 color_buffer. rotateX(radians(rotCamX));
		 color_buffer. rotateY(radians(rotCamY));
		 color_buffer. rotateZ(radians(rotCamZ));
		  
	    color_buffer.endCamera();
		 
	 }
	  /**
	   * Draw objects in 3D space
	   **/

	  //draw point representing lightbulb
	  //  noLights();
	  //  pushMatrix();
	  //  {
	  //    translate(lightX, lightY, lightZ);
	  //    rect(0,0,10,10);
	  //    text("light!", 10, 0);
	  //  }
	  //  popMatrix();

	 // pointLight(200,200,200, lightX, lightY, lightZ); 
	  pointLight(200,200,200, camX, camY, camZ);
	  int light_intensity = 200;
	  ambientLight(light_intensity, light_intensity, light_intensity);

	  pushMatrix();//A
	  {
	    //rotate object around center of the screen based on the mouse
	    translate(width/2, height/2, 0);
	    rotateX((float)Math.toRadians(rotObjX));
	    rotateY((float)Math.toRadians(rotObjY));
	    rotateZ(-(float)Math.toRadians(rotObjZ)); //More natural rotation by negating it
	    translate(-width/2, -height/2, 0);
	    
	    if(process_color_buffer){
			color_buffer.pushMatrix();//A
			color_buffer.translate(width/2, height/2, 0);
			color_buffer.rotateX((float)Math.toRadians(rotObjX));
			color_buffer.rotateY((float)Math.toRadians(rotObjY));
			color_buffer.rotateZ(-(float)Math.toRadians(rotObjZ)); //More natural rotation by negating it
			color_buffer.translate(-width/2, -height/2, 0);
			
	  }
	    /**** We now have our environment transformations set to begin making grids and stuff ******/
	    
	    //Draw a grid...
	    if(draw_all_months){
	    	pushMatrix();
	    	int row=0;
	    	int col=0;
	    	int max_col = 3;
	    	
	    	float row_trans, col_trans;
	    	if(simple_grid){
	    		col_trans = (MAX_COLS+1)*(grid_x_space)+MONTH_LABEL_PAD;
	    		row_trans = (ROWS+1)*(grid_y_space)+MONTH_LABEL_PAD;
	    		
	    	}else{
	    		col_trans = (MAX_COLS+1)*(grid_x_space+grid_y_pad)+MONTH_LABEL_PAD;
	    		row_trans = (ROWS+1)*(grid_y_space+grid_y_pad)+MONTH_LABEL_PAD;
	    	}
	    	 
	    	for(int i =0; i < all_months.size(); i++){
	    		changeMonth(i); 

	    		drawCalendarLabels();
			    drawTreesOnGrid();
			    drawGrid();
			    drawDateLabels();
			    drawHolidays();
			    drawCalendarButtons();
	    		
	    		if(col < (max_col)){
	    			translate(col_trans,0,0); //go over	
	    			col++;
	    		}else{
	    			translate(-max_col*col_trans,row_trans,0); //go back to begininng and down a row
	    			col=0;
	    		}
	    			    		 
	    	}
	    	popMatrix();
	    }else{ //draw just the current month
	    	drawCalendarLabels();
		    drawTreesOnGrid();
		    drawGrid();
		    drawDateLabels();
		    drawHolidays();
		    drawCalendarButtons();
	    }
	    
//	    drawGridCoords();
	
	  popMatrix();//A

	  /**
	   * Now lets draw some 2D things over our 3D world.
	   * These will *not* be affected by our 3D transformations.
	   **/

	  //reset camera
	 
		  camera();
		  noLights();
		  if(show_fps){
		 fill( label_color);
		 textAlign(LEFT);
		 textFont(font24,24);
		  text("fps = " + frameRate, 10, height - 5);
		  }
	
	  drawLegend();
	  
	  
	  if(process_color_buffer){//See what we selected
		  println("Seeing what we selected ...");
		  // get the pixel color under the mouse
		  int pick = color_buffer.get(lastScreenXY[0], lastScreenXY[1]);
//		  lastScreenXY[0] = -1; lastScreenXY[1] = -1; //invalidate them
		  // get object id
		  int id = getIDForColor(pick); //will be negative if invalid, might not belong to a tree either
		  println("Clicked on object id="+id);
		  if(id==next_month_button_id){
			  incrementMonth();
		  }else if(id==prev_month_button_id){
			  decrementMonth();
		  }else if(id >= 0 && id < current_trees.size()){ //selected a tree
			  if(id != tree_selected_id)
				  selectTreeByID(id);//this will cause things to change on next draw() iteration
//			  else
				  
//			  println("Found Color="+new Color(pick)+" so id = "+id);
		  }else{
			  println("No Tree Selected bg color="+new Color(pick));
			  setAllUnselected(); //fade back to full alpha all around
			
			  
		  }
		  
		  
		  color_buffer.popMatrix();//A
		  process_color_buffer = false; //reset flag
	
		  color_buffer.endDraw();
		
	  }
	  }
	}

	//Draw what each color means
private void drawLegend(){
	hint(DISABLE_DEPTH_TEST);
	float y_step = HEIGHT / 10.0f;
	float y=(float) (0.5f*y_step); 
	int x = 20;
//	textFont(font24,24);
	textFont(font50,24);
	textAlign(LEFT);
	for(int s=9; s >= 0; s--){
		
		fill(slice_colors[current_month_index][s]);
	
		text(s+"00\n"+dewey_names[s], x,y);		
		y+=y_step;
	}
	hint(ENABLE_DEPTH_TEST);
}
	private void drawHolidays() {

		strokeWeight(grid_line_weight);
	
		noFill();
		float[] center_coords;
		int first_grid_id=-1,last_grid_id=-1; //if we want to only draw the min # of grid cells, what range is this?
		if(min_grid){
			first_grid_id = current_trees.get(0).grid_cell_number; //get the first cell #
			last_grid_id = current_trees.get(current_trees.size()-1).grid_cell_number; //get the last cell #
			
		}
		if(simple_grid){
			//			do nothing for now...
		}else{
			
			Iterator hdays = ((ArrayList) holidays.get(current_month_index)).iterator();
			while(hdays.hasNext()){
				Integer cell = (Integer) hdays.next();
				if(min_grid && (cell.intValue() > last_grid_id) || (cell.intValue() < first_grid_id))
					continue; //skip this holiday
				
				center_coords = getGridCellCenterCoords(cell.intValue());
				
				fill(holiday_color, 120);
				rect(center_coords[0],center_coords[1],grid_x_space, grid_y_space);
			}
			
	}
		
	}


	//returns xy of the CENTER of a grid cell
	
	void incrementMonth(){
		setAllUnselected();
		current_month_index++;
		if(current_month_index >= all_months.size())
			current_month_index = all_months.size()-1;
		changeMonth(current_month_index); //change based on index
	
	}
	
	void decrementMonth(){
		setAllUnselected();
		current_month_index--;
		if(current_month_index <0)
			current_month_index = 0;
		
		changeMonth(current_month_index); //change based on index
		
		
		
	}
	
	//this will do what is necessary to change the month on the calendar
	//takes effect on the next draw() loop
	private void changeMonth(int index){
		noLoop(); //pause animation so the current_trees list is not accessed by drawing thread while it is changing
		int month_num;
		if(index >= 0 && index < all_months.size()){
			current_month_index = index;
			
			current_trees = all_months.get(current_month_index); 
			
			adjustRows();
			
			DeweyTree sample = current_trees.get(0);
			String ckidate = sample.ckidate;
			
			month_num = sample.month;
			current_year = sample.year;
			
//			println("month_num is "+month_num+" from "+ckidate.substring(5,7));
			if(month_num <= 12 && month_num > 0 ){
				current_month = month_num;
//				current_year = Integer.parseInt(ckidate.substring(0,4));
				current_month_label = month_names[month_num-1]+" "+current_year;			
				if(debug) System.out.println("Current Month is index# "+current_month_index+" = "+month_names[month_num-1]+" "+current_year);
			}else{
				System.err.println("Invalid month number: "+month_num);
			}
			
		}else{
			System.err.println("Invalid month index for all_trees: "+current_month_index);
			System.exit(-1);
		}
		loop();

	}
	
//	private void changeMonth(String ckidate){ //update status vars first
//		current_month_index = Integer.parseInt(ckidate.substring(5,7));
//	    current_year = Integer.parseInt(ckidate.substring(0,4));
//	    current_trees = all_months.get(current_month_index); 
//	    if(current_month_index <= 12 && current_month_index > 0 ){
//			current_month_label = month_names[current_month_index]+" "+current_year;
//			
//		}else{
//			System.err.println("Invalid month index: "+current_month_index);
//		}
//	}
	
	void drawCalendarLabels(){
		textAlign(CENTER);
		fill(label_color);
		
		float[] month_coords = getMonthLabelCoords(); //x,y pair for big month label
		textFont(font100,100);
		text(current_month_label, month_coords[0], month_coords[1]);
		
		textFont(font50,50);
		
		
		
		String name;
		float[] day_names_x = getDayLabelXCoords(); //one x coord for each column
		for(int col=0; col < day_names_x.length; col++){ 
			if(!simple_grid)
				name =day_names[col % 7] ;
			else
				name = day_names_abbrev[col % 7];
			text(name, day_names_x[col],(float) -0.5*MONTH_LABEL_PAD );
		}
	}
	

	private void drawCalendarButtons() {
		// TODO Auto-generated method stub
		
		textFont(font50,50);
		textAlign(CENTER);
		float[] prevnext = getPrevNextButtonCoords();
//		println("Drawing buttons at xy xy "+prevnext[0]+" "+prevnext[1]+" "+prevnext[2]+" "+prevnext[3]);
		
		if(current_month_index > 0){ //there is a previous month
		fill(label_color);
		text("<- Previous Month", prevnext[0],prevnext[1]);
//		noFill();
//		stroke(label_color);
//		rect(prevnext[0],prevnext[1]-25,600,100);
		}
		
		if(current_month_index < (all_months.size() - 1)){ //there is a next month
		fill(label_color);
		text("Next Month ->",prevnext[2],prevnext[3]);
//		noFill();
//		stroke(label_color);			
//		rect(prevnext[2],prevnext[3]-25,600,100);
		
		}
				
		if(process_color_buffer){
//			println("drawing cal button rects");
			color_buffer.fill(getColorForID(next_month_button_id));
			color_buffer.stroke(getColorForID(next_month_button_id));
//			println("next fill is "+getColorForID(next_month_button_id));
			color_buffer.rect(prevnext[2],prevnext[3]-25,600,100);
			
			color_buffer.fill(getColorForID(prev_month_button_id));
			color_buffer.rect(prevnext[0],prevnext[1]-25,600,100);
		}
		
		
	}
	//Given an absolute cell number, figure out row,col
	float[] getGridCellCenterCoords(int grid_cell){
		int[] rowcol = getRowCol(grid_cell);
		return getGridCellCenterCoords(rowcol[0],rowcol[1]);
	}
	
	float[] getGridCellCenterCoords(int row, int col){
	
		float[]xy = new float[2];
		if(simple_grid){
			xy[0] = col*grid_x_space+(grid_x_space/2);
			xy[1] = row*grid_y_space+(grid_y_space/2);
		}else{
			xy[0] = col*(grid_x_space+grid_x_pad)+(grid_x_space/2);
			xy[1] = row*(grid_y_space+grid_y_pad)+(grid_y_space/2);
		}
		if(noisy_grid){
			xy[0]+=+60*noise(row*col);
				xy[1]+=+60*noise(row*col);
		}
//		println("r,c "+row+" "+col+" -- > "+xy[0]+","+xy[1]);
		return xy;
	}
	float[] getDayLabelXCoords(){ //these go on top of the calendar, centered on each day cell
		float[]x = new float[MAX_COLS]; //
		for(int col=0; col < MAX_COLS; col++){
			if(simple_grid){
				x[col] = (float) ((col)*(grid_x_space)+grid_x_space/2.0);//go to just inside the cell (x-wise)
			}else{
				
				x[col] = (float) ((col)*(grid_x_space+grid_x_pad)+grid_x_space/2.0);//go to just inside the cell (x-wise)
			}
			
			
		}

		return x;
	}
	
	float[] getMonthLabelCoords(){ //place a label above and centered with the calendar
		float[]xy = new float[2];
		if(simple_grid){
			xy[0] = (float) (0.5*(MAX_COLS)*grid_x_space); 
			xy[1] = -1.5f*MONTH_LABEL_PAD; ///stack month on top of next/prev buttons
		}else{
			xy[0] = (float) ((float) (0.5*(MAX_COLS-1)*(grid_x_space+grid_x_pad))+0.5*grid_x_pad);//go to just inside the cell (x-wise)
			xy[1] = -MONTH_LABEL_PAD;//go to just outside bottom of the cell (y-wise)
		}

		return xy;
	}
	
	float[] getPrevNextButtonCoords(){ //prev_x, prev_y,next_x, next_y 
		float[]xyxy = new float[4];
		float[] monthcoords = getMonthLabelCoords();
		if(simple_grid){
			
			xyxy[0] = (float) (0.5*monthcoords[0]);
			xyxy[1] =-MONTH_LABEL_PAD;
			xyxy[2] = (float) (1.5*monthcoords[0]);
			xyxy[3] = -MONTH_LABEL_PAD;
		}else{

			
			xyxy[0] = (float) (0.5*monthcoords[0]);
			xyxy[1] = monthcoords[1];
			xyxy[2] = (float) (1.5*monthcoords[0]);
			xyxy[3] = monthcoords[1];
			
			
		}
		return xyxy;
	}
	
	//Convert a grid cell number to a row,col index which varies depending on how many cols there are
	int[] getRowCol(int grid_cell){
		int[] rowcol = new int[2];

		rowcol[0] =(int) Math.floor(grid_cell/MAX_COLS);
		rowcol[1] = (grid_cell % MAX_COLS);
			return rowcol;
	}
	//the month/day label for each cell in the month
	float[] getDateLabelCoords(int grid_cell){
		int[] rowcol = getRowCol(grid_cell);
		return getDateLabelCoords(rowcol[0],rowcol[1]);
	}
	float[] getDateLabelCoords(int row, int col){ //place a label at the bottom of a grid cell
		float[]xy = new float[2];
		if(simple_grid){
			xy[0] = (float) ((col)*grid_x_space+grid_x_space/2.0); //go to just inside the cell (x-wise)
			xy[1] = (row+1)*grid_y_space; //go to bottom of the cell (y-wise)
		}else{
			xy[0] = (float) ((col)*(grid_x_space+grid_x_pad)+grid_x_space/2.0);//go to just inside the cell (x-wise)
			xy[1] = (row)*(grid_y_space+grid_y_pad)+grid_y_space + text_height;//go to just outside bottom of the cell (y-wise)
		}
//		println("r,c "+row+" "+col+" -- > "+xy[0]+","+xy[1]);
		return xy;
	}
	float[] getGridCoordLabelCoords(int row, int col){ //place a label at the bottom of a grid cell
		float[]xy = new float[2];
		if(simple_grid){
			xy[0] = (col)*grid_x_space; //go to just inside the cell (x-wise)
			xy[1] = (row)*grid_y_space; //go to bottom of the cell (y-wise)
		}else{
			xy[0] = (col)*(grid_x_space+grid_x_pad);//go to just inside the cell (x-wise)
			xy[1] = (row)*(grid_y_space+grid_y_pad) ;//go to just outside bottom of the cell (y-wise)
		}
//		println("r,c "+row+" "+col+" -- > "+xy[0]+","+xy[1]);
		return xy;
	}
	//label coords of each cell
	void drawGridCoords(){
		
		float[] label_coords;  
		
		int count = 0;
		for(int r=0; r < ROWS; r++){ //draw grid with padding to have each cell separated
			
			for(int c=0; c < MAX_COLS; c++){			
				label_coords = getGridCoordLabelCoords(r,c);
				textAlign(LEFT);
				fill(label_color);
				text(""+label_coords[0]+", "+label_coords[1],label_coords[0],label_coords[1]);
				count++;
				
				if(min_grid && count == current_trees.size())
					break;
			
			}//end of this row	
			if(min_grid && count == current_trees.size())
				break;
			
		}
	}
	
	void drawDateLabels(){
		
		//Draw the date label
    	if(draw_date_labels){
    		pushMatrix();
    		float[] label_coords;
    		 Calendar cal = new GregorianCalendar(current_year, current_month-1, 1);  //what day of week is first day of the month? 		 
    		int offset = cal.get(Calendar.DAY_OF_WEEK)-1; 
    		int max_date = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
	    	fill(label_color);
	    	textAlign(CENTER);
	    	for(int cell=offset; cell<offset+max_date; cell++){
	    	label_coords = getDateLabelCoords(cell);
	    	String month_str = ""+current_month;
	    	if(current_month < 10)
	    		month_str = "0"+month_str;
	    	
	    	String day_str = ""+(cell-offset+1);
	    	if(day_str.length()==1)
	    		day_str = "0"+day_str;
	    	text(month_str+"-"+day_str,label_coords[0],label_coords[1],0); 
	    	}
	    	popMatrix();
    	}
		
	}
	
	void drawGrid(){
		pushMatrix();
		if(process_color_buffer){
			color_buffer.pushMatrix();
		}
		stroke(128);
		strokeWeight(grid_line_weight);
		
		noFill();
		float[] center_coords;
		int first_grid_id=-1,last_grid_id=-1; //if we want to only draw the min # of grid cells, what range is this?
		if(min_grid){
			first_grid_id = current_trees.get(0).grid_cell_number; //get the first cell #
			last_grid_id = current_trees.get(current_trees.size()-1).grid_cell_number; //get the last cell #
			
		}
		if(simple_grid){
			float x=0, y=0;
			float xmax,ymax;
			 xmax= x+MAX_COLS*(grid_x_space);
			 ymax =  y+ROWS*(grid_y_space);
			 y=ystart;
			for(int r=0; r < ROWS; r++){ //draw simple grid with minimal lines needed
				
				x=xstart;
				for(int c=0; c < MAX_COLS; c++){			
					line(x,ystart,0,x,ymax,0); //vertical
					line(xstart,y,0,xmax, y,0); //horiz
					noFill();
		
					x+=grid_x_space;
								
				}//end of this row	
				y+=grid_y_space;
				
			}
			//Cap the ends of the grid
			line(xmax,ystart,0,xmax,ymax,0); //vertical
			line(xstart,ymax,0,xmax, ymax,0); //horiz
		}else{
			rectMode(CENTER);
			if(process_color_buffer){
				color_buffer.rectMode(CENTER);
			}
	
			 int max_cell_id = MAX_COLS*ROWS;
			 
			for(int cell = 0; cell < max_cell_id; cell++){		
				
					if(min_grid && ( cell > last_grid_id || cell < first_grid_id)){
//						println("skipping cell# "+cell+" bc first/last = "+first_grid_id+" "+last_grid_id);
						continue;//skip this cell
					}
					
					center_coords = getGridCellCenterCoords(cell);
					
					noFill();
					rect(center_coords[0],center_coords[1],grid_x_space, grid_y_space);
					
					if(number_the_grid){
						fill(label_color);
						text(""+cell,center_coords[0],center_coords[1] );
					}
	
				}	

		}
		
		//Draw rects into the color buffer for where trees are if necessary
		if(process_color_buffer ){ //we may draw more cells than there are trees
			Iterator trees = current_trees.iterator();
			DeweyTree tree;
			float[] rect_coords;
			while(trees.hasNext()){
				tree=(DeweyTree) trees.next();
				rect_coords = getGridCellCenterCoords(tree.grid_cell_number);
				color_buffer.fill(getColorForID(tree.id));	
				color_buffer.rect(rect_coords[0],rect_coords[1],grid_x_space, grid_y_space);	
				
			}
			
			
		}
		

		if(draw_axes){
			strokeWeight(3);
			//draw some axes...
			stroke(color(255,0,0)); fill(color(255,0,0));
			line(-grid_x_space,0,0,(float) (3*grid_x_space),0,0); //X
			text("Y",(float) -grid_x_space,0,0);
			
			stroke(color(0,255,0)); fill(color(0,255,0));
			line(0,-grid_y_space,0,0, (float) (3*grid_y_space),0); //Y
			text("Y",0,(float) -grid_y_space,0);
			
			stroke(color(0,0,255)); fill(color(0,0,255));
			line(0,0,-grid_y_space,0,0, (float) (3*grid_y_space)); //Z
			text("Z",0,0,(float) -grid_y_space);
		}

		popMatrix();
		if(process_color_buffer){
			color_buffer.popMatrix();
		}
	}

	private void drawTreesOnGrid() { //draw each tree on the grid and draw to color buffer if needed
		
		pushMatrix();
		
	    DeweyTree tree;
	    double[][] pcts;
	    
     	forest_alpha = (int) forest_alpha_AL.update(); //advance the auto fader
     	highlight_alpha = (int) highlight_alpha_AL.update(); //advance the auto fader
     	previous_highlight_alpha = (int) prev_highlight_alpha_AL.update(); //advance the auto fader
     	
    	float [] center_coords;
    	float [] label_coords;
    	float [][] last_xyz = new float[10][3]; //for each slice store the xyz of the top of the slice
    	
    	if(process_color_buffer){ //once per draw()
    		 color_buffer.pushMatrix();
    		 color_buffer.background(-1); // since background is not an object, its id is -1
    		 color_buffer.noStroke();
    	}  	 
	  
    	float max_height; //used in scaling tree height
    	
    	int whole_cat_index = 10;
    	if(connect_the_slices)
    		strokeWeight(dewey_line_weight);
	    for(int t=0; t < current_trees.size(); t++){
	    	
	    	tree = current_trees.get(t);
	    	
	    	pcts = tree.percentages; //the percent of total obj height that each main cat occupies
//	    	println("Drawing tree # "+tree.id+" a cell "+tree.grid_cell_number);
	    	double relative_pct;
	    	if(relative_heights)
	    		relative_pct = tree.grand_total / largest_grand_total; //how tall this should be relative to largest totem
	    	else
	    		relative_pct = 1.0;
	    	int cumulative_hght=1; //start at 1 so that bottom of tree does not interfere with the grid
	    	float slice_height;
	    		    	
	    	center_coords = getGridCellCenterCoords(tree.grid_cell_number);
	    	
	    	//Inform the tree of where it is plotted
	    	tree.grid_x = center_coords[0];
	    	tree.grid_y = center_coords[1];
	    	

	    	if(process_color_buffer){ //once per tree
	    		color_buffer.fill(getColorForID(tree.id));
	    	}
	    	
	    	
//	    	 if(tree_selected_id == tree.id){ //draw the highlight under tree to show it is selected
//				 noStroke();
//    			 fill(tree_selected_color,highlight_alpha);
//				 rectMode(CENTER);
//				 rect(center_coords[0],center_coords[1],grid_x_space,grid_x_space);	    
////    				 println("Selecting tree#"+t);
//			 }
	    	
	    	if(!draw_trees){ //draw a simple shape instead so we know where the trees would be
	    		fill(slice_colors[current_month_index][0],128);
	    		noStroke();
    			 drawCylinder(center_coords[0],center_coords[1], cumulative_hght,OBJ_RADIUS,(float) (OBJ_RADIUS),10,CYLINDER_SIDES );
	    	}
	    	float wedge_z;
	    	
	    	if(tree.id == tree_selected_id)
	    		max_height = height_AL.update();
	    	else if(tree.id == previous_tree_selected_id)
	    		max_height = prev_height_AL.update();
	    	else
	    		max_height = MAX_OBJ_HEIGHT;
	    		
		    	for(int slice=0; slice < 10; slice++){
		    		
		    		 if(tree.id == tree_selected_id){ //draw extra detail 
		    			 slice_height = (float) (pcts[slice][whole_cat_index]*max_height); //how tall this wedge should be
		    			 fill(slice_colors[current_month_index][slice], highlight_alpha);
		    			 float wedge_pct;
		    			 float wedge_rad;
		    			 wedge_z=cumulative_hght;
		    			 
		    			 for(int w=0; w<10;w++){
		    				 wedge_pct = (float) (pcts[slice][w]);
		    				 float wedge_height = slice_height/10;
		    				 wedge_rad = (wedge_pct*OBJ_RADIUS);
		    				 if(wedge_rad<10) wedge_rad=10;
		    				 noStroke();
		    				 drawCylinder(center_coords[0],center_coords[1], wedge_z,wedge_rad,wedge_rad,wedge_height,CYLINDER_SIDES );
		    				 
			    			 if(process_color_buffer){//once per slice
			    				 
//			    				 drawCylinder(color_buffer,center_coords[0],center_coords[1], wedge_z,wedge_rad,wedge_rad,wedge_height,CYLINDER_SIDES );
			    				 drawCylinder(color_buffer,center_coords[0],center_coords[1], wedge_z,wedge_rad,wedge_rad,wedge_height,CYLINDER_SIDES );
			    			 }
			    			 
			    			 wedge_z+=wedge_height;
		    			 }
		    			 
		    			 pushMatrix(); //label each major cat at TOP of the whole slice
		    			 translate((float) (center_coords[0]+1.3f*OBJ_RADIUS),center_coords[1],wedge_z);
		    			 textFont(font50,50);
		    			 textAlign(LEFT, TOP);
	    				 rotateX((float) Math.toRadians(-90));
		    			 text(slice+"00",0,0, 0);
	    				 popMatrix();
		    			 
		    			 
		    		 }else{ //just draw this slice as one obj 
		    			 slice_height = (float) (pcts[slice][whole_cat_index]*relative_pct*max_height); //how tall this wedge should be

		    			 if(tree.id == previous_tree_selected_id)
		    				 fill(slice_colors[current_month_index][slice],previous_highlight_alpha);
		    			 else
		    				 fill(slice_colors[current_month_index][slice],forest_alpha);
		    			 
		    			 if(draw_trees){
		    			
			    			 noStroke();
			    			 drawCylinder(center_coords[0],center_coords[1], cumulative_hght,OBJ_RADIUS,(float) (OBJ_RADIUS),slice_height,CYLINDER_SIDES );
			    		
			    			 if(process_color_buffer){//once per slice
			    				 drawCylinder(color_buffer,center_coords[0],center_coords[1], cumulative_hght,OBJ_RADIUS,(float) (OBJ_RADIUS),slice_height,CYLINDER_SIDES );
			    			 }
			    		 
			    		 }
		    		 }
		    		
		    		  //connect current tree to the previous tree?
		    		 //avoid zig zags by not connecting the last of one row to the first of another
		    		 //Not drawn into color_buffer
		    		 if(connect_the_slices && t > 0){
		    			 if(connect_by_week && (center_coords[0] - last_xyz[slice][0] > 0)){//only draw lines that go down the week and not zig zag 
		    				 if(tree.id == tree_selected_id || tree.id == tree_selected_id+1){ //hilight the lines on either side of this tree
		    					 stroke(slice_colors[current_month_index][slice],highlight_alpha);
//		    				 }else if(tree_selected_id > 0){
//		    					 stroke(slice_colors[current_month_index][slice],50); //very faint
//		    				 }else{//nothing is selected, show all
//		    					 stroke(slice_colors[current_month_index][slice],255);
//		    				 } 
		    				 }else if(tree.id == previous_tree_selected_id){
				    				stroke(slice_colors[current_month_index][slice],previous_highlight_alpha);
				    			
		    				 }else{
		    						 stroke(slice_colors[current_month_index][slice],forest_alpha);
		    				 }
		    				 
		   			 		line(center_coords[0], center_coords[1], cumulative_hght+slice_height, last_xyz[slice][0],last_xyz[slice][1],last_xyz[slice][2]); //connect this slice to previous one
		    			 }else{
		    				 
		    			 }
		   			 }
		    	
		    		 cumulative_hght+=(slice_height);
	    			 
	    			 last_xyz[slice][0] = center_coords[0];
	    			 last_xyz[slice][1] = center_coords[1];
	    			 last_xyz[slice][2] = cumulative_hght; //height from the xy plane
	    			 
	    			 if(slice<9){ //separate the slices a bit .. do this even if not drawing them so that lines are at the right height
		 			    
		 			    if(tree.id == tree_selected_id)		
		 			    	cumulative_hght+=45; //separate more to make room for text
		 			    else
		 			    	cumulative_hght+=25;
		 			     }
	    		
		    	}	
		    	
	    	
	    
	    }
	    popMatrix();
	    
	    if(process_color_buffer){
	    	color_buffer.popMatrix();
	    	
    	}
	}
	
	void setAllUnselected(){ //reset selected id and twiddle auto-lerps
		tree_selected_id=-1;
		  prev_height_AL.setTarget(MAX_OBJ_HEIGHT, auto_lerp_time);
		  forest_alpha_AL.reset(forest_alpha);
		  forest_alpha_AL.setTarget(full_alpha, auto_lerp_time); //fade from faded to full
		  
		  highlight_alpha_AL.setTarget(full_alpha, auto_lerp_time);	 
		  prev_highlight_alpha_AL.setTarget(full_alpha, auto_lerp_time);
	}
	
	void selectRandomTree(){
		setAllUnselected();
		int idx = (int) Math.floor(random(0,current_trees.size()));
//		current_trees.get(idx).grid_selected = true;
		tree_selected_id = idx;
		println("Random Selection: Tree # "+idx);
	}
	void selectTreeByID(int id){
		
		if(id < current_trees.size()){
		
			
			if(tree_selected_id > 0){ 
				previous_tree_selected_id = new Integer(tree_selected_id).intValue(); //save it
//				println("prev id is "+previous_tree_selected_id);
			}
//			setAllUnselected();
			tree_selected_id = id;
			println("Selected Tree # "+id);
			 height_AL.reset(MAX_OBJ_HEIGHT);
			  height_AL.setTarget(MAX_OBJ_HEIGHT_SEL, auto_lerp_time);
			  
			  prev_height_AL.reset(MAX_OBJ_HEIGHT_SEL);
			  prev_height_AL.setTarget(MAX_OBJ_HEIGHT, auto_lerp_time);
			  
			  
			  highlight_alpha_AL.reset(forest_alpha);
			  highlight_alpha_AL.setTarget(full_alpha, auto_lerp_time);
			  
			  prev_highlight_alpha_AL.reset(full_alpha); //fade the old highlighted one into the bg with the rest of the trees
			  prev_highlight_alpha_AL.setTarget(faded_alpha, auto_lerp_time);
			  
			  forest_alpha_AL.reset(forest_alpha); //fade the forest into the bg if needed
			  forest_alpha_AL.setTarget(faded_alpha, auto_lerp_time);
			
		}
	}

	//change max cols and recalculate # of rows. This affects how the grid looks
	void adjustRows(){
		
		double last_cell = current_trees.get(current_trees.size()-1).grid_cell_number; // get id of the last cell we need
//		println("last_cell is "+last_cell);
		ROWS =  (int) Math.ceil((float) (((double)last_cell+1) / MAX_COLS));
//		println(" MAX_COLS="+MAX_COLS+" so we need "+ROWS+" rows");
	
	}
	void decrementMaxCols(){
		MAX_COLS-=1;
		  if(MAX_COLS <=0)
			  MAX_COLS=1;
		   adjustRows();
	
	}
	
	void incrementMaxCols(){
		 MAX_COLS+=1;
		  if(MAX_COLS >current_trees.size())
			  MAX_COLS=current_trees.size();
		  
	 adjustRows();
		
	}
	public void keyPressed()
	{
	  switch(key)
	  {
	  case 'l':
		  initRandomSliceColors();
		  break;
	  case 'k':
		  initHSBSliceColors();
		  break;
		case 'q':
		case 'Q': // take a single screenshot
			// Each file from same program run will have same prefix, followed
			// by frame number
			noLoop();
			process_color_buffer = true; //ensure up to date color buffer
			redraw();
			String filename = screenshotspath + initTimeStamp + "-"
					+ frameCount + ".png";
			
			
			String filename2 = screenshotspath + initTimeStamp + "-"
			+ frameCount + "-cb.png";
			if(color_buffer!=null)
				color_buffer.save(filename2);
			System.out.println("Saved Screenshot to " + filename);
			saveFrame(filename);
			loop();
			break;
		case '/':
			draw_all_months = !draw_all_months;
			if(!draw_all_months)
				changeMonth(single_month_index);
			else //save which month we were on 
				single_month_index = current_month_index;
			break;
			
		case 'u':
			show_fps = ! show_fps;
			println("show_fps = "+show_fps);
		break;
	    //translate lights
//	  case 'a':
//	    lightX += 5;
//	    break;
//	  case 'z':
//	    lightX -= 5;
//	    break;
//	  case 's':
//	    lightY -= 5;
//	    break;
//	  case 'x':
//	    lightY += 5;
//	    break;
//	  case 'd':
//	    lightZ += 5;
//	    break;
//	  case 'c':
//	    lightZ -= 5;
//	    break;

	    //rotate object
//	  case '1':
//	    rotObjX -= 1f;
//	    break;
////	  case 'q':
////	    rotObjX += 1f;
////	    break;
//	  case '2':
//	    rotObjY -= 1f;
//	    break;
//	  case 'w':
//	    rotObjY += 1f;
//	    break;
//	  case '3':
//	    rotObjZ -= 1f;
//	    break;
	  case 'e':
		  decrementMaxCols();
	    break;
	  case 'r':
		  incrementMaxCols();
		 
	    break;
	  case 'g':
		  min_grid = ! min_grid;
		  println("Min grid = "+min_grid);
		  break;
	  case 'h':
		  simple_grid = ! simple_grid;
		  break;
	  case 'n':
		  noisy_grid = ! noisy_grid;
		  break;
	  case 'z':
		  relative_heights = ! relative_heights;
		  break;
	  case 'j':
		  draw_axes = ! draw_axes;
		  break;

	  case '[':
		  decrementMonth();
		  break;
	  case ']':
		  incrementMonth();
		  break;
		  
	    //rotate camera
	  case '8':
	    rotCamX -= 1f;
	    break;
	  case 'i':
	    rotCamX += 1f;
	    break;
	  case '9':
	    rotCamY -= 1f;
	    break;
	  case 'o':
	    rotCamY += 1f;
	    break;
	  case '0':
	    rotCamZ -= 1f;
	    break;
	  case 'p':
	    rotCamZ += 1f;
	    break;

	  case 'c':
		  connect_the_slices = !connect_the_slices;
		  break;
	  case 'C':
		  connect_by_week = !connect_by_week;
		  break;
	    //translate camera
	  case 'x':
		  draw_trees = !draw_trees;
		  break;
	  case 'a':
	    camX -= 25f;
	    break;
	  case 'd':
	    camX += 25f;
	    break;
	  case 'w':
	    camY -= 25f;
	    break;
	  case 's':
	    camY += 25f;
	    break;
	  case 't':
		  println("CameraXYZ: "+camX+", "+camY+", "+camZ+" RotObj: "+rotObjX+", "+rotObjY+", "+rotObjZ);
	    break;
//	  case 'k':
//		  number_the_grid = !number_the_grid;
//		  break;
	  case '=':
	    camZ -= 75f;
	    break;
	  case ',':
	  case '-':
	    camZ += 75f;
	    break;

	    //reset
	  case ' ':
		 // selectRandomTree();
		  run_anim = ! run_anim;
		  if(run_anim)
			  loop();
		  else
			  noLoop();
	   // reset();
	    break;

	  }
	}

//	public void mousePressed(){
//		println("Mouse: "+mouseX+" "+mouseY+" ");
//		process_highlight = true; //set flag
//		float[] lastXY = {mouseX,mouseY};
//	
//		lastScreenXY = lastXY;
//		//rect(modelX(mouseX,mouseY,0),modelY(mouseX,mouseY,0),30,30) ;
//		
//	}

	//Use mouse Click events to select objects using color-buffers
	//Based on code at http://processing.org/hacks/hacks:picking_color_buffer
	//Draw scene in off-screen buffer with each tree as a uniqe solid color, 
	//see what color is under mouse cursor, use that to get object id
	public void mouseClicked() {
		println("Mouse: "+mouseX+" "+mouseY+" ");
		process_color_buffer = true; //set flag to process during draw()
	
		lastScreenXY[0] = mouseX;
		lastScreenXY[1] = mouseY;	
		  
		}
		 
//		// id 0 gives color -2, etc.
//		int getColorForID(int id) {
//		  return -(id + 2);
//		}
//		 
//		// color -2 gives 0, etc.
//		int getIDForColor(int buffer_color) {
//		  return -(buffer_color + 2);
//		}
		
	
		int getColorForID(int id) {
//			println("getColorFor "+id);
			Integer color = (Integer) colors_by_id.get(id);
//			println("id "+id+" -> color "+new Color(color));
			if(color != null){
				
			  return color.intValue();
			}else{
//				System.err.println("Color not found for id="+id);
				return -1;
			}
		}
			 
			// color -2 gives 0, etc.
			int getIDForColor(int buffer_color) {
				Integer id= colors_by_id.indexOf(new Integer(buffer_color));
				println("color "+new Color(buffer_color)+" is at index "+id);
				return id.intValue();
			}

	public void mouseDragged() 
	{
	  float rate = 1f;
	  rotObjX += (pmouseY-mouseY) * rate;
	  rotObjZ += (mouseX-pmouseX) * rate;
	}

	void reset()
	{ 
	  lightX = width/2f + 30f;
	  lightY = height/2f + 30f;
	  lightZ = 400f;
	  rotObjX = 0f;
	  rotObjY = -30f;
	  rotObjZ = 0f;
	  rotCamX = 0f;
	  rotCamY = 0f;
	  rotCamZ = 0f;
	  camX = 0f;
	  camY = 0f;
	  camZ = 0f;

	}

//Draw cylindrical things... Based on processing example 
	//base is parallel to x-y plane andthe height is up in the z direction
	
	void drawCylinder(float topRadius, float bottomRadius, float how_tall, int sides) {
		  pushMatrix();
			float angle = 0;
		  float angleIncrement = TWO_PI / sides;
		  beginShape(QUAD_STRIP);
		  for (int i = 0; i < sides + 1; ++i) {
		    vertex(topRadius*cos(angle), topRadius*sin(angle),0);
		    vertex(bottomRadius*cos(angle), bottomRadius*sin(angle), how_tall);
		    angle += angleIncrement;
		  }
		  endShape();
		  
		  // If it is not a cone, draw the circular top cap
		  if (topRadius != 0) {
		    angle = 0;
		    beginShape(TRIANGLE_FAN);
		    
		    // Center point
		    vertex(0, 0, 0);
		    for (int i = 0; i < sides + 1; i++) {
		      vertex(topRadius * cos(angle), topRadius * sin(angle), 0);
		      angle += angleIncrement;
		    }
		    endShape();
		  }

		  // If it is not a cone, draw the circular bottom cap
		  if (bottomRadius != 0) {
		    angle = 0;
		    beginShape(TRIANGLE_FAN);

		    // Center point
		    vertex(0, 0, how_tall);
		    for (int i = 0; i < sides + 1; i++) {
		      vertex(bottomRadius * cos(angle), bottomRadius * sin(angle), how_tall);
		      angle += angleIncrement;
		    }
		    endShape();
		  }
		 
		  popMatrix();
		}
	
	//Draw cylinder on main canvas 
	void drawCylinder(float center_x, float center_y,float base_z,float topRadius, float bottomRadius, float how_tall, int sides) {
		drawCylinder(this.g, center_x, center_y,base_z,topRadius, bottomRadius, how_tall,  sides);
	}
	//draw cylinder on arbitrary PGraphics
	void drawCylinder(PGraphics g, float center_x, float center_y,float base_z,float topRadius, float bottomRadius, float how_tall, int sides) {
	
		  g.pushMatrix();
		  g.translate(center_x,center_y, base_z);
			float angle = 0;
		  float angleIncrement = TWO_PI / sides;
		  g.beginShape(QUAD_STRIP);
		  for (int i = 0; i < sides + 1; ++i) {
		    g.vertex(topRadius*cos(angle), topRadius*sin(angle),0);
		    g.vertex(bottomRadius*cos(angle), bottomRadius*sin(angle), how_tall);
		    angle += angleIncrement;
		  }
		  g.endShape();
		  
		  // If it is not a cone, draw the circular top cap
		  if (topRadius != 0) {
		    angle = 0;
		    g.beginShape(TRIANGLE_FAN);
		    
		    // Center point
		    g. vertex(0, 0, 0);
		    for (int i = 0; i < sides + 1; i++) {
		    	g. vertex(topRadius * cos(angle), topRadius * sin(angle), 0);
		      angle += angleIncrement;
		    }
		    g.endShape();
		  }

		  // If it is not a cone, draw the circular bottom cap
		  if (bottomRadius != 0) {
		    angle = 0;
		    g.beginShape(TRIANGLE_FAN);

		    // Center point
		    g. vertex(0, 0, how_tall);
		    for (int i = 0; i < sides + 1; i++) {
		    	g. vertex(bottomRadius * cos(angle), bottomRadius * sin(angle), how_tall);
		      angle += angleIncrement;
		    }
		    g.endShape();
		  }
		 
		  g.popMatrix();
		}
	
	//Run as Java Application to put it on a second monitor if desired
	//Run this as an application to allow us to manipulate the frame and thus get configurable fullscreen
	  public static void main(String args[]) {
		  boolean use_projector = false; //put PApplet on 2nd display if we have one!
		  int primary_display = 0; //index into Graphic Devices array...  
		 //Position onto projector/2nd display
		 
		  int seconday_width = 1280; 
		  int secondary_height = 1024;
		  
		  int primary_width = 1680; //default values, my laptop
		  int primary_height = 1050;
		  
		  GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
      GraphicsDevice devices[] = environment.getScreenDevices();
     //  System.out.println(Arrays.toString(devices));
       
     //Figure out if there is a 2nd display. 
		  //If there is, try to position screen on
      String location;
       if(devices.length>1 && use_projector){ //we have a 2nd display/projector
    	   //learn the true dimensions of the primary display
    	   primary_width = devices[0].getDisplayMode().getWidth();
    	   
    	  
    	   location = "--location="+primary_width+",00";
    	   System.out.println("Using 2nd display, anim putting window at "+primary_width+",0");
       }else{//
    	   location = "--location=0,0";
    	   System.out.println("Using primary display, putting window at 0,0");
       }
		  
		  String display = "--display="+primary_display+1;  //processing considers the first display to be # 1
		    PApplet.main(new String[] { location ,"--present", "--hide-stop", display,"DeweyForest" });// add "--present", for default present mode
		    
		  }
} //End DeweyForest

