package main;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import javax.swing.JPanel;

import com.softsynth.jsyn.EqualTemperedTuning;
import com.softsynth.jsyn.ExponentialLag;
import com.softsynth.jsyn.LineOut;
import com.softsynth.jsyn.MultiplyUnit;
import com.softsynth.jsyn.Synth;
import com.softsynth.jsyn.SynthMixer;

public class Score extends JPanel implements MouseListener, MouseMotionListener{
	
	ArrayList notes = new ArrayList();
	ArrayList lastNotes;
	float x1;
	float x2;
	float y1;
	float y2;
	
	WaveForm clickAndPlay;
	
	LineOut lineOut;
	
	int counter = 0;
	
	public Score(){

		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		this.setLayout(null);
		this.setBackground(Color.WHITE);
		this.add(Rise.BOUNDING_BOX);
		
		setNotes();

		this.add(Rise.SCROLLBAR);
		
	}
	
	public void setNotes(){
		lastNotes = (ArrayList) notes.clone();
		notes = new ArrayList();
		double centRange = (1200/Math.log(2.))*Math.log((double) Rise.FIRST_NUM/(Rise.FIRST_NUM-Rise.VOICES+1));
		int totalVoices = Rise.VOICES;
		int plotPoints = totalVoices;
		int count = 0;
		for (int voice = 0; voice < totalVoices; voice++){
			for (int plotPoint = voice; plotPoint < plotPoints; plotPoint++){
				double x = (1./(plotPoints-1))*plotPoint;
				double pitch = 1200./Math.log(2.)*Math.log((double) (Rise.FIRST_NUM+voice-plotPoint)/(Rise.FIRST_NUM-plotPoint));
				double y = Math.abs(((pitch)/centRange)-1);
				notes.add(new Note((x*Rise.X_DIV-((double) Rise.X_DIV/Rise.X_DIV)*Rise.CURRENT_X_PAGE),
						(y*Rise.Y_DIV-((double) Rise.Y_DIV/Rise.Y_DIV)*Math.abs(Rise.CURRENT_Y_PAGE-(Rise.Y_DIV-1))),
						pitch,voice,plotPoint));
				if (x >= (1./Rise.X_DIV)*Rise.CURRENT_X_PAGE && x <= (1./Rise.X_DIV)*(Rise.CURRENT_X_PAGE+1)
						&& y >= 1-(1./Rise.Y_DIV)*(Rise.CURRENT_Y_PAGE+1) && y <= 1-(1./Rise.Y_DIV)*(Rise.CURRENT_Y_PAGE)) {
					this.add((Note) notes.get(count));

				}
				count++;
			}
		}	
		Rise.NOTE_FRAME.setNoteAccess(notes);
	}
	
	public void setNoteChoices(){
		for (int count = 0; count < notes.size(); count++){
			((Note) notes.get(count)).clefChoice = ((Note) lastNotes.get(count)).clefChoice;
			((Note) notes.get(count)).octavaAndBassoChoice = ((Note) lastNotes.get(count)).octavaAndBassoChoice;
			((Note) notes.get(count)).spellingChoice = ((Note) lastNotes.get(count)).spellingChoice;
			((Note) notes.get(count)).ratioChoice = ((Note) lastNotes.get(count)).ratioChoice;
		}
	}
	
	public void paint(Graphics g){
		
		super.paint(g);
		Rise.SCROLLBAR_WIDTH = (int) (this.getPreferredSize().width - Rise.PAGE_MARGIN*Rise.ZOOM * 2);
		Graphics2D g2 = (Graphics2D) g;
		g2.translate(Rise.PAGE_MARGIN*Rise.ZOOM, Rise.PAGE_MARGIN*Rise.ZOOM);
		g2.scale(Rise.ZOOM, Rise.ZOOM);
		g2.setStroke(new BasicStroke(Rise.LINE_THICKNESS,BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER));
		int totalVoices = Rise.VOICES;
		double centRange = (1200/Math.log(2.))*Math.log((double) Rise.FIRST_NUM/(Rise.FIRST_NUM-Rise.VOICES+1));
		int plotPoints = totalVoices*25;
		for (int voice = 1; voice < totalVoices-1; voice++){
			for (int plotPoint = voice*(plotPoints/totalVoices); plotPoint < plotPoints-(plotPoints/totalVoices); plotPoint++){
				double startX = (1./(plotPoints-plotPoints/totalVoices))*plotPoint;
				double startY = Math.abs(((1200./Math.log(2.)*Math.log((Rise.FIRST_NUM+voice-plotPoint/((double) plotPoints/totalVoices))/(Rise.FIRST_NUM-plotPoint/((double) plotPoints/totalVoices))))/centRange)-1);
				double endX = (1./(plotPoints-plotPoints/totalVoices))*(plotPoint+1);
				double endY = Math.abs(((1200./Math.log(2.)*Math.log((Rise.FIRST_NUM+voice-(plotPoint+1)/((double) plotPoints/totalVoices))/(Rise.FIRST_NUM-(plotPoint+1)/((double) plotPoints/totalVoices))))/centRange)-1);
				if (startX >= (1./Rise.X_DIV)*Rise.CURRENT_X_PAGE && startX <= (1./Rise.X_DIV)*(Rise.CURRENT_X_PAGE+1)
						&& startY >= 1-(1./Rise.Y_DIV)*(Rise.CURRENT_Y_PAGE+1) && startY <= 1-(1./Rise.Y_DIV)*(Rise.CURRENT_Y_PAGE)) {
					g2.draw(new Line2D.Double((startX*Rise.X_DIV-((double) Rise.X_DIV/Rise.X_DIV)*Rise.CURRENT_X_PAGE)*Rise.IMAGE_WIDTH,
							(startY*Rise.Y_DIV-((double) Rise.Y_DIV/Rise.Y_DIV)*Math.abs(Rise.CURRENT_Y_PAGE-(Rise.Y_DIV-1)))*Rise.IMAGE_HEIGHT,
							(endX*Rise.X_DIV-((double) Rise.X_DIV/Rise.X_DIV)*Rise.CURRENT_X_PAGE)*Rise.IMAGE_WIDTH,
								(endY*Rise.Y_DIV-((double) Rise.Y_DIV/Rise.Y_DIV)*Math.abs(Rise.CURRENT_Y_PAGE-(Rise.Y_DIV-1)))*Rise.IMAGE_HEIGHT));
				}
			}
		}
	}
	
	public void setMasterFader(double a){
		if (Rise.IS_ENGINE_ON == true) {
			Rise.MASTER_FADER.input.set(a);
		}
	}
	
	public void setClickFader(double a){
		if (Rise.IS_ENGINE_ON == true) {
			Rise.CLICK_FADER.input.set(a);
		}
	}

	public void mouseClicked(MouseEvent e) {
	}
	public void mousePressed(MouseEvent e) {
		x1 = e.getX();
		y1 = e.getY();
		if (Rise.MODE == 0){

			Rise.NOTE_FRAME.setVisible(false);
		}
		else {
			int registered = 0;
			for(int i = 0; i < notes.size(); i++ ) {
				//int add = ((Note) notes.get(i)).testSelectClickAndPlay((float) (x1-20*Rise.ZOOM), (float) (y1-20*Rise.ZOOM), (float) (x1+20*Rise.ZOOM), (float) (y1+20*Rise.ZOOM));
				int add = ((Note) notes.get(i)).testSelectClickAndPlay((float) (x1), (float) (y1));
				
				if (add != 0) {
					registered = registered + add;
					
					//System.out.println(((Note) notes.get(i)).x + " " + ((Note) notes.get(i)).y + 
					//		" " + ((Note) notes.get(i)).p + " " + ((Note) notes.get(i)).v);
					
					((Note) notes.get(i)).repaint();
					if (Rise.IS_ENGINE_ON == false){
						Rise.IS_ENGINE_ON = true;
						Synth.startEngine(0);
						Rise.MASTER_MIXER = new SynthMixer(1,1);
						Rise.CLICK_MIXER = new SynthMixer(10,1);
						Rise.MASTER_MULT = new MultiplyUnit();
						Rise.MASTER_FADER = new ExponentialLag();
						Rise.CLICK_MULT = new MultiplyUnit();
						Rise.CLICK_FADER = new ExponentialLag();
						lineOut = new LineOut();
						
						for (int c = 0; c < 10; c++){
							Rise.CLICK_MIXER.setGain(c, 0, 1);
						}
						
						Rise.CLICK_MULT.inputA.connect(Rise.CLICK_FADER.output);
						Rise.CLICK_MULT.inputB.connect(Rise.CLICK_MIXER.getOutput(0));
						
						
						Rise.MASTER_MIXER.connectInput(0, Rise.CLICK_MULT.output, 0);
						Rise.MASTER_MIXER.setGain(0, 0, 1);
						
						Rise.MASTER_MULT.inputA.connect(Rise.MASTER_FADER.output);
						Rise.MASTER_MULT.inputB.connect(Rise.MASTER_MIXER.getOutput(0));
						
						lineOut.input.connect(0, Rise.MASTER_MULT.output, 0);
						lineOut.input.connect(1, Rise.MASTER_MULT.output, 0);
						
						lineOut.start(0);
						Rise.MASTER_MIXER.start(0);
						Rise.MASTER_MULT.start(0);
						Rise.MASTER_FADER.start(0);
						Rise.CLICK_MIXER.start(0);
						Rise.CLICK_MULT.start(0);
						Rise.CLICK_FADER.start(0);
						
						setMasterFader(1);
						setClickFader(1);
						
						//System.out.println("STARTED!!!");

					}
					
					//clickAndPlay = new WaveForm();
					
					clickAndPlay = new WaveForm();
					Rise.CLICK_MIXER.connectInput((counter%10), clickAndPlay.output, 0);
					counter++;
					
					double envPosition = (x1 - Rise.PAGE_MARGIN*Rise.ZOOM)/(Rise.IMAGE_WIDTH*Rise.ZOOM);
					
					double duration =
						(Rise.GRAIN_SOUND_DUR_VAL_START - envPosition * Math.abs(Rise.GRAIN_SOUND_DUR_VAL_START - Rise.GRAIN_SOUND_DUR_VAL_END)) + 
						((Rise.GRAIN_SOUND_DUR_JIT_START - envPosition * Math.abs(Rise.GRAIN_SOUND_DUR_JIT_START - Rise.GRAIN_SOUND_DUR_JIT_END)) *
								(Math.random() * 2 - 1));
					clickAndPlay.envRate.set(1./duration);

					double amp =
						((Rise.GRAIN_AMP_VAL_START + envPosition * Math.abs(Rise.GRAIN_AMP_VAL_END - Rise.GRAIN_AMP_VAL_START)) + 
								((Rise.GRAIN_AMP_JIT_START + envPosition * Math.abs(Rise.GRAIN_AMP_JIT_END - Rise.GRAIN_AMP_JIT_START)) *
										(Math.random() * 2 - 1)));

					double spectrum =
						Rise.MIN_SPECTRUM + Math.random() * Math.abs(Rise.MAX_SPECTRUM - Rise.MIN_SPECTRUM);
					
					//System.out.println("Tone Info; dur = " + duration + ", amp =  " + amp);

					clickAndPlay.spectrum.set(spectrum);
					double frequency = EqualTemperedTuning.getMIDIFrequency(24/*+Rise.TRANSPOSE_SCORE*/+Rise.TRANSPOSE_SOUND+((Note) notes.get(i)).pitch/100.);
					
					clickAndPlay.clickOn(0, frequency, amp);
				}
			}
			//System.out.println(registered);
			
		}
	}
	public void mouseReleased(MouseEvent e) {
		if (Rise.MODE == 0){
			Rise.NOTE_FRAME.bass.setSelected(false);
			Rise.NOTE_FRAME.tenor.setSelected(false);
			Rise.NOTE_FRAME.alto.setSelected(false);
			Rise.NOTE_FRAME.treble.setSelected(false);

			int clef = 0;
			int oB = 0;
			int spell = 0;
			int ratio = 0;

			x2 = e.getX();
			y2 = e.getY();

			double rectX1;
			double rectX2;
			double rectY1;
			double rectY2;

			if (e.getX() < x1){
				rectX2 = x1;
				rectX1 = e.getX();
			}
			else {
				rectX1 = x1;
				rectX2 = e.getX();
			}
			if (e.getY() < y1){
				rectY2 = y1;
				rectY1 = e.getY();
			}
			else {
				rectY1 = y1;
				rectY2 = e.getY();
			}


			int registered = 0;
			for(int i = 0; i < notes.size(); i++ ) {
				int add = ((Note) notes.get(i)).testSelect((float) (rectX1-10*Rise.ZOOM), (float) (rectY1-10*Rise.ZOOM), (float) (rectX2+10*Rise.ZOOM), (float) (rectY2+10*Rise.ZOOM));

				if (add != 0) {
					registered = registered + add;
					clef = ((Note) notes.get(i)).clefChoice;
					oB = ((Note) notes.get(i)).octavaAndBassoChoice;
					spell = ((Note) notes.get(i)).spellingChoice;
					ratio = ((Note) notes.get(i)).ratioChoice;
				}
			}
			if (registered > 0) {
				Rise.NOTE_FRAME.setListeningNotes();
				Rise.BOUNDING_BOX.setVisible(true);
				Rise.BOUNDING_BOX.setCoordinates(rectX1, rectX2, rectY1, rectY2);
				Rise.BOUNDING_BOX.repaint();
				Rise.NOTE_FRAME.setVisible(true);
				if (registered == 1) {
					if (clef == 0) {
						Rise.NOTE_FRAME.bass.setSelected(true);
					}
					else if (clef == 1) {
						Rise.NOTE_FRAME.tenor.setSelected(true);
					}
					else if (clef == 2) {
						Rise.NOTE_FRAME.alto.setSelected(true);
					}
					else if (clef == 3) {
						Rise.NOTE_FRAME.treble.setSelected(true);
					}

					if (oB == 0) {
						Rise.NOTE_FRAME.mb.setSelected(true);
					}
					else if (oB == 1) {
						Rise.NOTE_FRAME.vb.setSelected(true);
					}
					else if (oB == 2) {
						Rise.NOTE_FRAME.none.setSelected(true);
					}
					else if (oB == 3) {
						Rise.NOTE_FRAME.va.setSelected(true);
					}
					else if (oB == 4) {
						Rise.NOTE_FRAME.ma.setSelected(true);
					}

					if (spell == 0) {
						Rise.NOTE_FRAME.sharps.setSelected(true);
					}
					else if (spell == 1) {
						Rise.NOTE_FRAME.flats.setSelected(true);
					}

					if (ratio == 0) {
						Rise.NOTE_FRAME.show.setSelected(true);
					}
					else if (ratio == 1) {
						Rise.NOTE_FRAME.hide.setSelected(true);
					}
				}
				else { 
					Rise.NOTE_FRAME.clefNada.setSelected(true);
					Rise.NOTE_FRAME.oBNada.setSelected(true);
					Rise.NOTE_FRAME.spellNada.setSelected(true);
					Rise.NOTE_FRAME.ratiosNada.setSelected(true);
				}
			}
			else {
				Rise.BOUNDING_BOX.setVisible(false);
			}
		}
	}
	public void mouseEntered(MouseEvent e) {
	}
	public void mouseExited(MouseEvent e) {
	}

	public void mouseDragged(MouseEvent e) {
		if (Rise.MODE == 0){
			Rise.BOUNDING_BOX.setVisible(true);
			double rectX1;
			double rectX2;
			double rectY1;
			double rectY2;

			if (e.getX() < x1){
				rectX2 = x1;
				rectX1 = e.getX();
			}
			else {
				rectX1 = x1;
				rectX2 = e.getX();
			}
			if (e.getY() < y1){
				rectY2 = y1;
				rectY1 = e.getY();
			}
			else {
				rectY1 = y1;
				rectY2 = e.getY();
			}
			Rise.BOUNDING_BOX.setCoordinates(rectX1, rectX2, rectY1, rectY2);
			Rise.BOUNDING_BOX.repaint();
		}
	}
	public void mouseMoved(MouseEvent e) {
	}
	
}
