package net.beadsproject.touch.examples;
import java.util.Arrays;

import net.beadsproject.beads.core.AudioContext;
import net.beadsproject.beads.data.Pitch;
import net.beadsproject.touch.SurfacePlayer;
import net.beadsproject.touch.event.BasicEventListener;
import net.beadsproject.touch.perform.PerformNode;
import net.beadsproject.touch.pianopush.PianoPush;
import net.beadsproject.touch.surface.PixelSurface;
import processing.core.PApplet;
import processing.core.PGraphics;

/**
 * Piano example.
 * 
 * @author ben
 *
 */
public class Piano extends PApplet {	
	
	PixelSurface ps;
	PianoPush pt;
	
	PGraphics tempImg;
	PGraphics pianoImage;
	
	float ox, oy; // old mousex, mousey;
	SurfacePlayer sp; // the user
			
	public void setup()
	{		
		size(600,768);
		
		println("Building tree");
		AudioContext ac = new AudioContext();
		pt = new PianoPush(this, ac, 600, 768);
		ac.start();
				
		println("Laying out nodes");
		sp = new SurfacePlayer();
		layoutPiano();
				
		println("Done");
	}
		
	public void layoutPiano()
	{
		ps = new PixelSurface(width,height);
		tempImg = this.createGraphics(width,height,P2D);		
		tempImg.beginDraw();
		tempImg.background(0);	
		tempImg.noStroke();
		
		pianoImage = this.createGraphics(width,height,P2D);
		pianoImage.beginDraw();
		pianoImage.background(0);
		
		// the general layout is:
		/*
		 * +------------+
		 * | fx1 |  fx2 |
		 * +-----+------+
		 * | fx3 |  fx4 |
		 * +-----+------+
		 * |[ ]|[ ]|[ ]|| <-- flats
		 * |c|d|e|f|g|a| ... etc|
		 * +------------+
		 */
		
		PerformNode[] keys = pt.getNotes();
		PerformNode[] fx = pt.getEffects();
		
		// run through and count the number of white keys
		int whiteKeys = Pitch.major.length;		
		
		// divide the width by that amount
		float dWhiteKey = (float)width/whiteKeys;
		
		// we have numFxRows rows of effects
		int numFxRows = (fx.length+1)/2;
		
		// hence we have
		float dRow = (float)height / (numFxRows + 1);
		
		// layout the regions and map from region to perform Node
		PerformNode idMap[] = new PerformNode[keys.length+fx.length]; 
		int index = 0;
		
		// layout fx first
		int row = 0, col = 0;
		for(PerformNode effect: fx)
		{
			int colour = index + 1;
			tempImg.fill((colour >> 16)&0xFF, (colour >> 8)&0xFF, colour&0xFF);			
			tempImg.rect(col*(width/2), row*dRow, width/2, dRow);
			idMap[index] = effect;
			
			pianoImage.fill(150,100,100);
			pianoImage.rect(col*(width/2), row*dRow, width/2, dRow);			
			
			col++;
			if (col>=2)
			{
				row++;
				col = 0;
			}
			
			index ++;
		}
		
		// lay out the white keys
		col = 0;
		for(int i: Pitch.major)
		{
			PerformNode key = keys[i];			
			int colour = index + 1;
			tempImg.fill((colour >> 16)&0xFF, (colour >> 8)&0xFF, colour&0xFF);
			tempImg.rect(col*dWhiteKey, height-dRow, dWhiteKey, dRow);
			
			pianoImage.fill(255);
			pianoImage.rect(col*dWhiteKey, height-dRow, dWhiteKey, dRow);
			
			idMap[index] = key;
			index ++;			
			col++;			
		}
		
		// lay out the black keys
		float fBlack = 0.5f;
		float wBlack = (float) (dWhiteKey*.7);
		float hBlack = fBlack*dRow;
		int lastWhiteKey = 0;
		for(int i=0;i<keys.length;i++)
		{
			int isWhite = Arrays.binarySearch(Pitch.major,i);
			if (isWhite>=0) lastWhiteKey = isWhite;
			else // isBlack 
			{	
				float x = (lastWhiteKey+1)*dWhiteKey - wBlack/2;
				float y = height-dRow;
				
				int colour = index + 1;
				tempImg.fill((colour >> 16)&0xFF, (colour >> 8)&0xFF, colour&0xFF);
				tempImg.rect(x,y,wBlack,hBlack);
				
				pianoImage.fill(0);
				pianoImage.rect(x,y,wBlack,hBlack);
				
				idMap[index] = keys[i];
				index ++;	
			}
		}
		
		if (index > (256*256*256))
		{
			System.err.println("Error, too many regions! Has to be less than 256^3 atm.\nContinuing anyway..");
		}
		tempImg.endDraw();
		pianoImage.endDraw();
		
		tempImg.loadPixels();
		for(int i=0;i<tempImg.pixels.length;i++)
		{
			int r = i/tempImg.width;
			int c = i%tempImg.width;
			int pni = tempImg.pixels[i];
			
			int rd = (pni >> 16) & 0xFF;
			int gr = (pni >> 8) & 0xFF;
			int bl = pni & 0xFF;
			
			int in = (int)((rd << 16) | (gr << 8) | bl) - 1;
			if (in<0)
				ps.set(c,r,null);
			else
				ps.set(c,r,idMap[in]);
		}			
		tempImg.updatePixels();
		
		ps.bake(this);			
		
		println("All done.");
		
		ps.setEventListener(new BasicEventListener());
	}
		
	public void draw()
	{	
		background(0);
		noFill();
		image(pianoImage,0,0);
		//ps.draw(this);
	}	
		
	public void mousePressed()
	{		
		float mx = (float)mouseX/width, my = (float)mouseY/height;
		ps.playerPressed(sp,mx,my);
		ox = mx;
		oy = my;
	}
	
	public void mouseReleased()
	{
		float mx = (float)mouseX/width, my = (float)mouseY/height;
		ps.playerReleased(sp,mx,my);		
		ox = mx;
		oy = my;
	}
	
	public void mouseDragged()
	{
		// transform the mouse click into particle world coordinates
		float mx = (float)mouseX/width, my = (float)mouseY/height;
		ps.playerMoved(sp,ox,oy,mx,my);			
		ox = mx;
		oy = my;
	}
}
