import processing.core.*; 
import processing.data.*; 
import processing.opengl.*; 

import processing.net.*; 
import omicronAPI.*; 

import java.applet.*; 
import java.awt.Dimension; 
import java.awt.Frame; 
import java.awt.event.MouseEvent; 
import java.awt.event.KeyEvent; 
import java.awt.event.FocusEvent; 
import java.awt.Image; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 
import java.util.regex.*; 

public class Shi424P1 extends PApplet {

////////////////////////////////////////////////////////////////////////// 



OmicronAPI omicronManager;
TouchListener touchListener;

// Link to this Processing applet - used for touchDown() callback example
PApplet applet;

// Override of PApplet init() which is called before setup()
public void init() {
  super.init();

  // Creates the OmicronAPI object. This is placed in init() since we want to use fullscreen
  omicronManager = new OmicronAPI(this);

  // Removes the title bar for full screen mode (present mode will not work on Cyber-commons wall)
  //omicronManager.setFullscreen(true);
}
///////////////////////////////////////////////////
UserInterface ui;

//resolution
//final int scale = 5;
final int scale = 1;
// 5 for cyber-commons
// 1 for full screen macbook

final int Width = 1632;
final int Height = 461;

//final int Width = 1440;
//final int Height = 460;

//final int Width = 8160;
//final int Height = 2304;

//ArrayList li = new ArrayList();

//IndexedList inli = new IndexedList();

//DropBox dropBox;

//Range range;

float dataMin;
float[] dataMax;
float[] originalDataMax;

int[] currentRow = {
  6, 6, 6, 6, 6, 6, 6
};
boolean[] currentDisplay = {
  true, false, false, false, false, false, false
};

int yearInterval = 10;

Integrator[] interpolators;

int i = 0;

float[] spacing = new float[2];

Legend[] legend;

boolean isDragLeft = false;
boolean isDragRight = false;

boolean isDrag = false;
float dataScale = 1;
float dragStartY;

// which Volume at Y axis is being displayed
int displayVolume = 0;

String[] unitText;
///////////////////////////////////
//
//
//                  SETUP
//
//
///////////////////////////////////
public void setup()
{
  //size(Width*scale, Height*scale);
  size(Width, Height);
  //Make the connection to the tracker machine (uncomment this if testing with touch)
  //omicronManager.ConnectToTracker(7001, 7340, "131.193.77.159");

  // Create a listener to get events
  touchListener = new TouchListener();

  // Register listener with OmicronAPI
  omicronManager.setTouchListener(touchListener);

  // Sets applet to this sketch
  applet = this;


  //load in data sets
  setupData(); 

  /*
  interpolators = new Integrator[columnCount];
   
   for (int column = 0; column < columnCount; column++) {
   float initialValue = data.getFloat(0, column);
   interpolators[column] = new Integrator(initialValue);
   interpolators[column].attraction = 0.1;
   }*/

  // setup Font
  plotFont = createFont("Verdana", 10*scale);
  textFont(plotFont);

  // setup Colors
  colors = new int[] {
    COLOR_0, COLOR_1, COLOR_2, COLOR_3, COLOR_4, COLOR_5, COLOR_6
  };

  defaultColor = new int[][] {
    {
      colors[0], DEFAULT_PIE_COLOR
    }
    , {
      colors[1], DEFAULT_PIE_COLOR
    }
    , {
      colors[2], color(100)
    }
    , {
      colors[3], DEFAULT_PIE_COLOR
    }
    , {
      colors[4], color(100)
    }
    , {
      colors[5], DEFAULT_PIE_COLOR
    }
    , {
      colors[6], DEFAULT_PIE_COLOR
    }
  };

  for (int i=0;i<DATA_SET_COUNT;i++) {
    defaultColor[i][0] = colors[i];
    defaultColor[i][1] = DEFAULT_PIE_COLOR;
  }

  unitText = new String[] {
    "(Quads)", "(Quads)", "(Quads/C)", "(MMT)", "(MT/C)", "(BkWh)", "(Millions)"
  };


  setupPlotAndTable();

  YEAR_SELECTOR_X = plotX1 + (plotX2 - plotX1) * 0.2f;

  ui = new UserInterface();

  //used by dashline
  setupDashLine(DASH_LINE_SPACING1, DASH_LINE_SPACING2);

  //dropBox= new DropBox(1, 1, 120, 10, li, inli, 0);
  //dropBox= new DropBox(1, 1, 120);

  legend = new Legend[DATA_SET_COUNT];
  for (int i=0;i<DATA_SET_COUNT;i++) {
    legend[i] = new Legend(PAGE_BTN_X+PAGE_BTN_W+LEGEND_DIFF, LIST_Y + (i - 1)*2.1f*LIST_LINE_H + 12*scale, LIST_LINE_H, ui.dataSelector[i].getTxt()+"\n"+unitText[i], colors[i]);
  }

  // setup Pie Charts
  pieChart = new PieChart[DATA_SET_COUNT];
  for (int i=0;i<DATA_SET_COUNT;i++) {
    float[] data = new float[2];
    data[0] = dataSets[i].getData().getFloat(currentRow[0], ui.getPieButton().getYear()-yearMin);
    data[1] = dataSets[i].getData().getFloat(getRegion(currentRow[0]), ui.getPieButton().getYear()-yearMin);

    pieChart[i] = new PieChart(data, 2, defaultColor[i], PIE_CHART_X, PIE_CHART_Y+ i * PIE_CHART_DIS, PIE_CHART_R, i);
  }
  pieChart[0].setDisplay();

  smooth();
} // end of setup


///////////////////////////////////
//
//
//                  DRAW
//
//
///////////////////////////////////
public void draw() {
  background(224);

  if (ui.getShowTable() == false) {
    //draw PLOT and Bar
    drawPlotAndBar();
  }
  else {
    ui.getTable().render();
  }
  //draw Legend
    for (int i=0;i<DATA_SET_COUNT;i++) {
      legend[i].render();
    }

    //draw pie Title
    drawPieTitle();
    //draw Pie Charts
    drawPieCharts();

//draw Title
    if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {
      drawTitle(countryNames[currentRow[0]]);
    }
    if (ui.getDisplayMode() == MULTI_COUNTRY_MODE) {
      int i=0;
      while (i<DATA_SET_COUNT && ui.getDataSelector(i).getCheck() == false) i++;
      if (i<DATA_SET_COUNT)
      {
        drawTitle(ui.getDataSelector(i).getTxt()+ " " + unitText[i]);
      }
    }

//draw User Interface
ui.render();

omicronManager.process();
}

public void drawTitle(String st) {
  pushStyle();
  fill(0);
  textSize(12*scale);
  textAlign(LEFT, TOP);
  if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {
    if (ui.getYearMode() == YEAR_MODE) {
      text("One Country Mode,   Year by year", SEARCH_BUTTON_LEN * 0.2f, 10*scale);
    }
    if (ui.getYearMode() == DECADE_MODE) {
      text("One Country Mode,   Decades average", SEARCH_BUTTON_LEN * 0.2f, 10*scale);
    }
  }
  if (ui.getDisplayMode() == MULTI_COUNTRY_MODE) {
    if (ui.getYearMode() == YEAR_MODE) {
      text("Multi Countries Compare,   Year by year", SEARCH_BUTTON_LEN * 0.2f, 10*scale);
    }
    if (ui.getYearMode() == DECADE_MODE) {
      text("Multi Countries Compare,   Decades average", SEARCH_BUTTON_LEN * 0.2f, 10*scale);
    }
  }
  textSize(24*scale);
  text(st, SEARCH_BUTTON_LEN * 0.5f, 30*scale);
  popStyle();
}

///////////////////
///////////////////
/////////////////////////////////////////////////////////
///////////////////
///////////////////      SETUP_DATA
///////////////////
/////////////////////////////////////////////////////////
///////////////////
///////////////////
public void setupData()
{
  
  dataMax = new float[DATA_SET_COUNT];
  originalDataMax = new float[DATA_SET_COUNT];
  
  dataSets = new DataSets[DATA_SET_COUNT];

  dataSets[0] = new DataSets("Total_Primary_Energy_Production.txt");
  dataSets[1] = new DataSets("Total_Primary_Energy_Consumption.txt");
  dataSets[2] = new DataSets("Total_Primary_Energy_Consumption_Per_Capita.txt");
  dataSets[3] = new DataSets("Total_CO2_Emission.txt");
  dataSets[4] = new DataSets("Total_CO2_Per_Capita.txt");
  dataSets[5] = new DataSets("Total_Renewable_Electricity_Net_Generation.txt");
  dataSets[6] = new DataSets("Population.txt");
  
  dataSets[0].setInterval(500);
  dataSets[1].setInterval(500);
  dataSets[2].setInterval(500);
  dataSets[3].setInterval(500);
  dataSets[4].setInterval(500);
  dataSets[5].setInterval(500);
  dataSets[6].setInterval(500);

  rowCount = dataSets[0].getData().getRowCount();
  columnCount = dataSets[0].getData().getColumnCount();

  years = PApplet.parseInt(dataSets[0].getData().getColumnNames());

  yearMin = years[0];
  yearMax = years[years.length-1];

  showYearMin = yearMin;
  showYearMax = yearMax;

  countryNames = dataSets[0].getData().getRowNames();
}

public void setupPlotAndTable()
{
  plotX1 = 510*scale;
  plotX2 = Width - 220*scale;
  
  TABLE_WINDOW_X = plotX1-30*scale;
  TABLE_PAGE_BTN_X = plotX2-TABLE_PAGE_BTN_H*0.5f-TABLE_PAGE_BTN_DIFF;

  plotY1 = 45*scale;
  plotY2 = Height - 120*scale;

  labelX = plotX1 - 35*scale;

  tableY1 = plotY2+20*scale;
  tableY2 = (Height - 60)*scale;

  yearCount = showYearMax-showYearMin+1;

  unitWidth = (plotX2-plotX1)/yearCount;

  displayNumber = DATA_SET_COUNT;

  unitHeight = (tableY2-tableY1)/displayNumber;

  dataMin = 0;
  for (int i=0;i<DATA_SET_COUNT;i++) {
    dataMax[i] = ceil(dataSets[i].getData().getTableMax()/dataSets[i].getInterval())*dataSets[i].getInterval();
    originalDataMax[i] = dataMax[i];
  }

  //setupDecade();
}


public void setupDashLine(float sp1, float sp2) {
 spacing[0] = sp1;
 spacing[1] = sp2;
 }

public void drawPlotAndBar()
{
  yearCount = showYearMax-showYearMin+1;
  unitWidth = (plotX2-plotX1)/yearCount;
  if (ui.getYearMode() == YEAR_MODE) {
    drawPlot();
  }
  else if (ui.getYearMode() == DECADE_MODE) {
    drawBar();
  }
  //drawTabular(yearCount, displayNumber);
}

public void drawPlot() {
  pushStyle();
  fill(245);
  rectMode(CORNERS);
  noStroke();
  rect(plotX1, plotY1, plotX2, plotY2);

  //drawAxisLabels();
  /*
  for (int column = 0; column < columnCount; column++) {
   interpolators[column].update();
   }*/

  drawYearLabels();

  noFill();
  strokeWeight(2*scale);

//////
//
//
//
//
//
//  volumeeeeee
//
//
//
///////
  //multi countries
  if (ui.getDisplayMode() == MULTI_COUNTRY_MODE) {
    for (int i = 0; i < DATA_SET_COUNT; i ++) {
      if (ui.dataSelector[i].getCheck() == true) { 
        drawVolumeLabels(i);
        break;
      }
    }
  }
  
  //one country, many data sets
  if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {
    int cnt = 0;
    for (int i=0;i < DATA_SET_COUNT;i++) {
      if (ui.dataSelector[i].getCheck() == true) {
        cnt++;
      }
    }
    // only 1 data set is being displayed
    if (cnt == 1) {
      for (int i=0;i<DATA_SET_COUNT;i++) {
        if (ui.dataSelector[i].getCheck() == true) {
          drawVolumeLabels(i);
          break;
        }
      }
    }
    else {
      drawVolumeLabels(displayVolume);
    }
  }    
  
  
  
 //////////END OF VOLUME

  if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {
    for (int i = 0; i < DATA_SET_COUNT; i ++) {
      if (ui.dataSelector[i].getCheck() == true) {
        drawDataCurve(currentRow[0], i, i, showYearMin, showYearMax);
        
        
        // special cases
        
        if (isNansilafu(currentRow[0])) {
          //slavia
          if (currentRow[0] == 67) {
            drawDataCurveDashSpecial(new int[]{79,89,57,59,66},i,i,showYearMin,showYearMax,1992,2005);
            drawDataCurveDashSpecial(new int[]{79,89,57,59,92,86},i,i,showYearMin,showYearMax,2006,2009);
          }
          else {
            drawDataCurveDash(67,i,i,showYearMin,showYearMax,1980,1991);
            if (currentRow[0] == 66) {
             drawDataCurveDashSpecial(new int[]{92,86},i,i,showYearMin,showYearMax,2006,2009);
            }
            else if (currentRow[0] == 92 || currentRow[0] == 86) {
                drawDataCurveDash(66,i,i,showYearMin,showYearMax,1992,2005);
            }
          }
        }
        else if (isGermany(currentRow[0])) {
          // Germany
          if (currentRow[0] == 69) {
            drawDataCurveDashSpecial(new int[]{70,71},i,i,showYearMin,showYearMax,1980,1990);
          }
          // East or West
          else {
            drawDataCurveDash(69,i,i,showYearMin,showYearMax,1991,2009);
          }
        }
        else if (isCzechSlovakia(currentRow[0])) {
          // Czechoslovakia
          if (currentRow[0] == 65) {
            drawDataCurveDashSpecial(new int[]{61,88},i,i,showYearMin,showYearMax,1993,2009);
          }
          // Czech Slovakia
          else {
            drawDataCurveDash(65, i,i,showYearMin,showYearMax, 1980,1992);
          }
          
        }
        else if (isUSSR(currentRow[0])) {
          // USSR
          if (currentRow[0] == 100) {
            drawDataCurveDash(95, i,i, showYearMin,showYearMax, 1992, 2009);
          }
          // new countries
          else {
            drawDataCurveDash(100,i,i,showYearMin,showYearMax, 1980, 1991);
          }
        }
      }
    }
  }

  if (ui.getDisplayMode() == MULTI_COUNTRY_MODE) {
    for (int i = 0; i < DATA_SET_COUNT; i++) {
      if (ui.dataSelector[i].getCheck() == true) {
        for (int j=0;j < DATA_SET_COUNT; j++) {
          if (currentDisplay[j] == true) {
            drawDataCurve(currentRow[j], j, i, showYearMin, showYearMax);
          }
        }
      }
    }
  }

  //drawDataHighlight(currentRow, i);
  //drawDataHighlight(currentRow2);
  popStyle();
}

public void drawBar() {
  pushStyle();
  fill(245);
  rectMode(CORNERS);
  noStroke();
  rect(plotX1, plotY1, plotX2, plotY2);

  fill(0);
  textSize(10*scale);
  textAlign(CENTER);

  // Use thin, gray lines to draw the grid
  stroke(0);
  strokeWeight(scale);
  float left = plotX1;
  float right = left + (plotX2-plotX1)/3.0f;
  for (int column = 0; column < 30; column+=10) {
    float x = (left+right)/2;
    text(years[column] + " - " + years[column+9], x, plotY2 + textAscent() + 5*scale);
    if (column + 10 < yearMax) {
      line(right, plotY2, right, plotY2+3*scale);
    }
    left = right;
    right += (plotX2-plotX1)*0.33f;
  }

  noFill();
  strokeWeight(2*scale);

  if (ui.getDisplayMode() == MULTI_COUNTRY_MODE) {
    for (int i = 0; i < DATA_SET_COUNT; i ++) {
      if (ui.dataSelector[i].getCheck() == true) { 
        drawVolumeLabels(i);
        break;
      }
    }
  }
  
  //one country, many data sets
  if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {
    int cnt = 0;
    for (int i=0;i < DATA_SET_COUNT;i++) {
      if (ui.dataSelector[i].getCheck() == true) {
        cnt++;
      }
    }
    // only 1 data set is being displayed
    if (cnt == 1) {
      for (int i=0;i<DATA_SET_COUNT;i++) {
        if (ui.dataSelector[i].getCheck() == true) {
          drawVolumeLabels(i);
          break;
        }
      }
    }
    else {
      drawVolumeLabels(displayVolume);
    }
  }

  if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {
    for (int i = 0; i < DATA_SET_COUNT; i ++) {
      if (ui.dataSelector[i].getCheck() == true) {
        //drawDataCurve(currentRow[0], i, showYearMin, showYearMax);
        drawDataBar(currentRow[0], i, i);
      }
    }
  }

  if (ui.getDisplayMode() == MULTI_COUNTRY_MODE) {
    for (int i = 0; i < DATA_SET_COUNT; i++) {
      if (ui.dataSelector[i].getCheck() == true) {
        for (int j=0;j < DATA_SET_COUNT; j++) {
          if (currentDisplay[j] == true) {
            //drawDataCurve(currentRow[j], j, showYearMin, showYearMax);
            drawDataBar(currentRow[j], i, j);
          }
        }
        break;
      }
    }
  }
  popStyle();
}

public void drawAxisLabels() {
  pushStyle();
  fill(0);
  textSize(13*scale);
  textLeading(15);

  textAlign(RIGHT, CENTER);
  text("What\nthe\nX", labelX, (plotY1+plotY2)/2);
  textAlign(CENTER);
  //text("What the Y", (plotX1+plotX2)/2, labelY);
  popStyle();
}

public void drawYearLabels() {
  pushStyle();

  fill(0);
  textSize(10*scale);
  textAlign(CENTER);

  // Use thin, gray lines to draw the grid
  stroke(0);
  strokeWeight(scale);

  float left = plotX1;
  float right = left+unitWidth;

  for (int column = showYearMin-yearMin; column < showYearMax-yearMin+1; column++) {
    float x = (left+right)/2;
    if (showYearMax-showYearMin+1 <=15)
    {
      text(years[column], x, plotY2 + textAscent() + 5*scale);
    }
    else if (column%2 == 0) {
      text(years[column], x, plotY2 + textAscent() + 5*scale);
    }
    line(x, plotY2, x, plotY2+3*scale);
    left = right;
    right += unitWidth;
  }

  popStyle();
}


public void drawVolumeLabels(int i) {

  pushStyle();
  fill(0);
  textSize(10*scale);
  textAlign(RIGHT);

  stroke(224);
  strokeWeight(1);

  //for (float v = dataMin; v <= dataMax[i]; v += volumeInterval[i]) {
  for (float v = dataMin; v < dataMax[i]*dataScale; v += (dataMax[i]*dataScale-dataMin)/15) {

    float y = map(v, dataMin, dataMax[i]*dataScale, plotY2, plotY1);  
    float textOffset = textAscent()/2;  // Center vertically
    if (v == dataMin) {
      textOffset = 0;                   // Align by the bottom
    } 
    else if (v == dataMax[i]) {
      textOffset = textAscent();        // Align by the top
    }
    text(round(v), plotX1 - 10, y + textOffset);
    line(plotX1, y, plotX2, y);     // Draw major tick
  }
  popStyle();
}

public void drawDataCurve(int row, int colour, int i, int yMin, int yMax) {
  pushStyle();

  noFill();
  stroke(colors[colour]);

  beginShape();
  float left = plotX1;
  float right = left+unitWidth;

  for (int col = showYearMin-yearMin; col < showYearMax-yearMin+1; col++) {

    if (dataSets[i].getData().isValid(row, col)) {
      float value = dataSets[i].getData().getFloat(row, col);
      float x = (left+right)/2;
      float y = map(value, dataMin, dataMax[i]*dataScale, plotY2, plotY1);
      ellipse(x, y, 3*scale, 4*scale);

      vertex(x, y);
      // double the curve points for the start and stop
      /*if (starting == false || col == columnCount-1) {
       starting = true; 
       curveVertex(x, y);
       }*/
    }
    left = right;
    right += unitWidth;
  }
  endShape();

  popStyle();
}

public void drawDataBar(int row, int table, int col) {
  pushStyle();

  fill(colors[col]);
  //noFill();
  noStroke();
  int left = 0;
  int right = 10;
  float amount = 0;
  boolean isValid = true;
  float barWidth = 18*scale;

  // 11111111
  for (int i=left;i<right;i++) {
    if (dataSets[table].getData().isValid(row, i)) {
      amount += dataSets[table].getData().getFloat(row, i);
    } 
    else
    {
      isValid = false;
      break;
    }
  }
  if (isValid) {
    rectMode(CORNERS);
    amount = amount * 0.1f;
    float xx = plotX1 + (1+left*0.2f)/6.0f*(plotX2-plotX1) - barWidth*(3-col);
    float yy = map(amount, dataMin, dataMax[table]*dataScale, plotY2, plotY1);
    //println("table:" + table + ",,,  " + xx + "  " + yy);
    rect(xx, yy, xx+barWidth, plotY2);
    /*
    
     stroke(0);
     line(plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) - barWidth, yy, plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) - barWidth, plotY2);
     line(plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) + barWidth, yy, plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) + barWidth, plotY2);
     stroke(colors[table]);
     line(plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) - barWidth, yy, plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) + barWidth, yy);*/
  }

  // 2222222
  left += 10;
  right += 10;
  amount = 0;
  isValid = true;
  for (int i=left;i<right;i++) {
    if (dataSets[table].getData().isValid(row, i)) {
      amount += dataSets[table].getData().getFloat(row, i);
    } 
    else
    {
      isValid = false;
      break;
    }
  }

  if (isValid) {
    rectMode(CORNERS);
    amount = amount * 0.1f;
    //rect(plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) - barWidth, map(amount, dataMin, dataMax[table], plotY2, plotY1), plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) + barWidth, plotY2);
    float xx = plotX1 + (1+left*0.2f)/6.0f*(plotX2-plotX1) - barWidth*(3-col);
    float yy = map(amount, dataMin, dataMax[table]*dataScale, plotY2, plotY1);
    rect(xx, yy, xx+barWidth, plotY2);
    /*
    stroke(0);
     line(plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) - barWidth, yy, plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) - barWidth, plotY2);
     line(plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) + barWidth, yy, plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) + barWidth, plotY2);
     stroke(colors[table]);
     line(plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) - barWidth, yy, plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) + barWidth, yy);*/
  }

  // 3333333
  left += 10;
  right += 10;
  amount = 0;
  isValid = true;
  for (int i=left;i<right;i++) {
    if (dataSets[table].getData().isValid(row, i)) {
      amount += dataSets[table].getData().getFloat(row, i);
    } 
    else
    {
      isValid = false;
      break;
    }
  }

  if (isValid) {
    rectMode(CORNERS);
    amount = amount * 0.1f;
    //rect(plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) - barWidth, map(amount, dataMin, dataMax[table], plotY2, plotY1), plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) + barWidth, plotY2);
    float xx = plotX1 + (1+left*0.2f)/6.0f*(plotX2-plotX1) - barWidth*(3-col);
    float yy = map(amount, dataMin, dataMax[table]*dataScale, plotY2, plotY1);
    rect(xx, yy, xx+barWidth, plotY2);
    /*
    stroke(0);
     line(plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) - barWidth, yy, plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) - barWidth, plotY2);
     line(plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) + barWidth, yy, plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) + barWidth, plotY2);
     stroke(colors[table]);
     line(plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) - barWidth, yy, plotX1 + (1+left*0.2)/6.0*(plotX2-plotX1) + barWidth, yy);*/
  }
}
public void drawDataCurveDash(int row, int colour, int i, int yMin, int yMax, int begin, int end) {
  pushStyle();

  noFill();
  stroke(colors[colour]);

  float[][] dashPoint = new float[columnCount][2];
  
  float left = plotX1;
  float right = left+unitWidth;

  for (int col = showYearMin-yearMin; col < showYearMax-yearMin+1; col++) {
    if (dataSets[i].getData().isValid(row, col) && col>=begin-1980 && col<=end-1980) {
      float value = dataSets[i].getData().getFloat(row, col);
      float x = (left+right)/2;
      float y = map(value, dataMin, dataMax[i]*dataScale, plotY2, plotY1);
      dashPoint[col][0] = x;
      dashPoint[col][1] = y;
      ellipse(x, y, 3*scale, 4*scale);
    }
    else {
      dashPoint[col][0] = -1000;
      dashPoint[col][1] = -1000;
    }
    
    left = right;
    right += unitWidth;
  }

  for (int col = showYearMin-yearMin+1; col < showYearMax-yearMin+1; col++) {
    if (dashPoint[col-1][0] != -1000 && dashPoint[col][0] != -1000)
      dashline(dashPoint[col-1][0], dashPoint[col-1][1], dashPoint[col][0], dashPoint[col][1], spacing);
  }

  popStyle();  
}

public void drawDataCurveDashSpecial(int[] row, int colour, int i, int yMin, int yMax, int begin, int end) {
  pushStyle();

  noFill();
  stroke(colors[colour]);

  float[][] dashPoint = new float[columnCount][2];
  
  float left = plotX1;
  float right = left+unitWidth;
  
  int ct = row.length;

  for (int col = showYearMin-yearMin; col < showYearMax-yearMin+1; col++) {
    if (col>=begin-1980 && col<=end-1980) {
      float value = 0;
      for (int j=0;j<ct;j++) {
        value += dataSets[i].getData().getFloat(row[j], col);
      }
      float x = (left+right)/2;
      float y = map(value, dataMin, dataMax[i]*dataScale, plotY2, plotY1);
      dashPoint[col][0] = x;
      dashPoint[col][1] = y;
      ellipse(x, y, 3*scale, 4*scale);
    }
    else {
      dashPoint[col][0] = -1000;
      dashPoint[col][1] = -1000;
    }
    
    left = right;
    right += unitWidth;
  }

  for (int col = showYearMin-yearMin+1; col < showYearMax-yearMin+1; col++) {
    if (dashPoint[col-1][0] != -1000 && dashPoint[col][0] != -1000)
      dashline(dashPoint[col-1][0], dashPoint[col-1][1], dashPoint[col][0], dashPoint[col][1], spacing);
  }

  popStyle();  
}

public void drawTabular(int rowC, int lineC) {
  pushStyle();
  fill(245);
  rectMode(CORNERS);
  stroke(0);
  strokeWeight(scale);
  rect(plotX1, tableY1, plotX2, tableY2);

  //draw vertical lines
  float xx = plotX1+unitWidth;

  while (xx<plotX2)
  {
    line(xx, tableY1, xx, tableY2);
    xx+=unitWidth;
  }

  //draw horizontal lines
  float yy = tableY1+unitHeight;

  while (yy<tableY2)
  {
    line(plotX1, yy, plotX2, yy);
    yy+=unitHeight;
  }

  popStyle();
}

public void drawPieCharts() {
  if (ui.getDisplayMode() == ONE_COUNTRY_MODE)
  {
    for (int i=0;i<DATA_SET_COUNT; i++)
    {
      if (pieChart[i].getIsDisplay()) {
        pieChart[i].render();
      }
      // update color and data for pie charts

      // draw pieChart
    }
  }
  else // MULTI_COUNTRY_MODE
  {
    pieChart[0].render();
  }
}

public int getRegion(int i)
{
  if (ui.getPieChartMode() == REGION_MODE)
  {
    if (i<7) {
      return 0;
    } 
    else if (i<53) {
      return 7;
    } 
    else if (i<95) {
      return 53;
    } 
    else if (i<112) {
      return 95;
    } 
    else if (i<127) {
      return 112;
    } 
    else if (i<184) {
      return 127;
    } 
    else if (i<rowCount-1) {
      return 184;
    }
  }
  return rowCount-1;
}

public void drawPieTitle() {
  pushStyle();
  textSize(15*scale);
  textAlign(LEFT, CENTER);
  if (ui.displayMode == ONE_COUNTRY_MODE) {
    if (ui.getPieChartMode() == REGION_MODE) {
      text("Compare to Region", Width - 200*scale, 15*scale);
    }
    else {
      text("Compare to World", Width - 200*scale, 15*scale);
    }
  }
  else { //MULTI_COUNTRY_MODE
    text("Compare to each other", Width - 200*scale, 15*scale);
  }
  textAlign(RIGHT, CENTER);
  textSize(12*scale);
  text("("+ui.getPieButton().getYear()+")", Width - 10*scale, 30*scale);
  popStyle();
}
class Button {
  
  PVector pos;
  PVector size;
  String txt;
  
  boolean isClick;
  boolean canClick;
  
  Button (float x, float y, float w, float h, String str)
  {
    pos = new PVector(x,y);
    size = new PVector(w,h);
    txt = str;
  }
  
  Button (PVector p, PVector s, String str)
  {
    pos = p;
    size = s;
    txt = str;
  }
  
  public void setIsClick(boolean bb)
  {
    isClick = bb;
  }
  
  public boolean getIsClick()
  {
    return isClick;
  }
  
  public void setCanClick(boolean bb)
  {
    canClick = bb;
  }
  
  public boolean getCanClick()
  {
    return canClick;
  }
  
  public void ClickUnClick()
  {
    if (canClick)
    {
      isClick = !isClick;
    }
    else {println("can't click it");}
  }
  
  
  public void render()
  {
    pushStyle();
    textAlign(LEFT,CENTER);
    fill(0);
    
    text(txt, pos.x+1*scale, pos.y+size.y/2);
    popStyle();
  }
  
  public String getTxt() {
    return txt;
  }
}
    
class CheckBox {
  
  PVector pos;
  PVector size;
  
  boolean isCheck;
  boolean canCheck;
  
  CheckBox(float x, float y, float w, float h)
  {
    pos = new PVector(x,y);
    size = new PVector(w,h);
  }
  
  public void setIsCheck(boolean bb)
  {
    isCheck = bb;
  }
  
  public boolean getIsCheck()
  {
    return isCheck;
  }
  
  public void setCanCheck(boolean bb)
  {
    canCheck = bb;
  }
  
  public boolean getCanCheck()
  {
    return canCheck;
  }
  
  public void CheckUnCheck()
  {
    if (canCheck)
    {
      isCheck = !isCheck;
    }
    else {println("can't check it");}
  }
  
  public void render()
  {
    pushStyle();
    noFill();
    stroke(127);
    strokeWeight(1*scale);
    rectMode(CORNER);
    rect(pos.x, pos.y, size.x, size.y);
    if (isCheck)
    {
      line(pos.x, pos.y, pos.x+size.x, pos.y+size.y);
      line(pos.x, pos.y+size.y, pos.x+size.x, pos.y);
    }
    popStyle();
  }
  
  public float getW()
  {
    return size.x;
  }
}

class CheckItem {
  
  CheckBox cb;
  
  Button btn;
  
  float w;
  
  float x,y;
  
  float h;
  
  CheckItem(float _x, float _y, float wid1, float wid2, float height, String str, boolean act) {
    cb = new CheckBox(_x, _y, wid1, height);
    btn = new Button(_x+wid1, _y, wid2, height, str);
    x = _x;
    y = _y;
    w = wid1+wid2;
    h = height;
  }
  
  CheckItem(float _x, float _y, float wid1, float wid2, float height, String str) {
    cb = new CheckBox(_x, _y, wid1, height);
    btn = new Button(_x+wid1, _y, wid2, height, str);
    x = _x;
    y = _y;
    w = wid1+wid2;
    h = height;
  }
  
  public void render()
  {
    cb.render();
    btn.render();
  }
  
  public boolean isCheck()
  {
    return cb.getIsCheck();
  }
  
  public boolean canCheck()
  {
    return cb.getCanCheck();
  }
  
  public void setCheck(boolean bb)
  {
    cb.setIsCheck(bb);
  }
  
  public void CheckUnCheck()
  {
    cb.CheckUnCheck();
  }
  
  public void update(float mousex, float mousey)
  {
  }
  
  public float getX()
  {
    return x;
  }
  public float getY()
  {
    return y;
  }
  public float getCBW()
  {
    return cb.getW();
  }
  public float getH()
  {
    return h;
  }
  
  public void setActive()
  {
    cb.setIsCheck(true);
  }
  
  public void setNotActive()
  {
    cb.setIsCheck(false);
  }
  
  public boolean getCheck()
  {
    return cb.getIsCheck();
  }
  
  public String getTxt() {
    return btn.getTxt();
  }
}
  
class CountryList {
  
  float x,y;
  float lineH;
  float w;
  
  String[] item;
  
  int cnt;
  int totalCnt;
  
  int page;
  int pageCount;
  
  float btn_x;
  float btn_y;
  float btn_w;
  float btn_diff;
  
  CountryList(float _x, float _y, float _w, float _h, float bx, float by, float bw, float diff) {
    x = _x;
    y = _y;
    w = _w;
    lineH = _h;
    btn_x = bx;
    btn_y = by;
    btn_w = bw;
    btn_diff = diff;
    
    totalCnt = dataSets[0].getData().getRowCount();
    item = new String[totalCnt];
    
    for (int i=0; i<totalCnt; i++) {
      if (i == 0 || i == 7 || i == 53 || i == 95 || i == 112 || i == 127 || i == 184) {
        item[i] = dataSets[0].getData().getRowName(i).toUpperCase();
      }
      else
      {
        item[i] = "  " + dataSets[0].getData().getRowName(i);
      }
    }
    
    cnt = totalCnt;
    page = 0;
    pageCount = (cnt % HOW_MANY_LINES_IN_A_PAGE == 0)? (cnt / HOW_MANY_LINES_IN_A_PAGE):(cnt/HOW_MANY_LINES_IN_A_PAGE + 1);
  }
  
  public void updateBySearch(String strrr) {
    cnt = 0;
    for (int i=0;i<totalCnt;i++) {
      
      /* if can search a region
      String name = dataSets[0].getData().getRowName(i);
      if (strrr.equalsIgnoreCase(name.substring(0,strrr.length())) == true)
      {
        if (i == 0 || i == 7 || i == 53 || i == 95 || i == 112 || i == 127 || i == 184 || i == totalCnt-1) {
          item[cnt] = name.toUpperCase();
        } else {
          item[cnt] = "  " + name;
        }
        cnt++;
      }
      */
      
      if (i == 0 || i == 7 || i == 53 || i == 95 || i == 112 || i == 127 || i == 184) { continue; }
      if (strrr.length() <= countryNames[i].length())
      {
        if (strrr.equalsIgnoreCase(countryNames[i].substring(0,strrr.length())) == true)
        {
          item[cnt] = "  " + countryNames[i];
          cnt++;
        }
      }
      println("count:"+cnt);
      page = 0;
      pageCount = (cnt % HOW_MANY_LINES_IN_A_PAGE == 0)? (cnt / HOW_MANY_LINES_IN_A_PAGE):(cnt/HOW_MANY_LINES_IN_A_PAGE + 1);
    }
  }
  
  public void updateByMap(int region) {
    switch (region)
    {
      case N_AME:
        item[0] = countryNames[0].toUpperCase();
        cnt = 1;
        for (int i=1;i<7;i++) {
          item[cnt] =  "  " + countryNames[i];
          cnt ++;
        }
        page = 0;
        pageCount = (cnt % HOW_MANY_LINES_IN_A_PAGE == 0)? (cnt / HOW_MANY_LINES_IN_A_PAGE):(cnt/HOW_MANY_LINES_IN_A_PAGE + 1);
        break;
      case S_AME:
        item[0] = countryNames[7].toUpperCase();
        cnt = 1;
        for (int i=8;i<53;i++) {
          item[cnt] =  "  " + countryNames[i];
          cnt ++;
        }
        page = 0;
        pageCount = (cnt % HOW_MANY_LINES_IN_A_PAGE == 0)? (cnt / HOW_MANY_LINES_IN_A_PAGE):(cnt/HOW_MANY_LINES_IN_A_PAGE + 1);
        break;
      case AFRI:
        item[0] = countryNames[127].toUpperCase();
        cnt = 1;
        for (int i=128;i<184;i++) {
          item[cnt] =  "  " + countryNames[i];
          cnt ++;
        }
        page = 0;
        pageCount = (cnt % HOW_MANY_LINES_IN_A_PAGE == 0)? (cnt / HOW_MANY_LINES_IN_A_PAGE):(cnt/HOW_MANY_LINES_IN_A_PAGE + 1);
        break;
      case EURO:
        item[0] = countryNames[53].toUpperCase();
        cnt = 1;
        for (int i=54;i<95;i++) {
          item[cnt] =  "  " + countryNames[i];
          cnt ++;
        }
        page = 0;
        pageCount = (cnt % HOW_MANY_LINES_IN_A_PAGE == 0)? (cnt / HOW_MANY_LINES_IN_A_PAGE):(cnt/HOW_MANY_LINES_IN_A_PAGE + 1);
        break;
      case RUS:
        item[0] = countryNames[95].toUpperCase();
        cnt = 1;
        for (int i=96;i<112;i++) {
          item[cnt] =  "  " + countryNames[i];
          cnt ++;
        }
        page = 0;
        pageCount = (cnt % HOW_MANY_LINES_IN_A_PAGE == 0)? (cnt / HOW_MANY_LINES_IN_A_PAGE):(cnt/HOW_MANY_LINES_IN_A_PAGE + 1);
        break;
      case ASIA:
        item[0] = countryNames[184].toUpperCase();
        cnt = 1;
        for (int i=185;i<231;i++) {
          item[cnt] =  "  " + countryNames[i];
          cnt ++;
        }
        page = 0;
        pageCount = (cnt % HOW_MANY_LINES_IN_A_PAGE == 0)? (cnt / HOW_MANY_LINES_IN_A_PAGE):(cnt/HOW_MANY_LINES_IN_A_PAGE + 1);
        break;
      case MID:
        item[0] = countryNames[112].toUpperCase();
        cnt = 1;
        for (int i=113;i<127;i++) {
          item[cnt] =  "  " + countryNames[i];
          cnt ++;
        }
        page = 0;
        pageCount = (cnt % HOW_MANY_LINES_IN_A_PAGE == 0)? (cnt / HOW_MANY_LINES_IN_A_PAGE):(cnt/HOW_MANY_LINES_IN_A_PAGE + 1);
        break;
      case -11: break;
    }
  }
  
  public void render() {
    pushStyle();
    fill(245);
    stroke(0);
    strokeWeight(scale);
    rectMode(CORNER);
    rect(x,y,w,lineH*HOW_MANY_LINES_IN_A_PAGE);
    fill(224);
    noStroke();
    for (int i=1;i<HOW_MANY_LINES_IN_A_PAGE;i++) {
      //line(x,y+lineH*i,x+w,y+lineH*i);
      if (i%2==1) {
        rect(x+scale,y+lineH*(i-1)+scale,w-scale,lineH);
      }
    }
    popStyle();
    renderButton();
    renderName();
  }
  
  private void renderName() {
    pushStyle();
    int begin = page*HOW_MANY_LINES_IN_A_PAGE;
    int end;
   
    if (begin+HOW_MANY_LINES_IN_A_PAGE < cnt) {
      end = begin+HOW_MANY_LINES_IN_A_PAGE;
    } else {
      end = cnt;
    }
    fill(0);
    textAlign(LEFT, CENTER);
    for (int i=begin;i<end;i++) {
      text(item[i], x+2*scale, y+(i-begin)*lineH+lineH*0.5f);
    }
    popStyle();
  }
  
  private void renderButton() {
    pushStyle();
    fill(127);
    noStroke();
    triangle(btn_x, btn_y, btn_x+btn_w, btn_y, btn_x + btn_w*0.5f, btn_y - btn_w * 0.5f);
    triangle(btn_x, btn_y+btn_diff, btn_x+btn_w, btn_y+btn_diff, btn_x + btn_w*0.5f, btn_y + btn_w * 0.5f + btn_diff);
    popStyle();
  }
  
  public void update(float _x, float _y) {
    if (ui.searchWindow.getIsDisplay() == false && ui.mapWindow.getIsDisplay() == false) {
      if (_x>= btn_x && _x <= btn_x + btn_w && _y >= btn_y - btn_w * 0.5f && _y <= btn_y) {
        if (page > 0) {
          page--;
        }
      } else if (_x>= btn_x && _x <= btn_x + btn_w && _y >= btn_y +btn_diff * 0.5f && _y <= btn_y + btn_diff + btn_w * 0.5f) {
        if (page<pageCount-1) {
          page++;
        }
      }
    }
  }
  
  public void updateCountryDisplay(float _x, float _y) {
    if (ui.searchWindow.getIsDisplay() == false && ui.mapWindow.getIsDisplay() == false) {
      if (_x >= x && _x <= x+w) {
        for (int i=0;i<HOW_MANY_LINES_IN_A_PAGE;i++) {
          if (_y>= y + lineH*i && _y < y + lineH*(i+1)) {
            String select = item[page*HOW_MANY_LINES_IN_A_PAGE + i];
            if (select.substring(0, 2).equals("  ") == true) {
              select = select.substring(2);
            }
            for (int j=0;j<totalCnt; j++) {
              if (select.equalsIgnoreCase(countryNames[j]) == true) {
                if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {
                  currentRow[0] = j;
                } else if (ui.getDisplayMode() == MULTI_COUNTRY_MODE) {
                  //println("MULTI_COUNTRY_MODE");
                  int ii=0;
                  while (ii<DATA_SET_COUNT && currentDisplay[ii] == true) ii++;
                  if (ii >= DATA_SET_COUNT) {
                    //println("don't have place to display new country");
                    break;
                  }
                  // if ii < 6
                  currentDisplay[ii] = true;
                  currentRow[ii] = j;
                  legend[ii].updateString(countryNames[j]);
                  
                  //test
                  for (int k = 0;k<DATA_SET_COUNT;k++) {
                    println(currentDisplay[k] + "    " + countryNames[currentRow[k]]);
                  }
                  //end of test
                }
              }
            }
            break;
          }
        }
      }
    }
  }
}
    
class DataSets {
  FloatTable data;
  int volumeInterval;
  
  //Constructor
  DataSets (String str) // str is the file name
  {
    data = new FloatTable(str);
    volumeInterval = -1;
  }
  
  public void setupData(String str)
  {
    data = new FloatTable(str);
  }
  
  public void setInterval(int i)
  {
    volumeInterval = i;
  }
  
  public FloatTable getData()
  {
    return data;
  }
  
  public int getInterval()
  {return volumeInterval;}
}

class EventWindow {

  float btnX;
  float btnY;
  float btnLen;

  boolean isDisplay;

  EventWindow(float bx, float by, float bl) {

    btnX = bx;
    btnY = by;
    btnLen = bl;

    isDisplay = false;
  }

  public void render() {
    drawEventButton();
    if (isDisplay) {

      // China
      if (currentRow[0] == 193) {
        renderEvent(2001, "China Joined WTO");
      }
      // Eurasia
      else if (currentRow[0]>=95 && currentRow[0]<= 111) {

        renderEvent(1991, "U.S.S.R Collapsed");
      }
      // Cuba
      else if (currentRow[0] == 21) {

        renderEvent(1980, "Economic Blockage Underway");
      }
      // Iraq
      else if (currentRow[0] == 115) {

        renderEvent(1991, "Gulf War I");
        renderEvent(2003, "Gulf War II");
      }
    }
  }

  private void renderEvent(int year, String txt) {
    pushStyle();
    if (year!=1980) {
      if (year<showYearMin || year > showYearMax) return;
      float x = plotX1+(year-showYearMin+0.5f) * (plotX2 - plotX1) / (showYearMax - showYearMin + 1);
      noFill();
      strokeWeight(2*scale);
      stroke(color(50, 50, 50, 200));
      line(x, plotY2, x, plotY1-2*scale);
      textSize(15*scale);
      textAlign(CENTER, BASELINE);
      text(txt, x, plotY1);
    }
    //Cuba
    else {
      float x = plotX1 + 0.5f * (plotX2 - plotX1) / (showYearMax - showYearMin + 1);
      float x2= plotX2 - 0.5f * (plotX2 - plotX1) / (showYearMax - showYearMin + 1);
      noFill();
      strokeWeight(2*scale);
      stroke(color(50, 50, 50, 200));
      line(x, plotY2, x, plotY1);
      line(x2, plotY2, x2, plotY1);
      textSize(15*scale);
      textAlign(CENTER, BASELINE);
      text(txt, (x+x2)/2, plotY1);
    }

    popStyle();
  }

  public void drawEventButton() {
    pushStyle();
    noStroke();
    fill(127);
    rectMode(CORNER);
    rect(btnX, btnY, btnLen, btnLen);
    fill(0);
    textSize(10*scale);
    textAlign(CENTER, CENTER);

    text("EVENTS", btnX+btnLen/2, btnY+btnLen/2);
    popStyle();
  }

  private void switchDisplay()
  {
    isDisplay = !isDisplay;
  }

  public void update(float _x, float _y) {
    if (_x >= btnX && _x <= btnX+btnLen && _y >= btnY && _y <= btnY+btnLen) {
      switchDisplay();
    }
    if (ui.getDisplayMode() == MULTI_COUNTRY_MODE || ui.getYearMode() == DECADE_MODE || ui.getTable().getIsDisplay()) {
      setNotDisplay();
    }
  }

  public void setNotDisplay()
  {
    isDisplay = false;
  }

  public boolean getIsDisplay()
  {
    return isDisplay;
  }
}

// first line of the file should be the column headers
// first column should be the row titles
// all other values are expected to be floats
// getFloat(0, 0) returns the first data value in the upper lefthand corner
// files should be saved as "text, tab-delimited"
// empty rows are ignored
// extra whitespace is ignored


class FloatTable {
  int rowCount;
  int columnCount;
  float[][] data;
  String[] rowNames;
  String[] columnNames;
  
  
  FloatTable(String filename) {
    
    rowCount = 0;
    String[] rows = loadStrings(filename);
    
    String[] columns = split(rows[0], TAB);
    columnNames = subset(columns, 1); // upper-left corner ignored
    scrubQuotes(columnNames);
    columnCount = columnNames.length;

    rowNames = new String[rows.length-1];
    data = new float[rows.length-1][];

    // start reading at row 1, because the first row was only the column headers
    for (int i = 1; i < rows.length; i++) {
      if (trim(rows[i]).length() == 0) {
        continue; // skip empty rows
      }
      if (rows[i].startsWith("#")) {
        continue;  // skip comment lines
      }

      // split the row on the tabs
      String[] pieces = split(rows[i], TAB);
      scrubQuotes(pieces);
      
      // copy row title
      rowNames[rowCount] = pieces[0];
      // copy data into the table starting at pieces[1]
      data[rowCount] = parseFloat(subset(pieces, 1));

      // increment the number of valid rows found so far
      rowCount++;      
    }
    // resize the 'data' array as necessary
    data = (float[][]) subset(data, 0, rowCount);
  }
 
  public void scrubQuotes(String[] array) {
    for (int i = 0; i < array.length; i++) {
      if (array[i].length() > 2) {
        // remove quotes at start and end, if present
        if (array[i].startsWith("\"") && array[i].endsWith("\"")) {
          array[i] = array[i].substring(1, array[i].length() - 1);
        }
      }
      // make double quotes into single quotes
      array[i] = array[i].replaceAll("\"\"", "\"");
    }
  }
  
  
  public int getRowCount() {
    return rowCount;
  }
  
  
  public String getRowName(int rowIndex) {
    return rowNames[rowIndex];
  }
  
  
  public String[] getRowNames() {
    return rowNames;
  }

  
  // Find a row by its name, returns -1 if no row found. 
  // This will return the index of the first row with this name.
  // A more efficient version of this function would put row names
  // into a Hashtable (or HashMap) that would map to an integer for the row.
  public int getRowIndex(String name) {
    for (int i = 0; i < rowCount; i++) {
      if (rowNames[i].equals(name)) {
        return i;
      }
    }
    //println("No row named '" + name + "' was found");
    return -1;
  }
  
  public int getColumnIndex(String name) {
    for (int i=0;i<columnCount; i++) {
      if (columnNames[i].equals(name)) {
        return i;
      }
    }
    return -1;
  }
  
  // technically, this only returns the number of columns 
  // in the very first row (which will be most accurate)
  public int getColumnCount() {
    return columnCount;
  }
  
  public String getColumnName(int colIndex) {
    return columnNames[colIndex];
  }
  
  
  public String[] getColumnNames() {
    return columnNames;
  }

  public float getFloat(int rowIndex, int col) {
    // Remove the 'training wheels' section for greater efficiency
    // It's included here to provide more useful error messages
    
    // begin training wheels
    if ((rowIndex < 0) || (rowIndex >= data.length)) {
      throw new RuntimeException("There is no row " + rowIndex);
    }
    if ((col < 0) || (col >= data[rowIndex].length)) {
      throw new RuntimeException("Row " + rowIndex + " does not have a column " + col);
    }
    // end training wheels
    
    return data[rowIndex][col];
  }
  
  public boolean isValid(int row, int col) {
    if (row < 0) return false;
    if (row >= rowCount) return false;
    if (col >= columnCount) return false;
    if (col >= data[row].length) return false;
    if (col < 0) return false;
    if (data[row][col] == -1) return false;
    if (data[row][col] == -10) return false;
    return !Float.isNaN(data[row][col]);
  }


  public float getColumnMin(int col) {
    float m = Float.MAX_VALUE;
    for (int row = 0; row < rowCount; row++) {
      if (isValid(row, col)) {
        if (data[row][col] < m) {
          m = data[row][col];
        }
      }
    }
    return m;
  }


  public float getColumnMax(int col) {
    float m = -Float.MAX_VALUE;
    for (int row = 0; row < rowCount; row++) {
      if (isValid(row, col)) {
        if (data[row][col] > m) {
          m = data[row][col];
        }
      }
    }
    return m;
  }

  
  public float getRowMin(int row) {
    float m = Float.MAX_VALUE;
    for (int col = 0; col < columnCount; col++) {
      if (isValid(row, col)) {
        if (data[row][col] < m) {
          m = data[row][col];
        }
      }
    }
    return m;
  } 


  public float getRowMax(int row) {
    float m = -Float.MAX_VALUE;
    for (int col = 0; col < columnCount; col++) {
      if (isValid(row, col)) {
        if (data[row][col] > m) {
          m = data[row][col];
        }
      }
    }
    return m;
  }

  public float getTableMin() {
    float m = Float.MAX_VALUE;
    for (int row = 0; row < rowCount; row++) {
      for (int col = 0; col < columnCount; col++) {
        if (isValid(row, col)) {
          if (data[row][col] < m) {
            m = data[row][col];
          }
        }
      }
    }
    return m;
  }

  public float getTableMax() {
    float m = -Float.MAX_VALUE;
    for (int row = 0; row < rowCount-1; row++) { //exclude last row: World
      for (int col = 0; col < columnCount; col++) {
        if (isValid(row, col)) {
          if (data[row][col] > m) {
            m = data[row][col];
          }
        }
      }
    }
    return m;
  }
}
class HelpWindow
{
  float x;
  float y;
  float w;
  float h;
  
  float btnX;
  float btnY;
  float btnLen;
  
  boolean isActive;
  
  String helpContent;
  
  HelpWindow(float _x, float _y, float _w, float _h, String content, float bx, float by, float _len)
  {
    x = _x;
    y = _y;
    w = _w;
    h = _h;
    
    helpContent = content;
    
    isActive = false;
    
    btnX = bx;
    btnY = by;
    
    btnLen = _len;
    
  }

  public void render() {
    drawHelpButton();
    if (isActive)
    {
      pushStyle();
      stroke(0);
      fill(224);
      rectMode(CORNER);
      rect(x, y, w, h);
      fill(0);
      textAlign(CENTER, CENTER);
      textSize(16*scale);
      text(helpContent, (HELP_WINDOW_X1+HELP_WINDOW_X2)/2, (HELP_WINDOW_Y1+HELP_WINDOW_Y2)/2);
      popStyle();
    }
    pushStyle();
  }
  
  private void switchActive()
  {
    isActive = !isActive;
  }
  
  public void drawHelpButton()
  {
    pushStyle();
    noStroke();
    fill(127);
    rectMode(CORNER);
    rect(btnX, btnY, btnLen, btnLen);
    fill(0);
    textSize(10*scale);
    textAlign(CENTER, CENTER);
    text("CREDITS", btnX+btnLen/2, btnY+btnLen/2);
    popStyle();
  }
  
  public void update(float _x, float _y)
  {
    if (_x >= btnX && _x <= btnX+btnLen && _y >= btnY && _y <= btnY+btnLen) {
      switchActive();
    }
  }
  
  public boolean getIsDisplay() {
    return isActive;
  }
}
class Integrator {

  final float DAMPING = 0.5f;
  final float ATTRACTION = 0.2f;

  float value;
  float vel;
  float accel;
  float force;
  float mass = 1;

  float damping = DAMPING;
  float attraction = ATTRACTION;
  boolean targeting;
  float target;


  Integrator() { }


  Integrator(float value) {
    this.value = value;
  }


  Integrator(float value, float damping, float attraction) {
    this.value = value;
    this.damping = damping;
    this.attraction = attraction;
  }


  public void set(float v) {
    value = v;
  }


  public void update() {
    if (targeting) {
      force += attraction * (target - value);      
    }

    accel = force / mass;
    vel = (vel + accel) * damping;
    value += vel;

    force = 0;
  }


  public void target(float t) {
    targeting = true;
    target = t;
  }


  public void noTarget() {
    targeting = false;
  }
}
String str1 = "QWERTYUIOP";
String str2 = "ASDFGHJKL";
String str3 = "ZXCVBNM";
char[] keyText1 = str1.toCharArray();
char[] keyText2 = str2.toCharArray();
char[] keyText3 = str3.toCharArray();

final float KEY_LEN = 34*scale;
final float KEY_TAB = 10*scale;
final float KEY_DIFF = 6*scale;

class Keyboard {
  
  float x, y;
  float tab;
  float len; // length of each key
  float diff;
  
  Keyboard(float _x, float _y) {
    x = _x;
    y = _y;
    
    len = KEY_LEN;
    tab = KEY_TAB;
    diff = KEY_DIFF;
  }
  
  public void render() {
    pushStyle();
    
    
    //translate(x,y);
    stroke(0);
    noFill();
    rectMode(CORNER);
    textSize(25*scale);
    textAlign(CENTER, CENTER);
    
    // 1st line QWERTYUIOP
    for (int i=0;i<10;i++) {
      rect(x+i*(len+diff), y, len, len);
      fill(0);
      text(keyText1[i], x+i*(len+diff)+len/2, y+len/2);
      noFill();
    }
    
    // BACKSPACE
    rect(x+10*(len+diff), y, 1.5f*len, len);
    fill(0);
    text("<=", x+10*(len+diff)+len*0.75f, y+len/2);
    noFill();
    
    // 2nd line ASDFGHJKL
    for (int i=0;i<9;i++) {
      rect(x+i*(len+diff)+tab, y+len+diff, len, len);
      fill(0);
      text(keyText2[i], x+i*(len+diff)+tab+len/2, y+len+diff+len/2);
      noFill();
    }
    
    // ENTER
    rect(x+9*(len+diff)+tab, y+len+diff, 2*len, len);
    fill(0);
    textSize(15*scale);
    text("     ENTER", x+9*(len+diff)+tab+len/2, y+len+diff+len/2);
    noFill();
    
    textSize(25*scale);
    // 3rd line ZXCVBNM
    for (int i=0;i<7;i++) {
      rect(x+i*(len+diff)+2.5f*tab, y+(len+diff)*2, len, len);
      fill(0);
      text(keyText3[i], x+i*(len+diff)+2.5f*tab+len/2, y+(len+diff)*2+len/2);
      noFill();
    }
      
      
    popStyle();
  }
  
  public int update(float _x, float _y) {
    for (int i=0;i<10;i++) {
      if (inside(x+i*(len+diff), y, len, len, _x, _y)) {
        println((int)keyText1[i]);
        return (int) keyText1[i];
      }
    }
    for (int i=0;i<9;i++) {
      if (inside(x+i*(len+diff)+tab, y+len+diff, len, len, _x, _y)) {
        println((int)keyText2[i]);
        return (int) keyText2[i];
      }
    }
    for (int i=0;i<7;i++) {
      if (inside(x+i*(len+diff)+2.5f*tab, y+(len+diff)*2, len, len, _x, _y)) {
        println((int)keyText3[i]);
        return (int) keyText3[i];
      }
    }
    if (inside(x+10*(len+diff), y, 1.5f*len, len, _x, _y)) {
      return WRONG;
    }
      
    if (inside(x+9*(len+diff)+tab, y+len+diff, 2*len, len, _x, _y)) {
      return ZHONG;
    }
    
    return 0;
 }
 
 private boolean inside(float x, float y, float w, float h, float posx, float posy) {
   return (posx >= x && posx <= x+w && posy >= y && posy <= y + h)? true:false;
 }
 
 public float getKeyLen() {
   return len;
 }
 
}
class Legend {
  float x;
  float y;
  String str;
  int colour;
  
  float w;
  
  Legend(float _x, float _y, float _w, String _s, int c) {
    x = _x;
    y = _y;
    w = _w;
    str = _s;
    colour = c;
  }
  
  public void render() {
    pushStyle();
    fill(colour);
    noStroke();
    rectMode(CORNER);
    rect(x,y,w,w);
    fill(0);
    textAlign(LEFT, CENTER);
    text(str, x+w+2*scale, y+w*0.5f);
  }
  
  public void update(int i) {
    if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {
      str = ui.dataSelector[i].getTxt()+"\n"+unitText[i];
    } else if (ui.getDisplayMode() == MULTI_COUNTRY_MODE) {
      if (currentDisplay[i] == true) {
        str = countryNames[currentRow[i]];
      } else {
        str = "";
      }
    }
  }
  
  public void updateString(String strr) {
    str = strr;
  }
  
  public void click(float _x, float _y, int number) {
    if (_x >= x && _x <= x+110*scale && _y>=y && _y<=y+w) {
      if (ui.getDisplayMode() == MULTI_COUNTRY_MODE) {
        if (ui.getSearchWindow().getIsDisplay() == false && ui.getMapWindow().getIsDisplay() == false && ui.getTableWindow().getIsDisplay() == false && ui.getHelpWindow().getIsDisplay() == false) {
          currentDisplay[number] = false;
          str = "";
        }
      }
      if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {
        if (ui.getSearchWindow().getIsDisplay() == false && ui.getMapWindow().getIsDisplay() == false && ui.getTableWindow().getIsDisplay() == false && ui.getHelpWindow().getIsDisplay() == false) {
          displayVolume = number;
        }
      }
    }
    ui.updateDataForPieChart();
  }
}

class MapWindow {
  float x;
  float y;
  
  float capHeight;
  float w;
  float winHeight;
  
  String caption;
  
  float btnX;
  float btnY;
  float btnLen;
  
  PImage worldmap;
  
  boolean isDisplay;
  
  MapWindow(float _x, float _y, float wid, String cap, float cH, float wH, float bx, float by, float bl) {
    
    x = _x;
    y = _y;
    w = wid;
    caption = cap;
    capHeight = cH;
    winHeight = wH;

    worldmap = loadImage("map.png");
    worldmap.resize((int)w-1, (int)(winHeight)-1);    
    
    btnX = bx;
    btnY = by;
    btnLen = bl;

    isDisplay = false;
  }
  
  public void render() {
    drawMapButton();
    if (isDisplay) {
      pushStyle();
      fill(202);
      stroke(0);
      rectMode(CORNER);
      rect(x, y, w, capHeight+winHeight);
      line(x, y+capHeight, x+w, y+capHeight);
      textAlign(CENTER, CENTER);
      fill(0);
      textSize(25*scale);
      text(caption, x+0.5f*w, y+0.5f*capHeight);
      popStyle();
      image(worldmap, x+1, y+capHeight+1);
    }
  }
  
  public void drawMapButton() {
    pushStyle();
    noStroke();
    fill(127);
    rectMode(CORNER);
    rect(btnX, btnY, btnLen, btnLen);
    fill(0);
    textSize(10*scale);
    textAlign(CENTER, CENTER);
    
    text("MAP", btnX+btnLen/2, btnY+btnLen/2);
    popStyle();
  }
  
  private void switchDisplay()
  {
    isDisplay = !isDisplay;
  }
  
  public void update(float _x, float _y) {
    if (_x >= btnX && _x <= btnX+btnLen && _y >= btnY && _y <= btnY+btnLen) {
      switchDisplay();
      if (isDisplay)
      {
        ui.getSearchWindow().setNotDisplay();
      }
    }
    else {
      if (isDisplay && _x >= x && _x <= x + w && _y >= y+capHeight && _y <= y+capHeight + winHeight)
      {
        float xx = _x - x;
        float yy = _y - (y + capHeight);
        
        //float xx = map(_x, x, x+w, 0, 4399);
        //float yy = map(_y, y+capHeight, y+capHeight+winHeight, 0, 2233);
        
        int loc = (int)xx + (int)yy*(int)(w-1);
        float r = red(worldmap.pixels[loc]);
        float g = green(worldmap.pixels[loc]);
        float b = blue(worldmap.pixels[loc]);
        println("(r, g, b): " + "("+ r + ", " + g + ", " + b + ")");
        int region = -11;
        if (r == 254) {
          if (b == 191) {
            region = RUS;
          } else if (b == 63) {
            region = AFRI;
          } else if (b == 0) {
            region = MID;
          }
        } else if (r == 236) {
            region = N_AME;
        } else if (r == 127) {
          region = EURO;
        } else if (r == 169) {
          region = S_AME;
        } else if (r == 0) {
          region = ASIA;
        }
        ui.getCountryList().updateByMap(region);
        println("region: " +region);
        if (region != -11) {
          setNotDisplay();
        }
      }
    }
  }
  
  public void setNotDisplay()
  {
    isDisplay = false;
  }
  
  public boolean getIsDisplay()
  {
    return isDisplay;
  }
}
class PieButton {
  float x;
  float y;
  float h;
  float diff;
  
  int year;
  
  PieButton(float _x, float _y, float _h, float _diff) {
    x = _x;
    y = _y;
    h = _h;
    diff = _diff;
    
    year = yearMin;

  }
  public void render() {
    pushStyle();
    fill(127);
    noStroke();
    triangle(x, y, x, y+h, x - h*0.5f, y + h*0.5f);
    triangle(x+diff, y, x+diff, y+h, x+diff+h*0.5f, y+h*0.5f);
    popStyle();
  }
  public void update(float _x, float _y) {
    if (_y >= y && y <= y + h && _x >= x - h * 0.5f && _x <= x) {
      if (year > yearMin) year--;
    }

    if (_y>= y && _y <= y + h && _x >= x + diff && _x <= x + diff+h*0.5f) {
      if (year < yearMax) year++;
    }
  }
  
  public int getYear() {
    return year;
  }
}

  
class PieChart
{
  
  int[] colors;
  float[] percentages;
  float total;
  PVector center;
  float radius;
  
  boolean isDisplay;
  
  int index;

  String stats;
  
  PieChart(float[] theData, int n, int[] _colors, float x, float y, float _radius, int i)
  {

    updateData(theData, n, _colors, i);
    center = new PVector(x, y);
    radius = _radius;
    
    isDisplay = false;
  }

  public void updateData(float[] theData, int n, int[] _color, int index)
  {
    percentages = new float[n];
    float count = 0;

    // loop through data
    for (int i = 0; i < n; i++)
    {
      float theValue = theData[i];
      if (theValue >=0 ) count += theValue;
      if (theValue >=0 ) {
        percentages[i] = theValue;
      } else {percentages[i] = 0;
      }
    }
    
    if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {
     if(index != 4 && index != 2) {
      total = percentages[1];
      percentages[1] = percentages[1] - percentages[0];
      stats = nf(percentages[0]/total*100,0,2) + "%";    
     } else {
      total = count;
      stats = nf(percentages[0]/percentages[1]*100,0,2) + "%";
     }
    }
    else // MULTI_COUNTRY_MODE
    {
      total = count;
    }
    colors = _color;
  }

  public void render()
  {
    if (isDisplay) {
      
      float startDegree = 0.0f;

      pushStyle();
      stroke(0xff818181);
      strokeWeight(2*scale);
      
        for (int i = 0; i < percentages.length; i++)
        {
          if (percentages[i] > 0)
          {
            fill (colors[i]);
            float len = map(percentages[i], 0, total, 0.0f, TWO_PI); 
            arc(center.x, center.y, radius, radius, startDegree, startDegree + len);
            startDegree += len;
          }
        }

        if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {
          textAlign(RIGHT,CENTER);
          fill(0);
          textSize(15*scale);
          text(stats, Width-15*scale, center.y);
        }// end of ONE_COUNTRY_MODE
        else { // MULTI_COUNTRY_MODE
          fill(0);
          
          textSize(10*scale);
          int ct = 0;
          for (int i=0;i<DATA_SET_COUNT;i++) {
            if (currentDisplay[i] == true) {
              textAlign(LEFT, CENTER);
              text(countryNames[currentRow[i]], Width - 210*scale, 130*scale + ct*30*scale);
              textAlign(RIGHT, CENTER);
              text(nf(percentages[ct]/total*100, 0, 2) + "%", Width - 5*scale, 130*scale + ct*30*scale);
              ct++;
            }
          }
        }
      popStyle();
    }
  }

  public void setCenter(float x, float y)
  {
    center.x = x; 
    center.y = y;
  }

  public void setRadius(float r) {
    radius = r;
  }
  
  public void test() {
    println("test");
  }
  
  public boolean getIsDisplay() {
    return isDisplay;
  }
  public void setNotDisplay() {
    isDisplay = false;
  }
  
  public void setDisplay() {
    isDisplay = true;
  }
  
  public void setIfDisplay(boolean ss) {
    isDisplay = ss;
  }
}
final float KEYBOARD_X = 20*scale;
final float KEYBOARD_Y = 45*scale;

class SearchWindow {
  float x;
  float y;
  float capHeight;
  float w;
  float winHeight;
  
  String caption;
  
  float btnX;
  float btnY;
  float btnLen;

  //Button closeButton;

  Keyboard keyboard;

  boolean isDisplay;
  
  String searchKey;

  SearchWindow(float _x, float _y, float wid, String cap, float cH, float wH, float bx, float by, float bl) {
    x = _x;
    y = _y;
    w = wid;
    caption = cap;
    capHeight = cH;
    winHeight = wH;

    keyboard = new Keyboard(x+KEYBOARD_X, y+capHeight + KEYBOARD_Y); 
    
    btnX = bx;
    btnY = by;
    btnLen = bl;

    isDisplay = false;
    searchKey = "";
  }

  public void render() {
    drawSearchButton();
    if (isDisplay) {
      pushStyle();
      fill(202);
      stroke(0);
      rectMode(CORNER);
      rect(x, y, w, capHeight+winHeight);
      line(x, y+capHeight, x+w, y+capHeight);
      textAlign(CENTER, CENTER);
      fill(0);
      textSize(25*scale);
      text(caption, x+0.5f*w, y+0.5f*capHeight);
      popStyle();
      keyboard.render();
      renderSearchKey();
    }
  }
  
  public void drawSearchButton() {
    pushStyle();
    noStroke();
    fill(127);
    rectMode(CORNER);
    rect(btnX, btnY, btnLen, btnLen);
    fill(0);
    textAlign(CENTER, CENTER);
    textSize(10*scale);
    text("SEARCH", btnX+btnLen/2, btnY+btnLen/2);
    popStyle();
  }
  
  private void switchDisplay()
  {
    isDisplay = !isDisplay;
  }
  
  public void update(float _x, float _y) {
    if (_x >= btnX && _x <= btnX+btnLen && _y >= btnY && _y <= btnY+btnLen) {
      switchDisplay();
      if (isDisplay)
      {
        ui.getMapWindow().setNotDisplay();
        
        searchKey = "";
      }
    }
    else {
      if (isDisplay) {
        int index = keyboard.update(_x, _y);
        if (index == ZHONG) {
          // update country list
          ui.getCountryList().updateBySearch(searchKey);
          println("search key: " + searchKey);
          setNotDisplay();
          //setNotDisplay();
        } else if (index == WRONG) {
          if (searchKey!="") {
            searchKey = searchKey.substring(0,searchKey.length()-1);
          }
          println("search key: " + searchKey);
        } else if (index!=0) {
          searchKey = searchKey + String.valueOf((char)index);
          println("search key: " + searchKey);
        }
      }
    }
  }
  
  public void setNotDisplay()
  {
    isDisplay = false;
  }
  
  private void renderSearchKey() {
    pushStyle();
    fill(255);
    stroke(0);
    rectMode(CORNERS);
    rect(x+ KEYBOARD_X + keyboard.getKeyLen() * 0.3f, y + capHeight + KEYBOARD_Y * 0.1f, x + w - keyboard.getKeyLen() * 3, y + capHeight + KEYBOARD_Y * 0.9f);
    
    fill(0);
    textSize(30*scale);
    textAlign(LEFT, CENTER);
    text(searchKey.toLowerCase(), x + KEYBOARD_X + keyboard.getKeyLen() * 0.5f, y + capHeight+ KEYBOARD_Y * 0.3f);
    
    popStyle();
  }
  
  public boolean getIsDisplay()
  {
    return isDisplay;
  }
}

class TableButton {
  float x;
  float y;
  float h;
  float diff;
  
  int page;
  
  TableButton(float _x, float _y, float _h, float _diff) {
    x = _x;
    y = _y;
    h = _h;
    diff = _diff;
    
    page = 0;

  }
  public void render() {
    pushStyle();
    fill(127);
    noStroke();
    triangle(x, y, x, y+h, x - h*0.5f, y + h*0.5f);
    triangle(x+diff, y, x+diff, y+h, x+diff+h*0.5f, y+h*0.5f);
    popStyle();
  }
  public void update(float _x, float _y) {
    if (_y >= y && y <= y + h && _x >= x - h * 0.5f && _x <= x) {
      if (page > 0) page--;
    }

    if (_y>= y && _y <= y + h && _x >= x + diff && _x <= x + diff+h*0.5f) {
      if (page < 2) page++;
    }
  }
  
  public int getPage() {
    return page;
  }
}

class TableWindow {

  float x;
  float y;

  float lineW;
  float lineH;

  float btnX;
  float btnY;
  float btnLen;

  boolean isDisplay;

  int colCount;
  int rowCount;

  String[] txt;

  TableButton pageBtn;

  TableWindow(float _x, float _y, float _w, float _h, float bx, float by, float bl, float pbx, float pby, float pbh, float _diff) {
    x = _x;
    y = _y;
    lineW = _w;
    lineH = _h;

    btnX = bx;
    btnY = by;
    btnLen = bl;

    pageBtn = new TableButton(pbx, pby, pbh, _diff);

    isDisplay = false;
    colCount = 12;
    rowCount = DATA_SET_COUNT+1;

    txt = new String[] {
      "Ener Prod", "Ener Use", "Ener Use Per", "CO2 Emit", "CO2 Emit Per", "Renew Elec Prod"
    };
  }

  public boolean getIsDisplay() {
    return isDisplay;
  }

  public void render() {
    renderButton();
    if (isDisplay) {
      pushStyle();
      noFill();
      //strokeWeight(scale);
      stroke(0);
      rectMode(CORNER);

      //table
      rect(x, y, lineW * colCount, lineH * rowCount);

      //horizontal
      for (int i=1;i<rowCount;i++)
      {
        line(x, y+lineH*i, x+lineW*colCount, y+lineH*i);
      }

      //vertical
      for (int i=colCount-10;i<colCount;i++)
      {
        line(x+lineW*i, y, x+lineW*i, y + lineH*rowCount);
      }
      // end of table

      fill(0);
      textAlign(CENTER, CENTER);
      //title
      for (int i=colCount-10;i<colCount;i++)
      {
        text(1980+i-colCount+10+10*pageBtn.getPage(), x+lineW*(i+0.5f), y+lineH*0.5f);
      }

      // end of title

      // content

      if (ui.getDisplayMode() == ONE_COUNTRY_MODE) {

        textAlign(LEFT, CENTER);
        for (int i=rowCount-DATA_SET_COUNT;i<rowCount;i++)
        {
          text(ui.getDataSelector(i-rowCount+DATA_SET_COUNT).getTxt(), x+2*scale, y+lineH*(i+0.5f));
          //text(txt[i-rowCount+6], x+scale,y+lineH*(i+0.5));
        }

        textAlign(RIGHT, CENTER);
        for (int i=rowCount-DATA_SET_COUNT;i<rowCount;i++)
        {
          for (int j=colCount-10;j<colCount;j++)
          {
            int colll = j-colCount+10 + pageBtn.getPage()*10;
            if (dataSets[i+DATA_SET_COUNT-rowCount].getData().isValid(currentRow[0], colll)) {
              text(nf(dataSets[i+DATA_SET_COUNT-rowCount].getData().getFloat(currentRow[0], colll), 0, 4), x+lineW*(j+1)-2*scale, y+lineH*(i+0.5f));
            }
          }
        }
      } 
      else {

        textAlign(LEFT, CENTER);
        for (int i=rowCount-DATA_SET_COUNT;i<rowCount;i++)
        {
          if (currentDisplay[i-rowCount+DATA_SET_COUNT] == true) {
            text(countryNames[currentRow[i-rowCount+DATA_SET_COUNT]], x+2*scale, y+lineH*(i+0.5f));
          }
        }

        textAlign(RIGHT, CENTER);
        int tab = 0;
        while (tab<DATA_SET_COUNT && ui.getDataSelector(tab).getCheck() == false) tab++;
        if (tab < DATA_SET_COUNT) { 
          for (int i=rowCount-DATA_SET_COUNT;i<rowCount;i++)
          {
            if (currentDisplay[i-rowCount+DATA_SET_COUNT] == true) {

              for (int j=colCount-10;j<colCount;j++)
              {
                int colll = j-colCount+10 + pageBtn.getPage()*10;
                if (dataSets[tab].getData().isValid(currentRow[i-rowCount+DATA_SET_COUNT], colll)) {
                  text(nf(dataSets[tab].getData().getFloat(currentRow[i-rowCount+DATA_SET_COUNT], colll), 0, 4), x+lineW*(j+1)-2*scale, y+lineH*(i+0.5f));
                }
              }
            }
          }
        }
      }
      // end of content

      popStyle();

      pageBtn.render();
    }
  }

  private void renderButton() {
    pushStyle();
    noStroke();
    fill(127);
    rectMode(CORNER);
    rect(btnX, btnY, btnLen, btnLen);
    fill(0);
    textAlign(CENTER, CENTER);
    textSize(10*scale);
    text("RAW\nDATA", btnX+btnLen/2, btnY+btnLen/2);
    popStyle();
  }

  public void updateButton(float _x, float _y) {
    if (_x >= btnX && _x <= btnX + btnLen && _y >= btnY && _y <= btnY + btnLen) {
      switchDisplay();
    }
  }

  public void switchDisplay() {
    isDisplay = !isDisplay;
  }

  public void updatePage(float _x, float _y) {
    pageBtn.update(_x, _y);
  }
}

class TimeLine {
  
  float x, y;
  float w, h;
  
  TimeLock leftLock, rightLock;
  
  TimeLine(float _x, float _y, float _w, float _h, float lockw, float lockh) {
    x = _x;
    y = _y;
    w = _w;
    h = _h;
    
    leftLock = new TimeLock(x,y,lockw, lockh);
    rightLock = new TimeLock(x+w,y, lockw, lockh);
  }
  
  
  public void render() {
    pushStyle();
    
    noFill();
    stroke(125);
    strokeWeight(h);
    
    line(x,y,x+w, y);
    
    for (int column = 0; column < columnCount; column++) {
      if (column%2 == 0) {
        float xx = map(years[column], yearMin, yearMax, x, x+w);
        line(xx, y-h, xx, y+h);
        //text(years[column], x, (plotY2 + textAscent() + 7)*scale);
      }
    }
  
    popStyle(); 
    leftLock.render();
    rightLock.render();
  }
  
  public TimeLock getLeftLock() {
    return leftLock;
  }
  
  public TimeLock getRightLock() {
    return rightLock;
  }
  
  public void update(float _x, int l_r) {
   if (ui.getYearMode() == YEAR_MODE) { 
    int Year = (int) map(_x, x,x+w, yearMin, yearMax);
    if (l_r == 0) {
      if (Year < yearMin) Year = yearMin;
      if (Year >= showYearMax) Year = showYearMax-1;
      leftLock.setX(w*(Year-1980)/29.0f + x);
      showYearMin = Year;
    } else if (l_r == 1) {
      if (Year > yearMax) Year = yearMax;
      if (Year <= showYearMin) Year = showYearMin+1;
      rightLock.setX(w*(Year-1980)/29.0f + x);
      showYearMax = Year;
    }
   }
  }
  
  public void resume() {
    leftLock.resume();
    rightLock.resume();
    showYearMin = yearMin;
    showYearMax = yearMax;
  }
}
    
class TimeLock {
  private float x;
  private float y;
  
  float init;
  
  float w;
  float h;
  
  TimeLock(float _x, float _y, float _w, float _h) {
    x = _x;
    y = _y;
    w = _w;
    h = _h;
    init = x;
  }
  
  public void updateTimeLock(float _x) {
    x = _x;
  }
  
  public void render()
  {
    pushStyle();
    fill(0);
    rectMode(CORNERS);
    ellipse(x,y,w*2,h*2);
    //rect(x-w*scale,y-h*scale,x+w*scale,y+h*scale);
    popStyle();
  }
  
  public float getX() {return x;}
  public float getY() {return y;}
  
  public void setX(float _x) {
    x = _x;
  }
  
  public void resume() {
    x = init;
  }
}
class TouchListener implements OmicronTouchListener{
 
  // Called on a touch down event
  // mousePressed events also call this with an ID of -1 and an xWidth and yWidth of 10.
  public void touchDown(int ID, float xPos, float yPos, float xWidth, float yWidth){
    fill(255,0,0);
    noStroke();
    ellipse( xPos, yPos, xWidth, yWidth );
    
    // This is an optional call if you want the function call in the main applet class.
    // 'OmicronExample' should be replaced with the sketch name i.e. ((SketchName)applet).touchDown( ID, xPos, yPos, xWidth, yWidth );
    // Make sure applet is defined as PApplet and that 'applet = this;' is in setup().
    ((Shi424P1)applet).touchDown( ID, xPos, yPos, xWidth, yWidth );
  }// touchDown
  
  // Called on a touch move event
  // mouseDragged events also call this with an ID of -1 and an xWidth and yWidth of 10.
  public void touchMove(int ID, float xPos, float yPos, float xWidth, float yWidth){
    fill(0,255,0);
    noStroke();
    ellipse( xPos, yPos, xWidth, yWidth );
    
    ((Shi424P1)applet).touchMove( ID, xPos, yPos, xWidth, yWidth );
  }// touchMove
  
  // Called on a touch up event
  // mouseReleased events also call this with an ID of -1 and an xWidth and yWidth of 10.
  public void touchUp(int ID, float xPos, float yPos, float xWidth, float yWidth){
    fill(0,0,255);
    noStroke();
    ellipse( xPos, yPos, xWidth, yWidth );
    
    ((Shi424P1)applet).touchUp( ID, xPos, yPos, xWidth, yWidth );
  }// touchUp
  
}// TouchListener
class UserInterface
{
  CheckItem[] dataSelector; //6 
  CheckItem[] countrySelector; //2
  CheckItem[] totalSelector; //2
  CheckItem[] yearSelector;
  PieButton pieButton;

  //Range range;

  CountryList countryList;

  TimeLine timeLine;

  HelpWindow helpWindow;

  MapWindow mapWindow;

  SearchWindow searchWindow;
  
  TableWindow tableWindow;
  
  EventWindow event;

  private int displayMode; //ONE_COUNTRY_MODE; MULTI_COUNTRY_MODE;

  private int yearMode;

  private int pieChartMode; //REGION_MODE; WORLD_MODE;

  private int countryCount;

  private int dataSetCount;
  

  //
  //
  //
  // iiiiiiinitiateeeeeeeeeeeee
  //
  //
  /////////////////
  ///////////
  /////////////////////
  ////////////////////////////
  UserInterface() { 

    //List Box 1
    //myList = setupListBox("Countries", "SELECT COUNTRY");
    //myList = setupListBox("Countries2", "SELECT COUNTRY 2");


    // initiate selectors
    countrySelector = new CheckItem[2];
    countrySelector[0] = new CheckItem(COUNTRY_SELECTOR_X, COUNTRY_SELECTOR_Y, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, "ONE-COUNTRY Mode");
    countrySelector[1] = new CheckItem(COUNTRY_SELECTOR_X, COUNTRY_SELECTOR_Y+CHECK_ITEM_HEIGHT+3*scale, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, "MUTLI-COUNTRY Mode");

    countrySelector[0].setActive();

    dataSelector = new CheckItem[DATA_SET_COUNT];
    dataSelector[0] = new CheckItem(DATA_SELECTOR_X, DATA_SELECTOR_Y, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, "Energy Production");
    dataSelector[1] = new CheckItem(DATA_SELECTOR_X, DATA_SELECTOR_Y_2, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, "Energy Consumption");
    dataSelector[2] = new CheckItem(DATA_SELECTOR_X, DATA_SELECTOR_Y_3, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, "Energy Consumption Per");
    dataSelector[3] = new CheckItem(DATA_SELECTOR_X+DATA_SELECTOR_DIFF, DATA_SELECTOR_Y, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, "CO2 Emission");
    dataSelector[4] = new CheckItem(DATA_SELECTOR_X+DATA_SELECTOR_DIFF, DATA_SELECTOR_Y_2, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, "CO2 Emission Per");
    dataSelector[5] = new CheckItem(DATA_SELECTOR_X+DATA_SELECTOR_DIFF, DATA_SELECTOR_Y_3, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, "Renewable Electricity Gen");
    dataSelector[6] = new CheckItem(DATA_SELECTOR_X+DATA_SELECTOR_DIFF*2, DATA_SELECTOR_Y_3, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, "Population!");

    dataSelector[0].setActive();

    totalSelector = new CheckItem[2];
    totalSelector[0] = new CheckItem(TOTAL_SELECTOR_X, TOTAL_SELECTOR_Y, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, " % in Region");
    totalSelector[1] = new CheckItem(TOTAL_SELECTOR_X, TOTAL_SELECTOR_Y+TOTAL_DIFF, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, " % in World");

    totalSelector[0].setActive();

    yearSelector = new CheckItem[2];
    yearSelector[0] = new CheckItem(YEAR_SELECTOR_X, YEAR_SELECTOR_Y, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, "show data by Year");
    yearSelector[1] = new CheckItem(YEAR_SELECTOR_X+YEAR_DIFF, YEAR_SELECTOR_Y, CHECK_BOX_WIDTH, BUTTON_WIDTH, CHECK_ITEM_HEIGHT, "show data by Decade");

    yearSelector[0].setActive();

    timeLine = new TimeLine(plotX1, plotY2 + 40*scale, plotX2-plotX1, 5*scale, TIME_LOCK_W, TIME_LOCK_H);

    pieButton = new PieButton(PIE_BUTTON_X, PIE_BUTTON_Y, PIE_BUTTON_H, PIE_BUTTON_DIFF);

    displayMode = ONE_COUNTRY_MODE;
    pieChartMode = REGION_MODE;

    yearMode = YEAR_MODE;

    countryCount = 1;
    dataSetCount = 1;

    countryList = new CountryList(LIST_X, LIST_Y, LIST_W, LIST_LINE_H, PAGE_BTN_X, PAGE_BTN_Y, PAGE_BTN_W, PAGE_BTN_DIFF);

    mapWindow = new MapWindow(MAP_WINDOW_X, MAP_WINDOW_Y, MAP_WINDOW_WIDTH, MAP_WINDOW_CAPTION, M_CAPTION_HEIGHT, M_WINDOW_HEIGHT, MAP_BUTTON_X, MAP_BUTTON_Y, MAP_BUTTON_LEN);

    searchWindow = new SearchWindow(SEARCH_WINDOW_X, SEARCH_WINDOW_Y, SEARCH_WINDOW_WIDTH, SEARCH_WINDOW_CAPTION, S_CAPTION_HEIGHT, S_WINDOW_HEIGHT, SEARCH_BUTTON_X, SEARCH_BUTTON_Y, SEARCH_BUTTON_LEN);

    helpWindow = new HelpWindow(HELP_WINDOW_X1, HELP_WINDOW_Y1, (HELP_WINDOW_X2-HELP_WINDOW_X1), (HELP_WINDOW_Y2-HELP_WINDOW_Y1), HelpContent, HELP_BUTTON_X, HELP_BUTTON_Y, HELP_BUTTON_LEN);
    
    tableWindow = new TableWindow(TABLE_WINDOW_X, TABLE_WINDOW_Y, TABLE_W, TABLE_H, TABLE_BTN_X, TABLE_BTN_Y, TABLE_BTN_LEN, TABLE_PAGE_BTN_X,TABLE_PAGE_BTN_Y,TABLE_PAGE_BTN_H,TABLE_PAGE_BTN_DIFF);
    
    event = new EventWindow(EVENT_BTN_X, EVENT_BTN_Y, EVENT_BTN_LEN);
  } 
  //end of constructor
  //end of constructor
  //end of constructor
  //end of constructor
  //end of constructor

  public void render()
  {
    // render country list

    // render check items
    for (int i=0;i<2;i++)
    {
      countrySelector[i].render();
      totalSelector[i].render();
      yearSelector[i].render();
    }
    for (int i=0;i<DATA_SET_COUNT;i++)
    {
      dataSelector[i].render();
    }

    // render country list
    countryList.render();

    // render time line
    timeLine.render();

    // render pie buttons
    pieButton.render();

    // render events
    event.render();
    
    // render table
    tableWindow.render();
    // render world map
    mapWindow.render();
    // 2nd last step is to render keyboard
    searchWindow.render();
    // last step is to render help window
    helpWindow.render();
  }

  public int getDisplayMode()
  {
    return displayMode;
  }

  public int getYearMode()
  {
    return yearMode;
  }

  public int getPieChartMode()
  {
    return pieChartMode;
  }

  public void setDisplayMode(int dismode)
  {
    displayMode = dismode;
  }

  public void setYearMode(int dismode)
  {
    yearMode = dismode;
  }

  public void setPieChartMode(int dismode)
  {
    pieChartMode = dismode;
  }

  public void updateCountryList(float posx, float posy) {
    countryList.update(posx, posy);
    updateDataForPieChart();
  }

  public void updateYearSelector(float posx, float posy) {
    if (posx >= yearSelector[0].getX() && posx <= yearSelector[0].getX() + yearSelector[0].getCBW()
      && posy >= yearSelector[0].getY() && posy <= yearSelector[0].getY() + yearSelector[0].getH()) {
      yearSelector[0].setCheck(true);
      yearSelector[1].setCheck(false);

      yearMode = YEAR_MODE;
      updateDataForPieChart();
    }
    if (posx >= yearSelector[1].getX() && posx <= yearSelector[1].getX() + yearSelector[1].getCBW()
      && posy >= yearSelector[1].getY() && posy <= yearSelector[1].getY() + yearSelector[1].getH()) {
      yearSelector[1].setCheck(true);
      yearSelector[0].setCheck(false);

      yearMode = DECADE_MODE;
      println(yearMode);
      timeLine.resume();
      updateDataForPieChart();
    }
  }

  public void updateCountrySelector(float posx, float posy) {
    if ( posx >= countrySelector[0].getX() && posx<= countrySelector[0].getX()+countrySelector[0].getCBW()
      && posy >= countrySelector[0].getY() && posy <= countrySelector[0].getY()+countrySelector[0].getH()) {
      countrySelector[0].setCheck(true);
      countrySelector[1].setCheck(false);

      displayMode = ONE_COUNTRY_MODE;
      for (int i=0;i<DATA_SET_COUNT;i++)
      {
        pieChart[i].setIfDisplay(dataSelector[i].getCheck());
      }
      updateLegend();
      updateDataForPieChart();
    }
    if ( posx >= countrySelector[1].getX() && posx<= countrySelector[1].getX()+countrySelector[1].getCBW()
      && posy >= countrySelector[1].getY() && posy <= countrySelector[1].getY()+countrySelector[1].getH()) {
      countrySelector[1].setCheck(true);
      countrySelector[0].setCheck(false);

      displayMode = MULTI_COUNTRY_MODE;
      //currentDisplay[0] = true;
      //pieChart[0].setDisplay();
      //for (int i=1;i<6;i++) {
        //currentDisplay[i] = false;
        //pieChart[i].setNotDisplay();
      //}

      // only one data set will be displayed in MULTI_COUNTRY_MODE
      boolean find = false;
      for (int i=0;i<DATA_SET_COUNT;i++) {
        if (!find && dataSelector[i].getCheck()) {
          find = true;
          continue;
        }
        if (find) {
          dataSelector[i].setNotActive();
        }
      }

      updateLegend();
      updateDataForPieChart();
    }
  }

  public void updateDataSelector(float posx, float posy) {
    int id = -1;
    for (int i=0;i<DATA_SET_COUNT;i++) {
      if ( posx >= dataSelector[i].getX() && posx<= dataSelector[i].getX() + dataSelector[i].getCBW()
        && posy >= dataSelector[i].getY() && posy <= dataSelector[i].getY() + dataSelector[i].getH()) {
        if (dataSelector[i].getCheck()) {
          dataSelector[i].setNotActive();
          pieChart[i].setNotDisplay();
        } 
        else {
          dataSelector[i].setActive();
          pieChart[i].setDisplay();
          id = i;
          println("data " + i + " is active");
        }
        //println("dataSelector["+i+"] is now" + (dataSelector[i].getCheck()?"active":"not active"));
        //id = i;
        break;
      }
    }
    if (id > -1 && displayMode == MULTI_COUNTRY_MODE) {
      for (int i=0;i<DATA_SET_COUNT;i++) {
        if (i != id) {
          dataSelector[i].setCheck(false);
        }
      }
      for (int i=1;i<DATA_SET_COUNT;i++) {
        pieChart[i].setNotDisplay();
      }
      pieChart[0].setDisplay();
      dataScale = 1;
    }
  }

  public void updateTotalSelector(float posx, float posy) {
    if ( posx >= totalSelector[0].getX() && posx<= totalSelector[0].getX()+totalSelector[0].getCBW()
      && posy >= totalSelector[0].getY() && posy <= totalSelector[0].getY()+totalSelector[0].getH()) {
      totalSelector[0].setCheck(true);
      totalSelector[1].setCheck(false);

      pieChartMode = REGION_MODE;
      updateDataForPieChart();
    }
    if ( posx >= totalSelector[1].getX() && posx<= totalSelector[1].getX() + totalSelector[1].getCBW()
      && posy >= totalSelector[1].getY() && posy <= totalSelector[1].getY()+totalSelector[1].getH()) {
      totalSelector[1].setCheck(true);
      totalSelector[0].setCheck(false);

      pieChartMode = WORLD_MODE;
      updateDataForPieChart();
    }
  }

  public void updateMapWindow(float posx, float posy) {
    mapWindow.update(posx, posy);
  }

  public void updateSearchWindow(float posx, float posy) {
    searchWindow.update(posx, posy);
  }

  public void updateHelpWindow(float posx, float posy) {
    helpWindow.update(posx, posy);
  }

  public void updateCountryDisplay(float posx, float posy) {
    countryList.updateCountryDisplay(posx, posy);
    updateDataForPieChart();
  }


  public MapWindow getMapWindow() {
    return mapWindow;
  }

  public SearchWindow getSearchWindow() {
    return searchWindow;
  }

  public HelpWindow getHelpWindow() {
    return helpWindow;
  }

  public CountryList getCountryList() {
    return countryList;
  }

  private void updateLegend() {
    for (int i=0;i<DATA_SET_COUNT;i++) {
      legend[i].update(i);
    }
  }

  public PieButton getPieButton() {
    return pieButton;
  }

  private void updateDataForPieChart() {

    if (displayMode == ONE_COUNTRY_MODE) {
      for (int i=0;i<DATA_SET_COUNT;i++) {
        float[] dataa = new float[2];
        dataa[0] = dataSets[i].getData().getFloat(currentRow[0], getPieButton().getYear()-yearMin);
        dataa[1] = dataSets[i].getData().getFloat(getRegion(currentRow[0]), getPieButton().getYear()-yearMin);
        //println(i+": " + dataa[0]+ "  " +dataa[1]);
        pieChart[i].updateData(dataa, 2, defaultColor[i], i);
        //println("update " + i + ": " + dataa[0] + " " + dataa[1] + ", from " + countryNames[currentRow[0]] + " of " + countryNames[getRegion(currentRow[0])]);
      }
    }
    if (displayMode == MULTI_COUNTRY_MODE) {
      float data[] = new float[DATA_SET_COUNT];
      int colour[] = new int[DATA_SET_COUNT];
      int whichData = 0;
      while (whichData<DATA_SET_COUNT && dataSelector[whichData].getCheck () == false) whichData++;
      if (whichData >=DATA_SET_COUNT) {
      }
      else {
        println("data: " + whichData);
        int cnt = 0;
        for (int j=0;j<DATA_SET_COUNT;j++) {
          if (currentDisplay[j] == true) {
            data[cnt] = dataSets[whichData].getData().getFloat(currentRow[j], getPieButton().getYear()-yearMin);
            colour[cnt] = colors[j]; 
            cnt++;
          }
        }
        println("cnt: " +cnt);
        pieChart[0].updateData(data, cnt, colour, 0);
        pieChart[0].setDisplay();
        //pieChart[0].updateData(data, colour);
      }
    } // end of multi mode
  } // end of updateDataForPieChart()

  public void updateTimeLineL(float posx) {
    timeLine.update(posx, 0);
  }
  public void updateTimeLineR(float posx) {
    timeLine.update(posx, 1);
  }
  public void updatePieButton(float posx, float posy) {
    pieButton.update(posx, posy);
    updateDataForPieChart();
  }

  public TimeLine getTimeLine() {
    return timeLine;
  }

  public CheckItem getDataSelector(int num) {
    return dataSelector[num];
  }
  
  public void updateDataMax(float currentY) {
    dataScale = dataScale * (plotY2 - dragStartY) / (plotY2 - currentY);
    println("dataScale: " + dataScale);
  }
  
  public boolean getShowTable() {
    return tableWindow.getIsDisplay();
  }
  
  public TableWindow getTable() {
    return tableWindow;
  }
  
  public void updateTable(float posx, float posy) {
    tableWindow.updateButton(posx, posy);
    tableWindow.updatePage(posx, posy);
  }
  
  public TableWindow getTableWindow() {
    return tableWindow;
  }
  
  public void updateEvent(float posx, float posy) {
    event.update(posx, posy);
  }
}

public void dashline(float x0, float y0, float x1, float y1, float[] spacing) 
{ 
  float distance = dist(x0, y0, x1, y1); 
  float[] xSpacing = new float[spacing.length]; 
  float[] ySpacing = new float[spacing.length]; 
  float drawn = 0.0f;  // amount of distance drawn 
 
  if (distance > 0) 
  { 
    int i; 
    boolean drawLine = true; // alternate between dashes and gaps 
 
    /* 
      Figure out x and y distances for each of the spacing values 
      I decided to trade memory for time; I'd rather allocate 
      a few dozen bytes than have to do a calculation every time 
      I draw. 
    */ 
    for (i = 0; i < spacing.length; i++) 
    { 
      xSpacing[i] = lerp(0, (x1 - x0), spacing[i] / distance); 
      ySpacing[i] = lerp(0, (y1 - y0), spacing[i] / distance); 
    } 
 
    i = 0; 
    while (drawn < distance) 
    { 
      if (drawLine) 
      { 
        line(x0, y0, x0 + xSpacing[i], y0 + ySpacing[i]); 
      } 
      x0 += xSpacing[i]; 
      y0 += ySpacing[i]; 
      /* Add distance "drawn" by this line or gap */ 
      drawn = drawn + mag(xSpacing[i], ySpacing[i]); 
      i = (i + 1) % spacing.length;  // cycle through array 
      drawLine = !drawLine;  // switch between dash and gap 
    } 
  } 
}
final int DATA_SET_COUNT = 7;


//about data sets
DataSets[] dataSets;
int rowCount;
int columnCount;
int[] years;
String[] countryNames;

//about PLOT and TABLE
float plotX1, plotY1;
float plotX2, plotY2;
float labelX, labelY;
float tableY1, tableY2;
int yearMin, yearMax;
int showYearMin, showYearMax;

int yearCount;
float unitWidth;
float unitHeight;

int displayNumber; // how many data sets are being displayed

//regions of the world
final int N_AME = 1;
final int S_AME = 2014;
final int AFRI = 2010;
final int EURO = 2012;
final int RUS = 1991;
final int ASIA = 1024;
final int MID = 10000000;

//about PIE CHART
final int DEFAULT_PIE_COLOR = 245;
final float PIE_CHART_X = Width - 130*scale;
final float PIE_CHART_Y = 80*scale;
final float PIE_CHART_R = 50*scale; // Diameter, not Radius
final float PIE_CHART_DIS = 55*scale;

PieChart[] pieChart;

int[][] defaultColor;

//about DASHLINE
final float DASH_LINE_SPACING1 = 3.0f*scale;
final float DASH_LINE_SPACING2 = 5.0f*scale;

//about visualization
PFont plotFont;
int[] colors; 

//about display modes
final int ONE_COUNTRY_MODE = 0;
final int MULTI_COUNTRY_MODE = 1;

//about year or decade mode
final int YEAR_MODE = 0;
final int DECADE_MODE = 1;

final int REGION_MODE = 0;
final int WORLD_MODE = 1;

//about coutrt list
final float LIST_X = 70*scale;
final float LIST_Y = 110*scale;
final float LIST_W = 180*scale;
final float LIST_LINE_H = 20*scale;
final int HOW_MANY_LINES_IN_A_PAGE = 12;
final float PAGE_BTN_DIFF = 10*scale;
final float PAGE_BTN_Y = LIST_Y + HOW_MANY_LINES_IN_A_PAGE * LIST_LINE_H * 0.5f - PAGE_BTN_DIFF * 0.5f; // make the button in the CENTER of the list
final float PAGE_BTN_X = LIST_X + LIST_W + 8*scale;
final float PAGE_BTN_W = 26*scale;

final float LEGEND_DIFF = 10*scale;

//about selectors
final float CHECK_BOX_WIDTH = 12*scale;
final float BUTTON_WIDTH = 100*scale;
final float CHECK_ITEM_HEIGHT = 12*scale;

final float COUNTRY_SELECTOR_X = 5*scale;
final float COUNTRY_SELECTOR_Y = Height-80*scale;

final float DATA_SELECTOR_X = 150*scale;
final float DATA_SELECTOR_Y = COUNTRY_SELECTOR_Y;
final float DATA_SELECTOR_Y_2 = DATA_SELECTOR_Y + CHECK_ITEM_HEIGHT + 3*scale;
final float DATA_SELECTOR_Y_3 = DATA_SELECTOR_Y_2 + CHECK_ITEM_HEIGHT + 3*scale;
final float DATA_SELECTOR_Y_4 = DATA_SELECTOR_Y_3 + CHECK_ITEM_HEIGHT + 3*scale;
final float DATA_SELECTOR_DIFF = 150*scale;

final float TOTAL_SELECTOR_X = Width-380*scale;
final float TOTAL_SELECTOR_Y = Height-50*scale;
//final float TOTAL_DIFF = 100*scale; //for different X
final float TOTAL_DIFF = CHECK_ITEM_HEIGHT+3*scale; //for different Y

float YEAR_SELECTOR_X;
final float YEAR_SELECTOR_Y = TOTAL_SELECTOR_Y;
final float YEAR_DIFF = 150*scale;

//about pie button
final float PIE_BUTTON_X = TOTAL_SELECTOR_X+120*scale;
final float PIE_BUTTON_Y = TOTAL_SELECTOR_Y;
final float PIE_BUTTON_H = PAGE_BTN_W;
final float PIE_BUTTON_DIFF = PAGE_BTN_DIFF;

//about windows and window buttons
final float SEARCH_WINDOW_X = 100*scale;
final float SEARCH_WINDOW_Y = 140*scale;
final float SEARCH_WINDOW_WIDTH = 500*scale;
final String SEARCH_WINDOW_CAPTION = "SEARCH A COUNTRY";
final float S_CAPTION_HEIGHT = 40*scale;
final float S_WINDOW_HEIGHT = 180*scale;

final float SEARCH_BUTTON_X = 0*scale;
final float SEARCH_BUTTON_Y = 150*scale;
final float SEARCH_BUTTON_LEN = 50*scale;

final float MAP_WINDOW_X = 100*scale;
final float MAP_WINDOW_Y = 100*scale;
final float MAP_WINDOW_WIDTH = 500*scale;
final String MAP_WINDOW_CAPTION = "SELECT A REGION";
final float M_CAPTION_HEIGHT = S_CAPTION_HEIGHT;
final float M_WINDOW_HEIGHT = MAP_WINDOW_WIDTH * (2234.0f/4500.0f);

final float MAP_BUTTON_X = SEARCH_BUTTON_X;
final float MAP_BUTTON_Y = SEARCH_BUTTON_Y + 70*scale;
final float MAP_BUTTON_LEN = SEARCH_BUTTON_LEN;

final float HELP_WINDOW_X1 = 550*scale;
final float HELP_WINDOW_X2 = 1000*scale;
final float HELP_WINDOW_Y1 = 70*scale;
final float HELP_WINDOW_Y2 = Height-150*scale;

final float HELP_BUTTON_X = SEARCH_BUTTON_X;
final float HELP_BUTTON_Y = SEARCH_BUTTON_Y + 140*scale;
final float HELP_BUTTON_LEN = SEARCH_BUTTON_LEN;

//about time line
final float TIME_LOCK_W = 5*scale;
final float TIME_LOCK_H = 5*scale;

//about keys on keyboard
final int ZHONG = 1024;
final int WRONG = 5858; 

final int BOTTOM_HEIGHT = 0;

//about tabular form
final float TABLE_BTN_X = 20*scale;
final float TABLE_BTN_Y = Height - 50*scale;
final float TABLE_BTN_LEN = 50*scale;

float TABLE_WINDOW_X;
final float TABLE_WINDOW_Y = 10*scale;
final float TABLE_W = 72*scale;
final float TABLE_H = 43*scale;

float TABLE_PAGE_BTN_X;
final float TABLE_PAGE_BTN_Y = TABLE_WINDOW_Y + TABLE_H * 3.5f;
final float TABLE_PAGE_BTN_H = PIE_BUTTON_H;
final float TABLE_PAGE_BTN_DIFF = PIE_BUTTON_DIFF;

//about event button
final float EVENT_BTN_X = TABLE_BTN_X + TABLE_BTN_LEN + 5*scale;
final float EVENT_BTN_Y = TABLE_BTN_Y;
final float EVENT_BTN_LEN = TABLE_BTN_LEN;



// colors
int COLOR_0 = 0xffff0000;
int COLOR_1 = 0xffff6600;
int COLOR_2 = 0xffFFC61E;
int COLOR_3 = 0xff00611c;
int COLOR_4 = 0xff01b2f1;
int COLOR_5 = 0xff0000ff;
int COLOR_6 = 0xffBB16F7;

String HelpContent = "Electric Avenue\nShi Yin\nComputer Science Department\nUniversity of Illinois at Chicago\n\n====\nmore information and how to use:\nhttp://joysword.com/projects/cs424p1/index.html";

public boolean isNansilafu(int i) {
  if (i == 67 || i == 79 || i == 89 || i == 97 || i == 59 || i == 66 || i == 92 || i == 86) {
    return true;
  }
  return false;
}
public boolean isCzechSlovakia(int i) {
  if (i == 61 || i == 65 || i == 88) {
    return true;
  }
  return false;
}
public boolean isGermany(int i) {
  if (i >= 69 && i <= 71) return true;
  return false;
}
public boolean isUSSR(int i) {
  if (i >= 96 && i <= 111) return true;
  return false;
}
/*void mousePressed() {
  if (ui.getTableWindow().getIsDisplay() == false) {
    if (mouseX >= ui.getTimeLine().getLeftLock().getX()-TIME_LOCK_W-8*scale && mouseX <= ui.getTimeLine().getLeftLock().getX()+TIME_LOCK_W+8*scale
      && mouseY >= ui.getTimeLine().getLeftLock().getY()-TIME_LOCK_H-8*scale && mouseY <= ui.getTimeLine().getLeftLock().getY()+TIME_LOCK_H+8*scale)
    {
      isDragLeft = true;
      //println(isDragLeft);
    } 
    else if (mouseX >= ui.getTimeLine().getRightLock().getX()-TIME_LOCK_W-8*scale && mouseX <= ui.getTimeLine().getRightLock().getX()+TIME_LOCK_W+8*scale
      && mouseY >= ui.getTimeLine().getRightLock().getY()-TIME_LOCK_H-8*scale && mouseY <= ui.getTimeLine().getRightLock().getY()+TIME_LOCK_H+8*scale)
    {
      isDragRight = true;
      //println(isDragRight);
    }
    if (mouseX > plotX1 && mouseX < plotX2 && mouseY > plotY1 && mouseY < plotY2) {
      isDrag = true;
      dragStartY = mouseY;
    }
  }
}

void mouseDragged() {
  if (isDragLeft) {
    ui.updateTimeLineL(mouseX);
    //println("update");
  }
  if (isDragRight) {
    ui.updateTimeLineR(mouseX);
    //println("update");
  }
}

void mouseReleased() {
  println("mouse: " + mouseX+" "+mouseY);

  isDragLeft = false;
  isDragRight = false;
  if (isDrag) {
    ui.updateDataMax(mouseY);
    isDrag = false;
  }
  //
  ui.updateCountryDisplay(mouseX, mouseY);

  //
  ui.updateCountryList(mouseX, mouseY);

  //
  ui.updateCountrySelector(mouseX, mouseY);

  //
  ui.updateDataSelector(mouseX, mouseY);

  //
  ui.updateTotalSelector(mouseX, mouseY);

  //
  ui.updateYearSelector(mouseX, mouseY);

  //
  ui.updatePieButton(mouseX, mouseY);

  //
  ui.updateMapWindow(mouseX, mouseY);

  //
  ui.updateSearchWindow(mouseX, mouseY);

  //
  ui.updateHelpWindow(mouseX, mouseY);

  //
  for (int i=0;i<DATA_SET_COUNT;i++) {
    legend[i].click(mouseX, mouseY, i);
  }

  //
  ui.updateTable(mouseX, mouseY);
  
  //
  ui.updateEvent(mouseX, mouseY);
}*/

public void touchDown(int ID, float xPos, float yPos, float xWidth, float yWidth) {
  if (ui.getTableWindow().getIsDisplay() == false) {
    if (xPos >= ui.getTimeLine().getLeftLock().getX()-TIME_LOCK_W-8*scale && xPos <= ui.getTimeLine().getLeftLock().getX()+TIME_LOCK_W+8*scale
      && yPos >= ui.getTimeLine().getLeftLock().getY()-TIME_LOCK_H-8*scale && yPos <= ui.getTimeLine().getLeftLock().getY()+TIME_LOCK_H+8*scale)
    {
      isDragLeft = true;
      //println(isDragLeft);
    } 
    else if (xPos >= ui.getTimeLine().getRightLock().getX()-TIME_LOCK_W-8*scale && xPos <= ui.getTimeLine().getRightLock().getX()+TIME_LOCK_W+8*scale
      && yPos >= ui.getTimeLine().getRightLock().getY()-TIME_LOCK_H-8*scale && yPos <= ui.getTimeLine().getRightLock().getY()+TIME_LOCK_H+8*scale)
    {
      isDragRight = true;
      //println(isDragRight);
    }
    if (xPos > plotX1 && xPos < plotX2 && yPos > plotY1 && yPos < plotY2) {
      isDrag = true;
      dragStartY = yPos;
    }
  }
}

public void touchUp(int ID, float xPos, float yPos, float xWidth, float yWidth){
  noFill();
  stroke(255,0,0);
  ellipse( xPos, yPos, xWidth * 2, yWidth * 2 );
  
  println("touch: " + xPos+" "+yPos);

  isDragLeft = false;
  isDragRight = false;
  if (isDrag) {
    ui.updateDataMax(yPos);
    isDrag = false;
  }
  //
  ui.updateCountryDisplay(xPos, yPos);

  //
  ui.updateCountryList(xPos, yPos);

  //
  ui.updateCountrySelector(xPos, yPos);

  //
  ui.updateDataSelector(xPos, yPos);

  //
  ui.updateTotalSelector(xPos, yPos);

  //
  ui.updateYearSelector(xPos, yPos);

  //
  ui.updatePieButton(xPos, yPos);

  //
  ui.updateMapWindow(xPos, yPos);

  //
  ui.updateSearchWindow(xPos, yPos);

  //
  ui.updateHelpWindow(xPos, yPos);

  //
  for (int i=0;i<DATA_SET_COUNT;i++) {
    legend[i].click(xPos, yPos, i);
  }

  //
  ui.updateTable(xPos, yPos);
  
  //
  ui.updateEvent(xPos, yPos);
}

public void touchMove(int ID, float xPos, float yPos, float xWidth, float yWidth){
  if (isDragLeft) {
    ui.updateTimeLineL(xPos);
    //println("update");
  }
  if (isDragRight) {
    ui.updateTimeLineR(xPos);
    //println("update");
  }
}// touchMove

  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "Shi424P1" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
