//tale classe(thread) ha il compito di eseguire le richieste accettate e di recuperare i dati richiesti
//sia in un DB locale che inoltrando (se necessario) la richiesta ad altre reti(ai Master di tali reti!)


import java.io.*;
import java.net.*;
import java.util.*;

public class SlaveExeMaster extends Thread{
	private int basePort;
	private DatagramPacket pacchetto;
	private int myIndex;
	private DatagramPacket input=null;
	private String inRich;
	private InetAddress pacAdd;
	private DatagramSocket ds=null;

	
	
	
	
	public SlaveExeMaster(int p, DatagramPacket dp){
		super("SLAVE" + p);
		synchronized(this){
			System.out.println("sono dentro al costruttore di: " + new String(dp.getData(), 0, dp.getLength()));
			pacchetto=dp;
			inRich = new String(pacchetto.getData(), 0, pacchetto.getLength());
			pacAdd=pacchetto.getAddress();
			ConstantsS.indexS++;
			myIndex=ConstantsS.indexS;
			basePort=p + 5 + myIndex; //porta udp verso il master da contattare per la risposta
		}
	}//costruttore
	
	public void run(){
		byte[] data;
		byte[] buffer = new byte[65507];
		
		DatagramPacket  output=null;
		
		
		try{
			Constants.prn(myIndex+"-provo a connettermi su porta: " + basePort);
			ds = new DatagramSocket(basePort);
			input=new DatagramPacket(buffer, buffer.length);
		}
		catch (Exception e){
			System.out.println(myIndex+"-errore crezione socket per richieste: "+e);
			ConstantsS.numRichiesteMaster--;
			ConstantsS.ackArrivato.remove(inRich);
			ConstantsS.slaveLoad--;
			return;
		}

		
		
		//scanno la richiesta x recuperare i campi che mi interessano
		//N.B. la richiesta è formata dai 4 campi soliti più la porta UDP dove dovrò rispondere quando avro' finito
		try{
			//String inRich = new String(pacchetto.getData(), 0, pacchetto.getLength());
			System.out.println(myIndex+"-richiesta master: " + inRich);
			BufferedReader br1 = new BufferedReader(new StringReader(inRich));

			
			String[] richiesta = new String[4];
			String portaMaster="";
			int i=0;
			String line;
			while ((line=br1.readLine())!=null){
				if (i<4){
					richiesta[i]=line;
				}
				else{
					portaMaster=line;
					System.out.println("il Master ha fornito alla richiesta la porta: " + portaMaster);
					//dopo che sono arrivato alla portamaster la richiesta è finita, se non lo è do errore!!
					if ((line=br1.readLine())!=null){
						ConstantsS.numRichiesteMaster--;
						System.out.println(myIndex+"-errore numero parametri richiesta Master");
						ConstantsS.slaveLoad--;
						return;
					}
					//dopo la portamaster esco xche la richiesta è finita
					break;
				}
				i++;
				System.out.println(line);
			}




		
			int masterPort=Integer.parseInt(portaMaster);
			int ttl=Integer.parseInt(richiesta[3]);

			
			System.out.println(myIndex+"-quando ho finito vado sulla porta: " + masterPort);
			Constants.prn(myIndex+"-comincio a lavorare");
			//inoltro solo se non è finito il TTL del pacchetto!!!
			inoltraQueryMaster  iqm=null;
			if (ttl>0){
				iqm = new inoltraQueryMaster(richiesta, myIndex);
				ConstantsS.slaveLoad++;
				iqm.start();
				//Tale thread inoltra la query e modifica alla fine una variabile in ConstantsS in cui c'è il numero di file reperiti
			}
			String ris=this.cercaDbLocale(richiesta);
			if (iqm!=null){
				iqm.join();//attendo che il thread finisca; è bloccante!!!
				ConstantsS.slaveLoad--;
			}
			//debug
			//i nomi dei file DEVONO essere cosi:
			//i nomi dei file in input sono "indexS-0.dat" fino a "index-numFile.dat"
			
			//File dest = new File(index + "-finito.dat");
			
			fondiFile ff = new fondiFile(ConstantsS.numReperiti + 1, myIndex);//il + 1 rappresenta il file locale
			ris=ff.esegui();//in ris ho il nome del file oppure errore
			System.out.println(myIndex+"-risultato fondiFile: " + ris);
			Constants.prn(myIndex+"-mi sono svegliato");
			//la porta TCP è la stessa porta UDP dalla quale lavoro!!!	
			String msg = "" + ConstantsS.portaSlave;
			//come ack che ho finito gli mando la porta dalla quale ascolto i master, cosi' lui controlla su contactSlave con la chiave giusta!!!
			data = new byte[msg.length()];
			data = msg.getBytes();
			System.out.println(myIndex+"-mando l'ack che ho finito su: "+ masterPort);
			//DEBUG RIELEZIONE
			output=new DatagramPacket(data, data.length, InetAddress.getByName(ConstantsS.masterIP), masterPort);

			
			//output=new DatagramPacket(data, data.length, pacAdd, masterPort);
			ds.send(output);
			Constants.prn(myIndex+"-attendo su porta: " + basePort);
			ds.receive(input);
			ServerSocket socket = new ServerSocket(basePort);
			Constants.prn(myIndex+"-attendo connessione su serversocket"+ basePort);
			Socket s = socket.accept();
			Constants.prn(myIndex+"-è arrivata connessione, devo scrivere!!");

			BufferedReader inp = new BufferedReader(new InputStreamReader(s.getInputStream()));
			PrintWriter out = new PrintWriter(s.getOutputStream(), true);
			try {
				if (ris!="errore"){
					FileReader fis = new FileReader(ris);
					BufferedReader br = new BufferedReader(fis);
					String inStr;
					String parti = inp.readLine();//mi da il via il master
					while (       (inStr = br.readLine())     != null){
						out.println(inStr);
					}
					out.println("..");
					fis.close();
				}
				else{
					out.println("..");
					System.out.println(myIndex+"-scrivo linea: ..");
				}
				out.flush();
				System.out.println(myIndex+"-ho scritto il risultato su: " + out);
				String fermati = inp.readLine();
				
				out.close();
				inp.close();
				System.out.println(myIndex+"-ok, dati scritti su: " + out);	
				
			} 
		
			catch ( Exception e){
				out.println(myIndex+"-errore download file-"+e);
				out.flush();
				out.close();
				inp.close();
			}
			
			ConstantsS.numRichiesteMaster--;
			ConstantsS.ackArrivato.remove(inRich);
			Constants.prn(myIndex+"-muoio");
			
		}
		catch (Exception e){
			System.out.println(myIndex+"-errore: "+e);
			ConstantsS.numRichiesteMaster--;
			ConstantsS.ackArrivato.remove(inRich);
			ConstantsS.slaveLoad--;
			
			return;
		}
	}//run
	
		
	
	//metodo che cerca nel db locale
	private String cercaDbLocale(String[] richiesta){
		//cerco localmente e metto il risultato in myIndex-O.dat
		//alla fine devo appiccicarci . ".." finali!!!
		try{
			System.out.println(myIndex+"-Cerco nel db locale...");
			File f = new File(myIndex+"-0.dat");
			PrintWriter pw = new PrintWriter(new FileWriter(f));
			//pw.println(myIndex +"-"+ this.getName() +"-"+ richiesta[0] + "-" + richiesta[1] + "-" + richiesta[2] + "-" + richiesta[3] + "-");
			System.out.println("Index: " + myIndex +";Nodo: "+ ds.getLocalAddress().getLocalHost().toString() + "Thread: "+ this.getName()+"; Richiesta: "+ richiesta[0] + "-" + richiesta[1] + "-" + richiesta[2] + "-" + richiesta[3]);
			pw.println("Index: " + myIndex +";Nodo: "+ ds.getLocalAddress().getLocalHost().toString()  + "Thread: "+ this.getName()+"; Richiesta: "+ richiesta[0] + "-" + richiesta[1] + "-" + richiesta[2] + "-" + richiesta[3]);

			pw.flush();
			pw.close();
		}
		catch (Exception e){
		}
		return "";
	}
	
	
	
}//class