package code;
import java.io.IOException;
import java.util.Date;

import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.json.JSONException;
import org.json.JSONObject;

public class SubscribeCallback implements MqttCallback{
	
	private Antifurto client;
	private Publisher publisher;
	private String nomeInterruttore; 
	private String nomeOutputSuono;
	private String nomeOutputAntifurto;
	private Esecutore esec;
	private Automa automa;
	
	public SubscribeCallback(Antifurto client, Publisher publisher, String nomeInterruttore, String nomeOutputSuono, Esecutore esec, Automa automa, String nomeOutputAntifurto) {
		this.client = client;
		this.publisher = publisher;
		this.nomeInterruttore = nomeInterruttore;
		this.nomeOutputSuono = nomeOutputSuono;
		this.nomeOutputAntifurto = nomeOutputAntifurto;
		this.esec = esec;
		this.automa = automa;
	}

	@Override
	public void connectionLost(Throwable arg0) {
		boolean retry = true;
		final Date d = new Date();
		Date d2 = new Date();
		long time = Math.abs(d2.getTime()-d.getTime());
		while(retry & (time<600000)) {
			try {
				client.startClient(esec, publisher);
				retry = false;
			} catch (MqttException e) {
				d2 = new Date();
				time = Math.abs(d2.getTime()-d.getTime());
			}
		}
		if(time>=600000) {
			System.out.println("Tentativo di riconnessione fallito");
			System.exit(1);
		}
	}

	
	@Override
	public void deliveryComplete(IMqttDeliveryToken arg0) {
		// Nessuna operazione
		
	}
	

	@Override
	public void messageArrived(String topic, MqttMessage message) throws Exception { 
		
		if(topic.equals("to/all")) {
			System.out.println("MESSAGGIO MQTT = "+message.toString());
			if(message.toString().equals("{request:description}"))
				sendDescription("from/"+Antifurto.getMqttTree()+"/antifurto/description");
			if(message.toString().equals("{request:status}"))
				sendMyStateToAll("from/"+Antifurto.getMqttTree()+"/antifurto");  // DA FARE: vedi cosa pubblicano gli altri microservizi e implementa di conseguenza questa funzione
		}
		else {
			JSONObject msgJson = new JSONObject(message.toString());
			if(topic.equals("conf/"+Antifurto.getMqttTree()+"/antifurto/sensore")) {
				if(msgJson.has("in") && msgJson.has("delta")) 
					handleConfSensoreMovimento(msgJson);
			}
			else {
				if(topic.equals("to/"+Antifurto.getMqttTree()+"/antifurto/soglia") && msgJson.has("soglia")) {
					handleSetSoglia(msgJson);
				}
				else {
					if(topic.equals("rpc/"+Antifurto.getMqttTree()+"/antifurto") && message.toString().equals("{\"request\":\"description\"}"))
						sendMyState("from/"+Antifurto.getMqttTree()+"/antifurto");  
					else {
						if(topic.equals("from/"+Antifurto.getMqttTree()+"/gpio/"+nomeOutputSuono)) {
							int newStatus = -1;
							if(msgJson.has("status"))
								newStatus = msgJson.getInt("status");
							else {
								if(msgJson.has("event"))
									newStatus = msgJson.getInt("event");
							}
							handleStateMsgFromOutputSuono(newStatus);
						}
						else {
							if(topic.equals("from/"+Antifurto.getMqttTree()+"/gpio/"+nomeOutputAntifurto)) {
								if(msgJson.has("event"))
									handleLuceAntifurto();
							}
							else {
								int event = msgJson.getInt("event");
								if(event==1) {
									// e' stato premuto l'interruttore / il sensore di movimento ha rilevato qualcuno
									if(topic.equals("from/"+Antifurto.getMqttTree()+"/gpio/"+nomeInterruttore))
										handleStateMsgFromInterruttore();
									else {
										// il topic sara' from/gruppo2/luci/gpio/IN0  dove IN0 e' un sensore di movimento
										System.out.println("TOPIC = "+topic);
										String[] t = topic.split("/");
										handleMovimento(t[4]);
									}
								}
							}
						}				
					}					
				}
				
			}
		}	
		
	}
	

	
	private synchronized void handleStateMsgFromInterruttore() throws JSONException, IOException {
		if(automa.getStatoInterruttore().equals("on")) 
			publisher.aggiungiComando("to/"+Antifurto.getMqttTree()+"/gpio/"+nomeOutputAntifurto, "{cmd:0}"); // voglio spegnere l'antifurto, quindi invio un messaggio per spegnere la luce che indica che l'antifurto e' acceso
		else 
			publisher.aggiungiComando("to/"+Antifurto.getMqttTree()+"/gpio/"+nomeOutputAntifurto, "{cmd:1}"); // voglio accendere l'antifurto, quindi invio un messaggio per accendere la luce che indica che l'antifurto e' acceso
	}
	
	private synchronized void handleLuceAntifurto() throws JSONException {
		try {
		automa.changeStatoInterruttore();
		if(automa.getStatoInterruttore().equals("off")) {
			publisher.aggiungiComando("to/"+Antifurto.getMqttTree()+"/gpio/"+nomeOutputSuono, "{cmd:0}"); 
			esec.reset();
		}		
		JSONObject js = new JSONObject();
		js.put("event", automa.getStatoInterrutoreTrueFalse());
		publisher.aggiungiComando("from/"+Antifurto.getMqttTree()+"/antifurto/antifurto", js.toString());
		}
		catch(IOException e) {
			JSONObject jsError = new JSONObject();
			jsError.put("error", e.getMessage());
			publisher.aggiungiComando("from/"+Antifurto.getMqttTree()+"/antifurto/antifurto", jsError.toString());
		}
	}
	
	
	private synchronized void handleStateMsgFromOutputSuono(int newStatus) throws JSONException, IOException {
		if(newStatus!=-1) {
			String statusOnOff = automa.converter(newStatus); // lo stato e' della forma "on" oppure "off"
			if(! statusOnOff.equals(automa.getStatoSuono()) ) {
				automa.aggiornaStatoSuono(statusOnOff);
				JSONObject js = new JSONObject();
				js.put("event", automa.getStatoSuonoTrueFalse());
				publisher.aggiungiComando("from/"+Antifurto.getMqttTree()+"/antifurto/allarme", js.toString());
			}
		}
	}
	

	
	
	private void handleMovimento(String nomeSensore) { 
		if(automa.getStatoInterruttore().equals("on"))
			esec.aggiungiVal(automa.getDelta(nomeSensore));
	}
	
	private void handleConfSensoreMovimento(JSONObject toAdd) throws JSONException { 
		// aggiorno il file che contiene l'associazione sensore-movimento, delta
		try {
			JSONObject sensori = new JSONObject(Helper.leggiFile(Automa.FILE_DELTA_SENSORI));
			String nomeSensore = toAdd.getString("in").toUpperCase();
			int delta = toAdd.getInt("delta");
			sensori.put(nomeSensore, delta);
			Helper.scriviFile(sensori, Automa.FILE_DELTA_SENSORI);
			
			// aggiorno il file zona: ho un sensore di movimento in piĆ¹
			JSONObject zona = new JSONObject(Helper.leggiFile(Antifurto.CONF_ZONA));
			zona.getJSONArray("sensoriMovimento").put(nomeSensore);
			Helper.scriviFile(zona, Antifurto.CONF_ZONA);
			Antifurto.addSensore(nomeSensore);
			automa.addDeltaSensori(nomeSensore, delta);
			client.addTopicToSubscribe("from/"+Antifurto.getMqttTree()+"/gpio/"+nomeSensore); System.out.println("from/"+Antifurto.getMqttTree()+"/gpio/"+nomeSensore);
			publisher.aggiungiComando("from/"+Antifurto.getMqttTree()+"/antifurto/sensore", toAdd.toString()); // invio questo messaggio per confermare alla web app che il sensore di movimento e' stato aggiunto		
		} catch(IOException | MqttException e) {
			JSONObject jsError = new JSONObject();
			jsError.put("error", e.getMessage());
			publisher.aggiungiComando("from/"+Antifurto.getMqttTree()+"/antifurto/sensore", jsError.toString());
		}
	}
	
	private void handleSetSoglia(JSONObject jsSoglia) throws JSONException {
		try {
			Esecutore.setSoglia(jsSoglia.getInt("soglia")); 
			JSONObject jsToSend = new JSONObject();
			jsToSend.put("soglia", Esecutore.getSoglia());
			publisher.aggiungiComando("from/"+Antifurto.getMqttTree()+"/antifurto/soglia", jsToSend.toString());
		} catch(IOException e) {
			JSONObject jsError = new JSONObject();
			jsError.put("error", e.getMessage());
			publisher.aggiungiComando("from/"+Antifurto.getMqttTree()+"/antifurto/soglia", jsError.toString());
		}
	}
	
	private void sendMyState(String topic) throws MqttException, NumberFormatException, JSONException {
		JSONObject json = new JSONObject();
		json.put("stato", automa.getStatoAutoma());
		json.put("interruttore", nomeInterruttore);
		json.put("sensori-movimento", Antifurto.getSensori());
		json.put("output-suono", nomeOutputSuono);
		json.put("soglia", Esecutore.getSoglia());
		json.put("allarme", automa.getStatoSuonoTrueFalse());
		json.put("valore-attuale", esec.getValore());
		json.put("antifurto", automa.getStatoInterrutoreTrueFalse());
		long allarmeTimeout = Esecutore.getAllarmeTimeout();
		if(automa.getStatoAutoma() == 2 && allarmeTimeout > 0)
			json.put("allarme-timeout", allarmeTimeout);
		publisher.aggiungiComando(topic, json.toString());		
	}
	
	private void sendDescription(String topic) {
		// DA FARE: guardare quale descrizione manda netmon, archiver, meter,... e confrontare l'implementazione di questo metodo
		String key = topic.replaceFirst("/description", "");
		String msg = "{\""+key+"\":[\"statoAntifurto\":\"" + automa.getStatoInterruttore()+"]"+"}";
//		
//		//DA FARE quali IN uso, quali Out uso, statoSuono, valore, soglia, statoInterruttore
//		[{"statoAntifurto":"on"},
//		    {"allarme":"on"},
//		    {"valore":5},
//		    {"soglia":1000},
//		    {"interruttore":"IN0"},
//		    {"sensoreDiMovimento":"IN1"},
//		    {"sensoreDiMovimento":"IN2"},
//		    {"sensoreDiMovimento":"IN3"},
//		    {"sensoreSuono":"OUT3"},]
//		publisher.aggiungiComando(topic, msg); 
	}
	
	private void sendMyStateToAll(String topic) {
		// DA FARE
	}
	

}