/*
 * SizesMoA.java
 * 
 * Copyright (C) 2005-2012 Huseyin Boyaci. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version. This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. 
 * 
 * See the GNU General Public License version 2 for more details 
 * (a copy is included in the LICENSE file that accompanied this code) 
 * (also available at http://www.gnu.org) You should have received a copy of 
 * the GNU General Public License along with this program; if not, write to 
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
 * Boston, MA 02111-1307, USA.
 * 
 */

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.imageio.ImageIO;

import psychWithJava.FullScreen;
import psychWithJava.Extras;

/**
 * Implements a simple "method of adjustment" experiment.
 *
 * In this experiment we investigate the perceived image size
 * in a complex environment. In each trial observer is presented
 * a sphere either in FRONT or BACK position and asked to adjust the
 * size of a 2D disk to match the test object in image size.
 *  
 * @author Huseyin Boyaci
 * @since 2012-03-14
 * @see FullScreen
 */
public class SizesMoA extends FullScreen implements Runnable {

  // Constant values:
  static final int[] SIZE = {123, 163, 204, 244, 286};
  static final String[] SCENE = {"Back", "Front"};
  static final int WIDTH = 800;
  static final int HEIGHT = 600;
  static final int N_BACK = SIZE.length;
  static final int SIZE_STEP_PROBE = 4;
  static final int SIZE_PROBE_MAX = 400;
  static final int SIZE_PROBE_MIN = 80;
  static final int N_TRIAL = 5;
 
  BufferedImage probe;
  
  static PrintWriter outputFile;
  
  public static void main(String[] args) {

    // Open the output file for saving the data
    try {
      outputFile = Extras.setOutputFile("data");
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    }
    // Initiate the full-screen mode
    SizesMoA fs = new SizesMoA();
    fs.setNBuffers(1);
    fs.createProbe();
    new Thread(fs).start();
  }  
  
  public void createProbe(){
  
    probe = this.getGraphicsConfiguration().
      createCompatibleImage(SIZE_PROBE_MAX, SIZE_PROBE_MAX);
    
    RenderingHints hints = new RenderingHints(null);
    //hints.put(RenderingHints.KEY_ANTIALIASING, 
    //    RenderingHints.VALUE_ANTIALIAS_ON);
    hints.put(RenderingHints.KEY_RENDERING,
        RenderingHints.VALUE_RENDER_QUALITY);
    Graphics2D gProbe = probe.createGraphics();
    gProbe.setRenderingHints(hints);
    gProbe.dispose();
  }
  
  public void updateProbe(int size){
    
    int shift = (SIZE_PROBE_MAX - size)/2;
    Graphics2D gProbe = probe.createGraphics();
    gProbe.setColor(Color.black);
    gProbe.fillRect(0, 0, SIZE_PROBE_MAX, SIZE_PROBE_MAX);
    gProbe.setColor(Color.GRAY);
    gProbe.fillOval(shift, shift, size, size);
    gProbe.dispose();
  }
  
  public void run(){
    
    // Display the task and wait for observer's response
    setFont(new Font("SansSerif", Font.BOLD, 26));
    displayText(100, 200, "Task:");
    displayText(100, 300, "  Adjust the size of the 2-D probe");
    displayText(100, 350, "  to match the size of the 3-D ball");    
    displayText(100, 450,
    "  (Up/Down for fine adjustment; Left/Right for fast adjustment)");
    displayText(100, 550, "  Press space bar to finalize the trial");
    displayText(100, 700, "Now press any key to continue");
    // Did they press any key?
    getKeyPressed(-1);
    // blank the screen using the current background color of the screen
    blankScreen();
    // make the cursor invisible
    hideCursor();
    
    // To shuffle around the order of presentation we create a list of
    // 'test' ids
    List<String> object = new ArrayList<String>();
    for (int repeat = 0; repeat < N_TRIAL; repeat++)
      for(String scene: SCENE)
        for (int id : SIZE)
          object.add(scene+"_"+String.valueOf(id)+".jpg");
    
    // put the whole loop inside a try-catch-finally clause. If something
    // goes wrong the program does not hang our computer.
    try {
 
      int stimulusX = (getWidth() - WIDTH)/3;
      int stimulusY = (getHeight() - HEIGHT)/2;
      int probeX = (getWidth() + stimulusX + WIDTH - SIZE_PROBE_MAX ) / 2 ;
      int probeY = (getHeight() - SIZE_PROBE_MAX) / 2;

      // A special function of Collections class is invoked to
      // randomize the order of presentation
      Collections.shuffle(object);
      // we show the trials back to back untill we exhaust our list.
      // Iterator is a fast way of accessing elements of a List of objects.
      Iterator<String> it = object.listIterator();

      while (it.hasNext()) {

        String fileName = it.next();
        String idScene = fileName.split("_")[0];
        int size = Integer.valueOf( (fileName.split(".jpg")[0]).split("_")[1]);
        int sizeProbe = Math.min(SIZE_PROBE_MAX, 
            Math.max(SIZE_PROBE_MIN, size/2 + (int)(Math.random()*size)));
        
        BufferedImage stimulus = ImageIO.read(SizesMoA.class.getResource(fileName));
        updateProbe(sizeProbe);
        
        String message = new String("Ball Size = " + size + ";");

        // Show the first images
        displayImage(stimulusX, stimulusY, stimulus);
        displayImage(probeX, probeY, probe);
        updateScreen();

        int response;
        while(true){
          response = getKeyPressed(-1).getKey();
          if (response == KeyEvent.VK_SPACE){
            outputFile.println(idScene + "   " + size + "   " + sizeProbe);
            outputFile.flush();

            // feedback
            message = message + new String(" match = " + sizeProbe + " (pixels)");
            blankScreen();
            displayText(message);
            updateScreen();
            getKeyPressed(-1);
            blankScreen();
            break;
          }
          else if (response == KeyEvent.VK_UP)
            sizeProbe += SIZE_STEP_PROBE;
          else if (response == KeyEvent.VK_DOWN)
            sizeProbe -= SIZE_STEP_PROBE;
          else if (response == KeyEvent.VK_LEFT)
            sizeProbe -= 5 * SIZE_STEP_PROBE;
          else if (response == KeyEvent.VK_RIGHT)
            sizeProbe += 5 * SIZE_STEP_PROBE;
          else
            continue;

          sizeProbe = Math.min(SIZE_PROBE_MAX, Math.max(SIZE_PROBE_MIN, sizeProbe));
          updateProbe(sizeProbe);

          displayImage(probeX,probeY,probe);
          updateScreen();
        }
      }

      // Clear the screen and display a thank you message
      blankScreen();
      String message = new String("Thank You!");
      displayText(message);
      java.lang.Thread.sleep(1500);
      // Here we catch the exceptions. if any of them happens the program
      // skips the rest of the try block, but always execute finally{...}
      // so we can be sure that the full-screen display will close.
    } catch (IOException ef) {
      System.err.println("cannot open file: ");
      ef.printStackTrace();
    } catch (InterruptedException et) {
      // In case Tread.sleep() fails.
      et.printStackTrace();
      Thread.currentThread().interrupt();
    }
    finally {
      // close the full screen mode, and dispose all display components
      // If you don't do this, the program will hang and you
      // will have to kill it manually.
      closeScreen();
    }
  }
}