package net.mgsx.gdx.pd;

import org.puredata.core.PdListener;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Slider;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.viewport.ScreenViewport;

import net.mgsx.pd.Pd;
import net.mgsx.pd.PdConfiguration;

public class PdAudioRemoteTest extends ApplicationAdapter
{
	private Stage stage;
	private Skin skin;
	private Table table;
	
	@Override
	public void create() {
		
		Pd.audio.create(new PdConfiguration());
		
		skin = new Skin(Gdx.files.classpath("skins/uiskin.json"));
		
		stage = new Stage(new ScreenViewport());
		
		table = new Table(skin);
		
		buildUI();
		
		ScrollPane scroll = new ScrollPane(table, skin);
		scroll.setFillParent(true);
		stage.addActor(scroll);
		
		Gdx.input.setInputProcessor(stage);
	}
	
	private void buildUI()
	{
		actionTest("Send Bang on recv_bang", new ActionHandler<Object>() {
			@Override
			public void run(Object value) {
				Pd.audio.sendBang("recv_bang");
			}
		});
		
		valueTest("Send Float on recv_float", new ActionHandler<Float>() {
			@Override
			public void run(Float value) {
				Pd.audio.sendFloat("recv_float", value);
			}
		});
		
		actionTest("Send List on recv_list", new ActionHandler<Object>() {
			@Override
			public void run(Object value) {
				Pd.audio.sendList("recv_list", "some text", 42, 4.45f, 3.12f, 67, 59);
			}
		});
		
		testSendList("Integers", 12, -57);
		testSendList("Integers limits", Integer.MIN_VALUE, Integer.MAX_VALUE);
		testSendList("Longs", 12L, -57L);
		testSendList("Longs limits", Long.MIN_VALUE, Long.MAX_VALUE);
		testSendList("Floats", 12.3f, -57.3456f);
		testSendList("Floats limits", Float.MIN_VALUE, Float.MAX_VALUE);
		testSendList("Doubles", 12.3, -57.3456);
		testSendList("Doubles limits", Double.MIN_VALUE, Double.MAX_VALUE);
		testSendList("Strings", "foo", "bar");
		testSendList("Strings with space", "foo bar hello world");
		testSendList("Strings empty", "", "", "");
		testSendList("Strings special", "\n", "$", "\\", "\u2834");
		
		
		testSendMessage("Integers", 12, -57);
		testSendMessage("Integers limits", Integer.MIN_VALUE, Integer.MAX_VALUE);
		testSendMessage("Longs", 12L, -57L);
		testSendMessage("Longs limits", Long.MIN_VALUE, Long.MAX_VALUE);
		testSendMessage("Floats", 12.3f, -57.3456f);
		testSendMessage("Floats limits", Float.MIN_VALUE, Float.MAX_VALUE);
		testSendMessage("Doubles", 12.3, -57.3456);
		testSendMessage("Doubles limits", Double.MIN_VALUE, Double.MAX_VALUE);
		testSendMessage("Strings", "foo", "bar");
		testSendMessage("Strings with space", "foo bar hello world");
		testSendMessage("Strings empty", "", "", "");
		testSendMessage("Strings special", "\n", "$", "\\", "\u2834");
		
		testSendSymbol("Symbol path", "/home/me/myFile.txt");
		
		testSendArray("Simple float array", "array_to_write", 0, 32.5f, 54.7f, 62.12f, 99.456f);
		
		
		// One second of 441 Hz sinusoidal signal.
		float [] sampleData = new float[44100];
		for(int i=0 ; i<sampleData.length ; i++){
			sampleData[i] = (float)Math.sin(Math.PI * 2 * 441 * (float)i / 44100f);
		}
		
		testSendArray("Big float array", "big_array", 0, sampleData);

		
		testListener("send_any");
	}
	
	private void testSendArray(String label, final String name, final int position, final float...values)
	{
		String valText = "";
		if(values.length < 8)
			for(float v : values) valText += "," + String.valueOf(v);
		else
			valText = ",...";
		if(valText.length()>0) valText = "(" + valText.substring(1) + ")";
		valText = "at " + String.valueOf(position) + " " + valText;
		TextButton bt = new TextButton(label + " " + valText, skin);
		bt.addListener(new ChangeListener() {
			@Override
			public void changed(ChangeEvent event, Actor actor) {
				Pd.audio.writeArray(name, position, values, 0, values.length);
			}
		});
		table.add("Write array to " + name);
		table.add(bt);
		table.row();
	}
	
	private void testListener(final String recv)
	{
		final Label content = new Label("", skin);
		table.add("Test Listeners");
		
		Table stable = new Table(skin);
		table.add(stable);
		table.row();
		
		final TextButton btListen = new TextButton("Listen", skin, "toggle");
		stable.add(btListen);
		stable.add(content);
		
		final PdListener listener = new PdListener(){

			@Override
			public void receiveBang(String source) {
				content.setText("receive bang on " + source);
			}

			@Override
			public void receiveFloat(String source, float x) {
				content.setText("receive float on " + source + " : " + String.valueOf(x));
			}

			@Override
			public void receiveSymbol(String source, String symbol) {
				content.setText("receive symbol on " + source + " : " + symbol);
			}

			@Override
			public void receiveList(String source, Object... args) {
				String text = "";
				for(Object arg : args) text += "," + arg.toString();
				if(text.length() > 0) text = text.substring(1);
				content.setText("receive list on " + source + " : " + text);
			}

			@Override
			public void receiveMessage(String source, String symbol, Object... args) {
				String text = symbol;
				for(Object arg : args) text += "," + arg.toString();
				content.setText("receive message on " + source + " : " + text);
			}
			
		};
		
		btListen.addListener(new ChangeListener() {
			@Override
			public void changed(ChangeEvent event, Actor actor) {
				if(btListen.isChecked()){
					Pd.audio.addListener(recv, listener);
				}else{
					Pd.audio.removeListener(recv, listener);
				}
			}
		});
		
	}
	
	private void testSendList(String label, final Object...values)
	{
		String valText = "";
		for(Object v : values) valText += "," + v.toString();
		valText = "(" + valText.substring(1) + ")";
		
		TextButton bt = new TextButton(label + " " + valText, skin);
		bt.addListener(new ChangeListener() {
			
			@Override
			public void changed(ChangeEvent event, Actor actor) {
				Pd.audio.sendList("recv_list", values);
			}
		});
		table.add("Send List on recv_list");
		table.add(bt);
		table.row();
	}
	
	private void testSendMessage(String label, final Object...values)
	{
		String valText = "";
		for(Object v : values) valText += "," + v.toString();
		valText = "(" + valText.substring(1) + ")";
		
		TextButton bt = new TextButton(label + " " + valText, skin);
		bt.addListener(new ChangeListener() {
			
			@Override
			public void changed(ChangeEvent event, Actor actor) {
				Pd.audio.sendMessage("recv_msg", "test", values);
			}
		});
		table.add("Send Message on recv_msg");
		table.add(bt);
		table.row();
	}

	private void testSendSymbol(String label, final String symbol)
	{
		TextButton bt = new TextButton(label + " (" + symbol + ")", skin);
		bt.addListener(new ChangeListener() {
			
			@Override
			public void changed(ChangeEvent event, Actor actor) {
				Pd.audio.sendSymbol("recv_sym", symbol);
			}
		});
		table.add("Send Symbol on recv_sym");
		table.add(bt);
		table.row();
	}

	
	private void actionTest(String label, final ActionHandler<?> runnable) {
		TextButton bt = new TextButton("test", skin);
		bt.addListener(new ChangeListener() {
			
			@Override
			public void changed(ChangeEvent event, Actor actor) {
				runnable.run(null);
			}
		});
		table.add(label);
		table.add(bt);
		table.row();
	}
	
	private void valueTest(String label, final ActionHandler<Float> runnable) {
		final Slider sl = new Slider(-100, 100, 0.1f, false, skin);
		sl.addListener(new ChangeListener() {
			@Override
			public void changed(ChangeEvent event, Actor actor) {
				runnable.run(sl.getValue());
			}
		});
		table.add(label);
		table.add(sl);
		table.row();
	}
	
	private static interface ActionHandler<T>
	{
		public void run(T value);
	}

	@Override
	public void render() {
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		stage.act();
		stage.draw();
	}
	
	@Override
	public void resize(int width, int height) {
		stage.getViewport().update(width, height, true);
	}
	
	public static void main(String[] args) 
	{
		LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
		config.height = 1000;
		PdConfiguration.remoteEnabled = true;
		
		new LwjglApplication(new PdAudioRemoteTest(), config);
		
	}
}