package theGhastModding.tgmsynth.synth;

import java.util.ArrayList;
import java.util.List;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

import theGhastModding.utils.math.ByteConverters;

public class TGMSynth implements Runnable {
	
	public static String NAME = "TGM's Superawesome MIDI Synth";
	public static String VERSION = "0.1b";
	
	private int polyphony;
	private List<Note> notes;
	
	private byte[] buffer;
	private int bufferSize = 882;
	
	private SourceDataLine dl;
	
	private boolean running = false;
	
	private Thread t;
	
	public TGMSynth(int polyphony) throws Exception {
		buffer = new byte[bufferSize];
		AudioFormat format = new AudioFormat(44100, 16, 1, true, false);
		DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
	    dl = (SourceDataLine)AudioSystem.getLine(dataLineInfo);
	    dl.open(format);
	    dl.start();
	    this.polyphony = polyphony;
	    notes = new ArrayList<Note>();
	    running = true;
	    t = new Thread(this);
	    t.start();
	}
	
	public void closeSynth(){
		running = false;
		try { t.join(); } catch(Exception e){ e.printStackTrace(); }
		dl.stop();
		dl.close();
	}
	
	public void noteOn(int channel, int key, int velocity){
		Note n = new Note(key, velocity, channel);
		if(notes.size() >= polyphony || notes.contains(n)) return;
		notes.add(n);
	}
	
	public void noteOff(int channel, int key, int velocity){
		Note n = new Note(key, velocity, channel);
		notes.remove(n);
	}
	
	public int getCurrentPolyphony(){
		return notes.size();
	}
	
	@Override
	public void run() {
		long debugTimer = System.currentTimeMillis();
		while(running){
			if(System.currentTimeMillis() - debugTimer >= 10){
				debugTimer = System.currentTimeMillis();
				buffer = new byte[bufferSize];
				for(int i = 0; i < 882; i+=2){
					updateNotes(i);
				}
				dl.write(buffer, 0, buffer.length);
			}
		}
	}
	
	private float sampleTime = 1000f / 44100f;
	
	private void updateNotes(int point){
		try {
			float nextSample = 0f;
			for(int i = 0; i < notes.size(); i++){
				if(i >= notes.size()) break;
				Note n = notes.get(i);
				if(n == null) continue;
				n.setStatus(n.getStatus() + sampleTime);
				if(n.getStatus() > n.getCycleLength()) n.setStatus(0);
				if(n.getStatus() < n.getCycleLength() / 2f){
					nextSample += n.getUpStrength();
				}else{
					if(n.getStatus() > n.getCycleLength()) n.setStatus(0);
					nextSample += n.getDownStrength();
				}
				//nextSample += n.getStatus() / n.getCycleLength() * n.getUpStrength() - (0.5D * n.getDownStrength());
			}
			if(nextSample > Short.MAX_VALUE){
				nextSample = Short.MAX_VALUE;
			}
			if(nextSample < Short.MIN_VALUE){
				nextSample = Short.MIN_VALUE;
			}
			//buffer[point] = (byte)((nextSample + 0.5f) * 255f);
			byte[] a = ByteConverters.shortToBytes((short)nextSample);
			buffer[point] = a[1];
			buffer[point + 1] = a[0];
		} catch(Exception e){
			e.printStackTrace();
			buffer[point] = 0;
			buffer[point] = 0;
		}
	}
	
}