/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
Part of the Processing project - http://processing.org
Copyright (c) 2012-15 The Processing Foundation

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

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 for more details.

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.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package processing.mode.java.tweak;

import java.net.*;
import java.nio.ByteBuffer;


public class TweakClient {
	private DatagramSocket socket;
	private InetAddress address;
	private boolean initialized;
	private int sketchPort;

	static final int VAR_INT = 0;
	static final int VAR_FLOAT = 1;
	static final int SHUTDOWN = 0xffffffff;


	public TweakClient(int sketchPort) {
		this.sketchPort = sketchPort;

		try {
			socket = new DatagramSocket();
			// only local sketch is allowed
			address = InetAddress.getByName("127.0.0.1");
			initialized = true;

		} catch (SocketException e) {
			initialized = false;

		} catch (UnknownHostException e) {
			socket.close();
			initialized = false;

		} catch (SecurityException e) {
			socket.close();
			initialized = false;
		}
	}


	public void shutdown() {
		if (initialized) {
			// send shutdown to the sketch
			sendShutdown();
			initialized = false;
		}
	}


	public boolean sendInt(int index, int val) {
		if (initialized) {
			try {
				byte[] buf = new byte[12];
				ByteBuffer bb = ByteBuffer.wrap(buf);
				bb.putInt(0, VAR_INT);
				bb.putInt(4, index);
				bb.putInt(8, val);
				DatagramPacket packet = new DatagramPacket(buf, buf.length, address, sketchPort);
				socket.send(packet);
				return true;

			} catch (Exception e) { }
		}
		return false;
	}


	public boolean sendFloat(int index, float val) {
		if (initialized) {
			try {
				byte[] buf = new byte[12];
				ByteBuffer bb = ByteBuffer.wrap(buf);
				bb.putInt(0, VAR_FLOAT);
				bb.putInt(4, index);
				bb.putFloat(8, val);
				socket.send(new DatagramPacket(buf, buf.length, address, sketchPort));
				return true;

			} catch (Exception e) { }
		}
		return false;
	}


	public boolean sendShutdown() {
		if (initialized) {
			try {
				byte[] buf = new byte[12];
				ByteBuffer bb = ByteBuffer.wrap(buf);
				bb.putInt(0, SHUTDOWN);
				socket.send(new DatagramPacket(buf, buf.length, address, sketchPort));
				return true;

			} catch (Exception e) { }
		}
		return false;
	}


	static public String getServerCode(int listenPort,
																		 boolean hasInts, boolean hasFloats) {
		String serverCode = ""+
		"class TweakModeServer extends Thread\n"+
		"{\n"+
		"	protected DatagramSocket socket = null;\n"+
		"	protected boolean running = true;\n"+
		"	final int INT_VAR = 0;\n"+
		"	final int FLOAT_VAR = 1;\n"+
		"	final int SHUTDOWN = 0xffffffff;\n"+
		"	public TweakModeServer() {\n"+
		"		this(\"TweakModeServer\");\n"+
		"	}\n"+
		"	public TweakModeServer(String name) {\n"+
		"		super(name);\n"+
		"	}\n"+
		"	public void setup()\n"+
		"	{\n"+
		"		try {\n"+
		"			socket = new DatagramSocket("+listenPort+");\n"+
		"			socket.setSoTimeout(250);\n"+
		"		} catch (IOException e) {\n"+
		"			println(\"error: could not create TweakMode server socket\");\n"+
		"		}\n"+
		"	}\n"+
		"	public void run()\n"+
		"	{\n"+
		"		byte[] buf = new byte[256];\n"+
		"		while(running)\n"+
		"		{\n"+
		"			try {\n"+
		"				DatagramPacket packet = new DatagramPacket(buf, buf.length);\n"+
		"				socket.receive(packet);\n"+
		"				ByteBuffer bb = ByteBuffer.wrap(buf);\n"+
		"				int type = bb.getInt(0);\n"+
		"				int index = bb.getInt(4);\n";

		if (hasInts) {
			serverCode +=
		"				if (type == INT_VAR) {\n"+
		"					int val = bb.getInt(8);\n"+
		"					tweakmode_int[index] = val;\n"+
		"				}\n"+
		"				else ";
		}
		if (hasFloats) {
			serverCode +=
		"				 if (type == FLOAT_VAR) {\n"+
		"					float val = bb.getFloat(8);\n"+
		"					tweakmode_float[index] = val;\n"+
		"				}\n"+
		"				else";
		}
		serverCode +=
		"				if (type == SHUTDOWN) {\n"+
		"					running = false;\n"+
		"				}\n"+
		"			} catch (SocketTimeoutException e) {\n"+
		"				// nothing to do here just try receiving again\n"+
		"			} catch (Exception e) {\n"+
		"			}\n"+
		"		}\n"+
		"		socket.close();\n"+
		"	}\n"+
		"}\n\n\n";

		return serverCode;
	}
}
