/*******************************************************************************
 * Copyright (c) 2015, 2016 CNRS, IRIT, IRT AESE (IRT Saint Exupéry).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the LGPL 3.0
 * which accompanies this distribution, and is available at
 * https://www.gnu.org/licenses/lgpl-3.0-standalone.html
 *
 * Contributors:
 *     Faiez Zalila - initial API and implementation and/or initial documentation
 *     Jorge Enrique Gutierrez Zuluaga
 *******************************************************************************/
package fr.laas.libtts;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.ui.PlatformUI;

import tts.Error;
import tts.InvalidPath;
import tts.Net;
import tts.TTS.Client;

public class LibTTS {
	private Client client;
	private TTransport transport;
	private Net tts_model;
	private TTSSimulator TTSSimulator;
	private Process tts_server_process;

	public LibTTS(String _tts_model_path) {

		// call the server automatically
		Runtime tts_server_run = Runtime.getRuntime();
		try {
			tts_server_process = tts_server_run
					.exec(Tools.LibTTS+" --port=9090");
			// tts_server_process.waitFor();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// Launch le LibTTS client
		client = launchClient();
		initialize_tts_model(_tts_model_path);
	}

	private void initialize_tts_model(String _tts_model_path) {
		Net pn;
		try {
			pn = client.load_local(_tts_model_path);
			if (pn.isTimed())
				System.out.print("Timed ");
			System.out.println("TTS successfully loaded");
			setTTS_model(pn);
			TTSSimulator = new TTSSimulator(tts_model, client);
		} catch (Error e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidPath e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TException e) {
			showException("TException", e);
			quit();
			//e.printStackTrace();
		}

	}

	public static void showException(String message, final Exception ex) {

		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		ex.printStackTrace(pw);
		// ex.printStackTrace();

		final String trace = sw.toString(); // stack trace as a string

		// Temp holder of child statuses
		List<Status> childStatuses = new ArrayList<Status>();

		String error = null;
		// Split output by OS-independend new-line
		for (String line : trace.split(System.getProperty("line.separator"))) {
			if (error == null)
				error = line;
			else
				childStatuses.add(new Status(IStatus.ERROR, "fr.laas.libtts", line));
		}

		MultiStatus multiStatus = new MultiStatus("fr.laas.libtts", IStatus.ERROR,
				childStatuses.toArray(new Status[] {}), // convert to array of
														// statuses
				error, ex);

		ErrorDialog.openError(null, "Unexpected Error", message, multiStatus);
	}

	public TTSSimulator getTTSSimulator() {
		return TTSSimulator;
	}

	public void setTTSSimulator(TTSSimulator simulateTTS) {
		this.TTSSimulator = simulateTTS;
	}

	private Client launchClient() {

		transport = new TSocket("localhost", 9090);
		try {
			// System.out.println("ttransport " + transport);
			transport.open();
		} catch (TTransportException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		TProtocol protocol = new TBinaryProtocol(transport);
		Client _client = new Client(protocol);
		// System.out.println("3");
		return _client;
	}

	public void simulate() {

		TTSSimulator.simulate();
	}

	public Net getTTS_model() {
		return tts_model;
	}

	public Client getClient() {
		return client;
	}

	public void setClient(Client client) {
		this.client = client;
	}

	public void setTTS_model(Net tts_model) {
		this.tts_model = tts_model;
	}

	public void quit() {
		try {
			client.shutdown();
		} catch (TException e) {
			// TODO Auto-generated catch block
			// e.printStackTrace();
		}
		transport.close();
		tts_server_process.destroyForcibly();
		// callCommand("kill -9 "+getPidOfProcess(tts_server_process));
		// IWorkbenchWindow window =
		// HandlerUtil.getActiveWorkbenchWindowChecked(event);
//		if (PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null) {
//			MessageDialog.openInformation(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
//					"Fiacre simulator", "Simulator closed");
//		}
	}

	private static void callCommand(String line_cmd) {
		Runtime cmd_run = Runtime.getRuntime();
		Process cmd_process;
		try {
			cmd_process = cmd_run.exec(line_cmd);

			try {
				cmd_process.waitFor();
			} catch (InterruptedException e) {

				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}

	public static long getPidOfProcess(Process p) {
		long pid = -1;

		try {
			if (p.getClass().getName().equals("java.lang.UNIXProcess")) {
				Field f = p.getClass().getDeclaredField("pid");
				f.setAccessible(true);
				pid = f.getLong(p);
				f.setAccessible(false);
			}
		} catch (Exception e) {
			pid = -1;
		}
		return pid;
	}

}
