package no.divvun.Analyzer.Client;

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

import no.divvun.Analyzer.Communication.*;

/**
 * This class implements basic TCP socket connection used for sending
 * requests and receiving responses.
 * @author tomi
 */
public class DivvunServerConnectionSocket implements DivvunServerConnection {
	DivvunRequestListener listener;
	Socket connection;
	PrintWriter outputStream;
	InputStream inputStream;
	DivvunRequestStatus requestStatus;
	ResponseParser responseParser;
	
	public DivvunServerConnectionSocket () {}
	
	private void requestStatusChanged(int status) {
		requestStatus.setStatus(status);
		listener.requestStatusChanged(requestStatus);
	}
	
	private void openConnection() throws IOException {
		requestStatusChanged(2);
		outputStream = new PrintWriter(connection.getOutputStream(), true);
		inputStream = connection.getInputStream();
		BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
//		System.out.println(in.readLine());
		in.readLine();
	}
	
	private Response receiveResponse()
		throws DivvunServerConnectionException
	{
		requestStatusChanged(4);
		Response response = null;
		DivvunServerConnectionException exception = null;
		
		try {
/*			responseParser.parseResponse(inputStream);
			response = responseParser.getResponse();
/* DEBUG the response  received
*/			String line;
			boolean hasMore = true;
			StringBuilder xmlBuilder = new StringBuilder();
			BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
			
			line = in.readLine();
			while (hasMore) {
				xmlBuilder.append(line + "\n");
				line = in.readLine();

				if (line.equals("</" + XMLTags.TAG_RESPONSE_PARA + ">") ||
					line.equals("<" + XMLTags.TAG_RESPONSE_PARA + "/>") ||
					line.equals("</" + XMLTags.TAG_RESPONSE_HYPH + ">") ||
					line.equals("<" + XMLTags.TAG_RESPONSE_HYPH + "/>") ||
					line.equals("</" + XMLTags.TAG_RESPONSE_GEN + ">") ||
					line.equals("<" + XMLTags.TAG_RESPONSE_GEN + "/>"))
					hasMore = false;
			}
			responseParser.parseResponse(xmlBuilder);
			response = responseParser.getResponse();
		}
		
		catch (Exception e) {
			exception = new DivvunServerConnectionException(e.getMessage());
		}
		
		if (exception != null)
			throw exception;
		else
			return response;
	}
	
	public void initialize(DivvunRequestListener listener, String serverAddress, int serverPort)
		throws DivvunServerConnectionException 
	{
		try {
			connection = null;
			requestStatus = new DivvunRequestStatus();
			this.listener = listener;
			connection = new Socket(serverAddress, serverPort);
			openConnection();
			responseParser = new ResponseParser();
		}
		
		catch (ResponseParserException e) {
			throw new DivvunServerConnectionException(e.getMessage());
		}
		
		catch (UnknownHostException e) {
			throw new DivvunServerConnectionException(e.getMessage());
		}
		
		catch (IOException e) {
			throw new DivvunServerConnectionException(e.getMessage());
		}
	}
	
	public Response sendRequest(Request request) {
		Response response = null;
		try {
//			System.out.println(request.toXML());
/*			response = new Response();
			response.getElementContainer().add(new Paradigm());
*///		outputStream.println(new String(request.toXML().getBytes(), "UTF-8"));
			outputStream.println(request.toXML());
			if (request.getType() != Request.INIT)
				response = receiveResponse();
		}
		
		catch (Exception e) {
			response = new Response();
			response.setStatus(-1);
			response.setErrorMessage("Failed to process given request! Reason: " + e.getMessage());
		}

		return response;
	}
	
	public void closeConnection() throws IOException {
		IOException exception = null;
		
		try {
			if(outputStream != null) {
				outputStream.print(XMLTags.END_CLIENT);
				outputStream.close();
				outputStream = null;
			}
			if(inputStream != null) {
				inputStream.close();
				inputStream = null;
			}
		}
		catch (IOException e) {
			outputStream = null;
			inputStream = null;
			exception = e;
		}

		if(connection != null) {
			connection.close();
			connection = null;
		}
		
		requestStatusChanged(5);
		if(exception != null)
			throw exception;
		else
			return;
	}
}
