import processing.core.*; 
import processing.xml.*; 

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 Ue2_Konrad_markus extends PApplet {

// 1. Test der "Reaktionsgeschw." (5x -> Durchschnitt)
// 2. Test der versch. Eigenschaften (einfach)
// 3. Test der versch. Eigenschaften (mit Distraktoren)
// 4. Test der versch. Eigenschaften (kombiniert)
// 5. Anzeige der Ergebnisse


final int APP_STATE_START = 0;
final int APP_STATE_TEST_0 = 1;
final int APP_STATE_TEST_1 = 2;
final int APP_STATE_TEST_2 = 3;
final int APP_STATE_TEST_3 = 4;
final int APP_STATE_TEST_4 = 5;
final int APP_STATE_TEST_5 = 6;
final int APP_STATE_TEST_6 = 7;
final int APP_STATE_TEST_7 = 8;
final int APP_STATE_TEST_8 = 9;
final int APP_STATE_TEST_9 = 10;
final int APP_STATE_RESULTS = 11;

final int SCREEN_W = 800;
final int SCREEN_H = 600;

final boolean DEBUG_VIS = false;

int appState;
PFont fontHeader;

AppTest appTest;
AppResults appResults;

ArrayList results = new ArrayList();  //List with testNum -> Result mapping
ArrayList testTitles = new ArrayList(); // List with Strings

public void setup() {
  if (!DEBUG_VIS)
    appState = APP_STATE_TEST_0;
  else
    appState = APP_STATE_RESULTS;
    
  if (appState == APP_STATE_START) {
    appState = APP_STATE_TEST_0;
  }  
    
  smooth();
  size(SCREEN_W, SCREEN_H);
  stroke(255);
  background(0, 0, 0);
  
  fontHeader = loadFont("SansSerif-28.vlw");
  
  appTest = new AppTest();
  appResults = new AppResults();
  
  // fill the results with empty values
  for (int i = 0; i < appTest.testMax; i++) {
    Result res = new Result();
    res.testNum = i;
    
    if (DEBUG_VIS) {  // fill with random values for debugging
      if (i < 5) {
        res.reactionTime = random(0.5f, 0.9f);
      } else {
        res.reactionTime = random(0.1f, 2.5f);
      }
    }
    
    results.add(res);
  }
  
  // set test titles
  testTitles.add("Einfacher Reaktionst.");
  testTitles.add("Farbe");
  testTitles.add("Form");
  testTitles.add("Gr\u00f6\u00dfe");
  testTitles.add("Distr.vielfalt I");
  testTitles.add("Distr.vielfalt II");
  testTitles.add("Distr.vielfalt III");
  testTitles.add("Zusammenges. Eig. I");
  testTitles.add("Zusammenges. Eig. II");
  testTitles.add("Zusammenges. Eig. III");
} 

public void draw() {
  background(0, 0, 0);
  showHeader();
  updateMain();
  showMain();
  showFooter();
}

public void showHeader() {
  fill(255);
  String title = "";
  
  switch (appState) {
    default:
      title = (String)testTitles.get(appState - 1);
    break;
    
    case APP_STATE_RESULTS:
      title = "Individuelle Resultate";
    break;
  }
  
  textFont(fontHeader, 28);
  textAlign(LEFT);
  text(title, 20, 50);
}

public void updateMain() {
  if (appState != APP_STATE_RESULTS) {
    appTest.update();
  } else {
    appResults.update();
  }
}

public void showMain() {
  if (appState != APP_STATE_RESULTS) {
    appTest.display();
  } else {
    appResults.display();
  }
}

public void showFooter() {
  
}
class AppResults {
  boolean firstCall = true;

  int testTypeCount = 10;
  int testsPerType = 5;

  ArrayList resultData; // ArrayList with ArrayList of data for each test type
  ArrayList resultDataMax; // ArrayList of Floats with maxium for each test type
  ArrayList resultDataMin; // ArrayList of Floats with minimum for each test type
  ArrayList resultDataAvg; // ArrayList of Floats with average for each test type

  float absResultDataMax = Float.MIN_VALUE;
  float avgReactionTime = 0.0f;

  PFont fontHeaderText;
  PFont fontVisValues;

  TextButton prevButton;

  AppResults() {
    fontHeaderText = loadFont("SansSerif-12.vlw");
    fontVisValues = loadFont("SansSerif-9.vlw");

    prevButton = new TextButton(370, 570, 60, 20, "OK", color(180), color(220), true);
  }

  public void display() {    
    int w = 125;
    int h = 100;
    int bX = 25;
    int bY = 100;

    textFont(fontHeaderText, 12);
    textAlign(LEFT);
    fill(200);
    text("Bei allen Tests nach dem einfachen Reaktionstest wurde die durchschn. Reaktionsgeschw. von " + nf(avgReactionTime, 1, 2) + "Sek. abgezogen", 25, 100);
    text("Alle Angaben sind in Sekunden.", 25, 113);

    for (int i = 0; i < testTypeCount; i++) {
      int row = i / testsPerType;
      int col = i % testsPerType;

      int x = bX + col * (w + bX);
      int y = 140 + row * (h + bY);

      textFont(fontHeaderText, 12);
      textAlign(LEFT);
      fill(200);
      text((String)testTitles.get(i), x, y);

      displayVisualizationData(i, x, y + h/2 + 25, w, h);

      //      rectMode(CORNER);
      //      rect(bX + col * (w + bX), 100 + row * (h + bY), w, h);
    }

    //    displayVisualizationData(deviationData, 50, 300, 600, 300, max(abs(deviationDataMax), abs(deviationDataMin)), 1.0f);
    //    displayMinAvgMax(680, 305, deviationDataMin, avgValue, avgDeviation, deviationDataMax);

    prevButton.display();

    /*    textFont(fontHeaderText, 20);
     textAlign(LEFT);
     fill(200);
     text("Lineare Abweichung:", 50, 375);
     
     displayVisualizationData(linDeviationData, 50, 450, 600, 100, 2.0f * max(abs(linDeviationDataMax), abs(linDeviationDataMin)), 1.0f);
     displayMinAvgMax(680, 455, linDeviationDataMin, avgLinDeviation, linDeviationDataMax);*/
  }

  public void displayVisualizationData(int dataId, int xOffset, int yOffset, int visWidth, int visHeight) {
    // get data
    if (resultData == null) return;
    
    ArrayList testData = (ArrayList)resultData.get(dataId);
    float testMax = ((Float)resultDataMax.get(dataId)).floatValue();
    float testMin = ((Float)resultDataMin.get(dataId)).floatValue();
    float testAvg = ((Float)resultDataAvg.get(dataId)).floatValue();

    stroke(100);

    // init values
    int barBorder = 2;
    int barWidth = visWidth / testData.size() - 2 * barBorder;
    float barHeightScale = 0.9f * visHeight / absResultDataMax;

    // make rect around whole diagram
    color(255);
    fill(50);
    rectMode(CORNER);
    stroke(180);
    rect(xOffset, yOffset - visHeight * 0.5f, visWidth, visHeight);

    // display avg line
    int yAvg = yOffset + visHeight / 2 - (int)(testAvg * barHeightScale);
    stroke(200, 200, 0);
    line(xOffset, yAvg, xOffset + visWidth, yAvg);

    // display description
    //    fill(200);
    //    textFont(fontVisValues, 9);
    //    textAlign(LEFT);
    //    text("Versuch", xOffset + visWidth + 10, yOffset - visHeight/2 - 5);
    //
    //    fill(200);
    //    textFont(fontVisValues, 9);
    //    textAlign(LEFT);
    //    text("Erg.", xOffset + visWidth + 10, yOffset + visHeight/2 + 17);

    // display data
    int xBarOffset = 0;

    for (int i = 0; i < testData.size(); i++) {
      float v = ((Float)testData.get(i)).floatValue();

      // display bar
      float c = v / absResultDataMax;
      fill(200 * c, 200 * (1-c), 0);  

      rectMode(CORNER);
      rect(xOffset + xBarOffset + barBorder, yOffset + visHeight / 2, barWidth, -v * barHeightScale);

      // display test number and value
      fill(200);
      textFont(fontVisValues, 9);
      textAlign(CENTER);
      text((i+1), xOffset + xBarOffset + barBorder + barWidth / 2, yOffset - visHeight/2 - 5);
      text(nf(v, 1, 2), xOffset + xBarOffset + barBorder + barWidth / 2, yOffset + visHeight/2 + 17);

      xBarOffset += barWidth + 2 * barBorder;
    }
    
    displayMinAvgMax(xOffset, yOffset + visHeight / 2 + 30, testMin, testAvg, testMax);
  }

  public void displayMinAvgMax(int xPos, int yPos, float min, float avg, float max) {
     fill(200);    
     textFont(fontVisValues, 9);
     textAlign(LEFT);
     text("min: " + nf(min, 1, 2), xPos, yPos);
     text("avg: " + nf(avg, 1, 2), xPos, yPos + 12);
     text("max: " + nf(max, 1, 2), xPos, yPos + 24);
  }

  public void update() {
    if (firstCall) {
      buildVisualizationData();
      firstCall = false;
    }

    prevButton.update();

    if (prevButton.pressed()) {
      exit();
    }
  }

  public void buildVisualizationData() {
    resultData = new ArrayList();
    resultDataMax = new ArrayList();
    resultDataMin = new ArrayList();
    resultDataAvg = new ArrayList();
    
    for (int i = 0; i < testTypeCount; i++) {  // for each test type
      ArrayList resultsForTest = new ArrayList();
      int testNumBegin = i * testsPerType;
      float resAvg = 0.0f;
      float resMax = Float.MIN_VALUE;
      float resMin = Float.MAX_VALUE;

      for (int testNum = testNumBegin; testNum < testNumBegin + testsPerType; testNum++) {
        float testRes = ((Result)results.get(testNum)).reactionTime;
        if (testNumBegin > 0) testRes -= avgReactionTime;
        resAvg += testRes / (float)testsPerType;
        if (testRes > resMax) resMax = testRes;
        if (testRes < resMin) resMin = testRes;
        resultsForTest.add(new Float(testRes));
      }
      
      if (testNumBegin == 0) {
        avgReactionTime = resAvg;
        println(avgReactionTime);
      }
      
      if (resMax > absResultDataMax) absResultDataMax = resMax;

      resultData.add(resultsForTest);
      resultDataMax.add(new Float(resMax));
      resultDataMin.add(new Float(resMin));
      resultDataAvg.add(new Float(resAvg));
    }
  }

  //  float roundByValue(float v, float r) {
  //    return round(v * r) / r;
  //  }
  //
  //  String formatValue1(float v) {
  //    DecimalFormat df =   new DecimalFormat("0.0");
  //
  //    return df.format(v);
  //  }
  //
  //  String formatValue2(float v) {
  //    DecimalFormat df =   new DecimalFormat("0.0#");
  //
  //    return df.format(v);
  //  }
}

class AppTest {
  int testNum;
  int testMax;

  PFont fontHeaderText;

  int testTime;       // in millis
  int testStartTime;  // in millis

  float avgReactionTime;

  int testAreaSizeX = 600;
  int testAreaSizeY = 400;
  int fullAreaCenterX = SCREEN_W / 2;
  int areaCenterY = SCREEN_H / 2 + 50;

  boolean testIsRunning = false;

  RectButton testArea;

  Button objectToClick;
  int objColor;
  int distrColor;
  int prevObjX;
  int prevObjY;
  ArrayList distractorObjects;

  TextButton infoOkButton;

  //TriangleButton prevButton;
  TriangleButton nextButton;

  AppTest() {
    testNum = 0;
    testMax = 50;  // 10 testtypes with 5 tests

    fontHeaderText = loadFont("SansSerif-20.vlw");

    //    testArea = new RectButton(fullAreaCenterX, areaCenterY, testAreaSizeX, testAreaSizeY, color(40), color(40), false);

    infoOkButton = new TextButton(400, 530, 50, 30, "Ok", color(40), color(60), true);

    nextButton = new TriangleButton(690, 550, 30, 50, 270, color(180), color(220), true);

    distractorObjects = new ArrayList();

    if (!DEBUG_VIS) {
      initValues();
    }
  }

  //--------------------------------------------//
  // main display functions //
  //--------------------------------------------//

  public void display() {
    fill(200);
    textAlign(LEFT);
    textFont(fontHeaderText, 20);
    text("Test #" + (testNum + 1) + "/" + testMax + ":", 20, 80);

    if (testIsRunning) {
      displayTest();
    } 
    else {
      displayInfo();
    }

    displayFooter();
  }

  public void displayFooter() {
    //    if (testType == 0 && userShapeSize > 0) {
    //      nextButton.display();
    //    }
  }

  //--------------------------------------------//
  // update functions //
  //--------------------------------------------//

  public void initValues() {
    // set the test type
    appState = (testNum+1) / 5 + 1;

    //    // reset other values
    //    testIsRunning = false;

    // init things for this test
    distractorObjects.clear();

    switch (appState) {
    case APP_STATE_TEST_0:
      {
        int randX = (int)random(0, testAreaSizeX - 100) + 100;
        int randY = (int)random(0, testAreaSizeY - 100) + 150;
        objectToClick = new RectButton(randX, randY, 50, 50, color(180, 0, 0), color(180, 0, 0), false);
        break;
      }
    case APP_STATE_TEST_1: 
    case APP_STATE_TEST_2:
    case APP_STATE_TEST_3:
    case APP_STATE_TEST_4:
    case APP_STATE_TEST_5:
    case APP_STATE_TEST_6:
    case APP_STATE_TEST_7:
    case APP_STATE_TEST_8:
    case APP_STATE_TEST_9:
      {
        int objSize = 50;
        int distrSize = 50;
        //        color distrColor = color(180);
        int thisDistrColor = distrColor;
        int thisObjColor = objColor;
        int numDistr = 10 + 5 * (testNum - 4);

        if (appState == APP_STATE_TEST_2) {
          objSize = 50;
          numDistr = 10 + 5 * (testNum - 9);
          thisDistrColor = distrColor = color(180);
          thisObjColor = distrColor;
        } 
        else if (appState == APP_STATE_TEST_3 || appState == APP_STATE_TEST_6) {
          numDistr = 15;

          if (appState == APP_STATE_TEST_3) {
            objSize = 50 + (20 - testNum) * 5;
            thisDistrColor = distrColor = color(180);
            thisObjColor = distrColor;
          } 
          else {
            if ((int)random(2) == 1) {
              thisObjColor = thisDistrColor;
            } else {
              thisObjColor = objColor;
            }
            
            objSize = 50 + (38 - testNum) * 5;
          }
        } 
        else if (appState == APP_STATE_TEST_4 || appState == APP_STATE_TEST_5) {
          objSize = 50;
          numDistr = 15;
        }

        if (appState == APP_STATE_TEST_1 || appState == APP_STATE_TEST_4 || appState == APP_STATE_TEST_5) {
          objectToClick = new RectButton(0, 0, objSize, objSize, thisObjColor, thisObjColor, false);
        } 
        else if (appState == APP_STATE_TEST_2 || appState == APP_STATE_TEST_3 || appState == APP_STATE_TEST_6) {
          objectToClick = new CircleButton(0, 0, objSize, thisObjColor, thisObjColor, false);
        }
        else if (appState == APP_STATE_TEST_7 || appState == APP_STATE_TEST_8 || appState == APP_STATE_TEST_9) {
          numDistr = 15;
        }

println("APPSTATE: " + appState);
println("thisDistrColor: " + thisDistrColor);

        int cols = 5;
        int rows = numDistr / cols + 1;
        int fieldW = testAreaSizeX / cols;
        int fieldH = testAreaSizeY / rows;

        int targetRow = (int)random(rows);
        int targetCol = (int)random(cols);

        Button distractor = null;
        for (int row = 0; row < rows; row++) {
          for (int col = 0; col < cols; col++) {
            int randX = col * fieldW + (int)random(0, fieldW - objSize / 2) + 100;
            int randY = row * fieldH + (int)random(0, fieldH - objSize / 2) + 150;

            if (targetRow == row && targetCol == col) {
              objectToClick.x = randX;
              objectToClick.y = randY;
            } 
            else {
              if (appState == APP_STATE_TEST_1) {
                distractor = new RectButton(randX, randY, distrSize, distrSize, thisDistrColor, color(0), false);
              } 
              
              if (appState == APP_STATE_TEST_3) {
                distractor = new CircleButton(randX, randY, distrSize, thisDistrColor, color(0), false);
              } 
              else {
                if (appState == APP_STATE_TEST_4) {
                  thisDistrColor = randColor(thisObjColor);
                } 
                else if (appState == APP_STATE_TEST_5 || appState == APP_STATE_TEST_6  || appState == APP_STATE_TEST_7  || appState == APP_STATE_TEST_8  || appState == APP_STATE_TEST_9) {
                  if ((int)random(2) == 1) {
                    thisDistrColor = objColor;
                  } 
                  else {
                    thisDistrColor = distrColor;
                  }
                }

                int distrShape = 0;
                float distrAngle = 0.0f;
                int randW = 0;
                int randH = 0;

                if (appState == APP_STATE_TEST_5 || appState == APP_STATE_TEST_6  || appState == APP_STATE_TEST_7  || appState == APP_STATE_TEST_8  || appState == APP_STATE_TEST_9) {
                  distrShape = (int)random(3);
                  distrAngle = random(360.0f);
                  randW = (int)random(-distrSize / 2, distrSize / 2);
                  randH = (int)random(-distrSize / 2, distrSize / 2);
                }

                switch (distrShape) {
                default:
                case 0:
                  distractor = new RectButton(randX, randY, distrSize + randW, distrSize + randH, thisDistrColor, color(0), false);
                  break;
                case 1:
                  distractor = new CircleButton(randX, randY, distrSize + randW, distrSize + randH, thisDistrColor, color(0), false);
                  break;
                case 2:
                  distractor = new TriangleButton(randX, randY, distrSize + randW, distrSize + randH, distrAngle, thisDistrColor, color(0), false);
                  break;
                }
              }

              println("Added distractor: " + distractor);
              println("With color: " + distractor.basecolor);
              
              distractorObjects.add(distractor);
            }
          }
        }

        break;
      }
    }
  }

  public void resetTimers() {
    // reset timers
    testStartTime = millis();
    testTime = 0;
  }

  public void update() {
    if (testNum >= testMax) {
      appState = APP_STATE_RESULTS;
      testNum = 0;
      initValues();
      return;
    }

    if (testIsRunning) {
      updateTest();
    } 
    else {
      updateInfo();
    }

    updateFooter();
  }

  public void updateFooter() {
  }

  //--------------------------------------------//
  // result handling //
  //--------------------------------------------//

  public void saveResults() {
    // update Result object
    Result res = (Result)results.get(testNum);
    res.reactionTime = (float)testTime / 1000.0f;

    if (testNum == 4) {
      testIsRunning = false;
      avgReactionTime = 0.0f;
      for (int i = 0; i < (testNum + 1); i++) {
        Result pastRes = (Result)results.get(i);
        avgReactionTime += pastRes.reactionTime / (testNum+1);
      }

      distrColor = randColor();
      objColor = randColor(distrColor);
    } 
    else if ((testNum+1) % 5 == 0) {
      testIsRunning = false;
      distrColor = randColor();
      objColor = randColor(distrColor);

      if (appState >= APP_STATE_TEST_6) {
        int objSize = 50;
        int objShape = (int)random(3);
        int objRandW = 0;
        int objRandH = 0;
        int objAngle = (int)random(360);

        objRandW = (int)random(-objSize / 2, objSize / 2);
        objRandH = (int)random(-objSize / 2, objSize / 2);

        switch (objShape) {
        default:
        case 0:
          objectToClick = new RectButton(0, 0, objSize + objRandW, objSize + objRandH, objColor, objColor, false);
          break;
        case 1:
          objectToClick = new CircleButton(0, 0, objSize + objRandW, objSize + objRandH, objColor, objColor, false);
          break;
        case 2:
          objectToClick = new TriangleButton(0, 0, objSize + objRandW, objSize + objRandH, objAngle, objColor, objColor, false);
          break;
        }
      }
    }

    // reset values
    initValues();    
    resetTimers();
    testNum++;

    println("Saved result:" + res);
  }

  //--------------------------------------------//
  // info functions //
  //--------------------------------------------//

  public void displayInfo() {    
    ArrayList infoText = new ArrayList();
    //objectToClick = null;

    objectToClick.x = 400;
    objectToClick.y = 450;

    boolean dontDisplayObject = false;

    if (appState == APP_STATE_TEST_1) {
      infoText.add("Ihre durchschnittl. Reaktionsgeschwindigkeit ist " + nf(avgReactionTime, 1, 2) + "s");
    }

    if (appState == APP_STATE_TEST_3 || appState == APP_STATE_TEST_6) {
      infoText.add("Klicken Sie schnellstm\u00f6glich auf den gr\u00f6\u00dften Kreis!");
      dontDisplayObject = true;
    }

    if (!dontDisplayObject) {
      infoText.add("Klicken Sie jetzt im n\u00e4chsten Test so schnell wie m\u00f6glich auf folgendes Objekt:");
      objectToClick.display();
    }

    // help text
    fill(200);
    textFont(fontHeaderText, 20);
    textAlign(CENTER);

    for (int i = 0; i < infoText.size(); i++) {
      text((String)infoText.get(i), 400, 330 + i * 30);
    }

    // ok button
    infoOkButton.display();
  }

  public void updateInfo() {
    infoOkButton.update();

    if (infoOkButton.pressed()) {
      resetTimers();
      testIsRunning = true;
      objectToClick.x = (int)random(testAreaSizeX - 100) + 100;
      objectToClick.y = (int)random(testAreaSizeY - 100) + 150;
    }
  }

  //--------------------------------------------//
  // test functions //
  //--------------------------------------------//

  public void displayTest() {
    // display timer    
    fill(200);
    textFont(fontHeaderText, 20);
    textAlign(CENTER);
    textAlign(RIGHT);
    text(nf((float)testTime / 1000.0f, 1, 2) + " s", 700, 80);

    // show "experiment area"
    //testArea.display();

    // show content depending on the app state
    if (appState > APP_STATE_TEST_0) {
      for (int i = 0; i < distractorObjects.size(); i++) {        
        ((Button)distractorObjects.get(i)).display();
      }
    }

    // show the object that should be found
    objectToClick.display();
  }

  public void updateTest() {
    // calculate timing
    testTime = millis() - testStartTime;

    // manage user interaction
    objectToClick.update();

    if (objectToClick.pressed()) {
      saveResults();
    }
  }

  //--------------------------------------------//
  // helper //
  //--------------------------------------------//

  public int randColor() {
    return color((int)random(55, 255), (int)random(55, 255), (int)random(55, 255));
  }

  public int randColor(int otherThan) {
    int c;
    float d;

    do {
      c = randColor();
      d = colorDist(c, otherThan);
    } 
    while(d < 30000);

    return c;
  }

  public float colorDist(int c1, int c2) {
    float dR = red(c1) - red(c2);
    float dG = green(c1) - green(c2);
    float dB = blue(c1) - blue(c2);

    return dR * dR + dG * dG + dB * dB;
  }
}

abstract class Button
{
  int x, y;
  int sizeW;
  int sizeH;
  int basecolor, highlightcolor;
  int currentcolor;
  boolean over = false;
  boolean pressed = false;
  int lastClick = 0;
  int clickLockTime = 0;  

  public void update() 
  {
    if(over()) {
      currentcolor = highlightcolor;
    } 
    else {
      currentcolor = basecolor;
    }
  }
  
  public abstract void display();

  public boolean pressed() 
  {
    if (clickLockTime > 0) {
      boolean pressed = (millis() - lastClick > clickLockTime && mousePressed && over);
      if (pressed)
        lastClick = millis();
      
      return pressed;
    }
    
    return (mousePressed && over);   
  }

  public boolean over() 
  { 
    return true; 
  }

  public boolean overRect(int x, int y, int width, int height) 
  {
    if (mouseX >= x && mouseX <= x+width && 
      mouseY >= y && mouseY <= y+height) {
      return true;
    } 
    else {
      return false;
    }
  }

  public boolean overCircle(int x, int y, int diameter) 
  {
    float disX = x - mouseX;
    float disY = y - mouseY;
    if(sqrt(sq(disX) + sq(disY)) < diameter/2 ) {
      return true;
    } 
    else {
      return false;
    }
  }

}

class CircleButton extends Button
{
  CircleButton(int ix, int iy, int isize, int icolor, int ihighlight, boolean clickLock) 
  {
    this(ix, iy, isize, isize, icolor, ihighlight, clickLock);
  }
  
  CircleButton(int ix, int iy, int isizeW, int isizeH, int icolor, int ihighlight, boolean clickLock) 
  {
    x = ix;
    y = iy;
    sizeW = isizeW;
    sizeH = isizeH;
    basecolor = icolor;
    highlightcolor = ihighlight;
    currentcolor = basecolor;
    
    if (clickLock)
      clickLockTime = 500;
    else 
      clickLockTime = 0;
  }

  public boolean over() 
  {
    if( overCircle(x, y, sizeW) ) {
      over = true;
      return true;
    } 
    else {
      over = false;
      return false;
    }
  }

  public void display() 
  {
    //stroke(255);
    noStroke();
    fill(currentcolor);
    ellipse(x, y, sizeW, sizeH);
  }
}

class RectButton extends Button
{
  RectButton(int ix, int iy, int isizeW, int isizeH, int icolor, int ihighlight, boolean clickLock) 
  {
    x = ix;
    y = iy;
    sizeW = isizeW;
    sizeH = isizeH;
    basecolor = icolor;
    highlightcolor = ihighlight;
    currentcolor = basecolor;
    
    if (clickLock)
      clickLockTime = 500;
    else 
      clickLockTime = 0;
  }

  public boolean over() 
  {
    if( overRect(x-sizeW/2, y-sizeW/2, sizeW, sizeW) ) {
      over = true;
      return true;
    } 
    else {
      over = false;
      return false;
    }
  }

  public void display() 
  {
    //stroke(255);
    noStroke();
    fill(currentcolor);
    rectMode(CENTER);
    rect(x, y, sizeW, sizeH);
  }
}

class TriangleButton extends Button
{
  float angle;
  
  TriangleButton(int ix, int iy, int isizeW, int isizeH, float iangle, int icolor, int ihighlight, boolean clickLock) 
  {
    x = ix;
    y = iy;
    sizeW = isizeW;
    sizeH = isizeH;
    angle = radians(iangle);
    basecolor = icolor;
    highlightcolor = ihighlight;
    currentcolor = basecolor;
    
    if (clickLock)
      clickLockTime = 500;
    else 
      clickLockTime = 0;
  }

  public boolean over() 
  {
    if( overRect(x-sizeW/2, y-sizeH/2, sizeW, sizeH) ) {
      over = true;
      return true;
    } 
    else {
      over = false;
      return false;
    }
  }

  public void display() 
  {
    //shapeMode(CENTER);
    pushMatrix();
    //stroke(255);
    noStroke();
    fill(currentcolor);
    translate(x, y);
    rotate(angle);
    triangle(-sizeW/2, -sizeH/2, sizeW/2.5f, -sizeH/2, 0, sizeH/2);
    popMatrix();
  }
}

class TextButton extends Button
{
  PFont labelFont;
  String label;
  
  TextButton(int ix, int iy, int isizeW, int isizeH, String ilabel, int icolor, int ihighlight, boolean clickLock) 
  {
    x = ix;
    y = iy;
    sizeW = isizeW;
    sizeH = isizeH;
    label = ilabel;
    basecolor = icolor;
    highlightcolor = ihighlight;
    currentcolor = basecolor;
    
    labelFont = loadFont("SansSerif-14.vlw");
    
    if (clickLock)
      clickLockTime = 500;
    else 
      clickLockTime = 0;
  }

  public boolean over() 
  {
    if( overRect(x-sizeW/2, y-sizeH/2, sizeW, sizeH) ) {
      over = true;
      return true;
    } 
    else {
      over = false;
      return false;
    }
  }

  public void display() 
  {
    stroke(255);
    fill(currentcolor);
    rectMode(CENTER);
    rect(x, y, sizeW, sizeH);
    fill(255);
    textFont(labelFont, 14);
    textAlign(CENTER);
    text(label, x, y + 5);
  }
}
class Result {
  int testNum = 0;
  int testType = 0;
  float reactionTime = 0.0f;
  
  public String toString() {
    return "Result for Test#" + testNum + ":\n" +
      " testType: " + testType + "\n" +
      " reactionTime: " + reactionTime;
  }
}
  static public void main(String args[]) {
    PApplet.main(new String[] { "--bgcolor=#FFFFFF", "Ue2_Konrad_markus" });
  }
}
