/*
	Name:		Linda-client for Java
	Author:		K.Y. Hui
	Version:	1.02
	Date:		12 November 1997
*/


package Linja;

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

/*

Constructors:	LindaClient()
		LindaClient(String host,int port)

Methods:

void connect(String host,int port)
	throws SocketException, UnknownHostException, IOException

void disconnect()
	throws IOException

boolean set_timeout(int millisecond)

boolean set_protocol(char protocol)

void out(String item)
	throws IOException

String in(String template)
	throws IOException, InterruptedIOException

String in(String[] templates)
	throws IOException, InterruptedIOException

String in_noblock(String template)
	throws IOException

String rd(String template)
	throws IOException, InterruptedIOException

String rd(String[] templates)
	throws IOException, InterruptedIOException

String rd_noblock(String template)
	throws IOException

*/



public class LindaClient
{
private char protocol='p';		//'p' or 'f'
private String host=null;		//host name of connection
private int port=0;			//socket port no.
private int timeout=5000;		//default timeout value
private boolean connected=false;	//connection status flag
private Socket socket=null;		//the socket
private BufferedReader in_stream=null;	//input stream
private BufferedWriter out_stream=null;	//output stream


//
// constructor without host:port
// will be specified when connecting
//

public LindaClient()
	{
	host=null;
	port=0;
	}


//
// constructor with host:port
//

public LindaClient(String host,int port)
	{
	this.host=host;
	this.port=port;
	}


//
// object finalizer
//

protected void finalize() throws IOException
	{
	if (socket!=null)
		disconnect();
	}



//
// connect without host:port
// take host:port during creation
//
// NB: socket is not created until connecting
//

public void connect() throws SocketException, UnknownHostException, IOException
	{
	connect(host,port);
	}


//
// connect with host:port
//
// NB: socket is not created until connecting
//

public void connect(String host,int port) throws SocketException, UnknownHostException, IOException
	{
	if (host==null)
		{
		throw new UnknownHostException("no host specified");
		}

	//create socket

	socket=new Socket(host,port);

	//create streams

	in_stream=new BufferedReader(new InputStreamReader(socket.getInputStream()));
	out_stream=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

	socket.setSoTimeout(timeout);	//set default socket timeout

	if (!ping())
		{
		disconnect();		//if ping fail, disconnect
		throw new SocketException("fail in PING");
		}
	}



//
// disconnect from connection
//

public void disconnect() throws IOException
	{
	in_stream.close();		//close streams
	out_stream.close();
	socket.close();			//and socket
	}



//
// set communication timeout value
//

public boolean set_timeout(int millisec)
	{
	try	{
		socket.setSoTimeout(millisec);
		} catch (SocketException e)
			{
			return(false);
			}
	timeout=millisec;
	return(true);
	}


//
// set protocol to use (p or f)
//

public boolean set_protocol(char prot)
	{
	if (prot!='p' && prot!='f')
		{
		System.err.println("ERROR: protocol ust be 'p' or 'f'");
		return(false);
		}
	protocol=prot;
	return(true);
	}


//
// ping Linda server & wait for reponse using the default protocol
//

protected boolean ping()
	{
	return(ping(protocol));
	}


//
// ping Linda server using the specified protocol
//

protected boolean ping(char prot)
	{
	String answer;

	try	{
		to_linda(prot,"ping");
		} catch (IOException e)
			{
			return(false);
			}
	try	{
		answer=from_linda(prot);
		} catch (InterruptedIOException e)
			{
			System.err.println("ERROR: timeout in response");
			return(false);
			}
		catch (IOException e)
			{
			System.err.println("ERROR: error reading from socket");
			return(false);
			}
	return(answer.equalsIgnoreCase("pong"));	//check for "pong"
	}


//
// send to Linda connection
//

private void to_linda(char code,String data) throws IOException
	{
	to_linda(protocol,code,data);		//send using default protocol
	}


//
// send a string array to the Linda server
//

private void to_linda(char code,String[] data_list) throws IOException
	{
	StringBuffer all=new StringBuffer("[");		//start with the left bracket
	int i;

	// compose a string representing a Prolog list

	for (i=0;i<data_list.length-1;i++)
		{
		all.append(data_list[i]);
		all.append(",");			//add comma between elements
		}
	if (data_list.length>0)
		{
		all.append(data_list[data_list.length-1]);
		}
	all.append("]");				//end of list
	to_linda(code,new String(all));			//send the string to Linda
	}


//
// send to Linda using the specified protocol
//

private void to_linda(char prot,char code,String data) throws IOException
	{
	//call appropriate method according to protocol

	if (out_stream==null)
		{
		System.err.println("ERROR: output stream not opened");
		throw new IOException("fail writing to Linda");
		}
	if (prot=='p')
		{
		out_stream.write('p');			//send protocol 1st
		out_stream.write(code);			//send command code
		out_stream.write(data,0,data.length());	//send data string
		out_stream.write('.');			//the Prolog period
		out_stream.write('\n');			//send newline
//		out_stream.newLine();			//send newline
		out_stream.flush();
		}
	else if (prot=='f')
		{
		System.err.println("ERROR: protocol 'f' not yet implemented");
		}
	else	{
		System.err.println("ERROR: protocol must be 'p' or 'f'");
		}
	}




//
// read from Linda using default protocol
// returning a string
//

private String from_linda() throws IOException
	{
	return(from_linda(protocol));		//use default protocol
	}



//
// read from Linda using specified protocol
// returning a string
//

private String from_linda(char prot) throws IOException
	{
	if (in_stream==null)
		{
		System.err.println("ERROR: input stream not opened");
		throw new IOException("fail reading from Linda");
		}
	if (prot=='p')
		{
		String s=in_stream.readLine();		//string read from stream

		if (s==null)
			return(null);
		return(s.substring(0,s.length()-1));	//remove the last period
		}
	else	{
		System.err.println("ERROR: protocol 'f' not yet implemented");
		return(null);
		}
	}





//
// read from Linda using default protocol
// returning a reply in String & a status in char
//

private ReplyWithCode from_linda_with_code() throws IOException
	{
	return(from_linda_with_code(protocol));	//use default protocol
	}


//
// read from Linda with a specified protocol
// returning a string & a char code
//

private ReplyWithCode from_linda_with_code(char prot) throws IOException
	{
	if (in_stream==null)
		{
		System.err.println("ERROR: input stream not opened");
		throw new IOException("fail reading from Linda");
		}
	if (prot=='p')
		{
		ReplyWithCode answer=new ReplyWithCode();
		String s;
		char c;

		c=(char)in_stream.read();			//read char from stream
		s=in_stream.readLine();				//read string from stream
		answer.reply=s.substring(0,s.length()-1);
		answer.code=c;
		return(answer);
		}
	else	{
		System.err.println("ERROR: protocol 'f' not yet implemented");
		return(null);
		}
	}



//
// 'in' an item from Linda
//

public String in(String template) throws IOException, InterruptedIOException
	{
	socket.setSoTimeout(0);		//turn off socket timeout for blocking 'in'
	to_linda('i',template);		//send Linda the code 'i' & item template
	return(from_linda());
	}


//
// 'in' an item with multiple templates
//

public String in(String[] templates) throws IOException, InterruptedIOException
	{
	socket.setSoTimeout(0);		//turn off socket timeout for blocking 'in'
	to_linda('I',templates);
	return(from_linda());
	}



//
// 'in' without blocking
//

public String in_noblock(String template,int this_timeout) throws IOException
	{
	String answer;

	to_linda('i',template);				//it is still an "in"
	try	{
		socket.setSoTimeout(this_timeout);	//turn on socket timeout detection
		answer=from_linda();			//read from Linda server
		} catch (InterruptedIOException e)
			{
			disconnect();			//close connectioin
			connect();			//and re-connect
			return(null);			//return null
			}
	socket.setSoTimeout(0);				//turn off timeout
	return(answer);					//return the reply string
	}

public String in_noblock(String template) throws IOException
	{
	ReplyWithCode answer;

	to_linda('j',template);
	try	{
		socket.setSoTimeout(timeout);		//turn on socket timeout detection
		answer=from_linda_with_code();	//read from Linda server
		} catch (InterruptedIOException e)
			{
			socket.setSoTimeout(0);		//turn off timeout
			return(null);			//if timeout, return null
			}
	socket.setSoTimeout(0);				//turn off timeout
	if (answer.code=='s')				//if code is 's' for success
		{
		return(answer.reply);			//return the reply string
		}
	return(null);					//otherwise, return null for failure
	}


//
// 'rd' gets an item without removing it
//

public String rd(String template) throws IOException, InterruptedIOException
	{
	socket.setSoTimeout(0);		//turn off socket timeout
	to_linda('r',template);		//send the command code 'r' & template
	return(from_linda());
	}



//
// 'rd' with multiple templates
//

public String rd(String[] templates) throws IOException, InterruptedIOException
	{
	socket.setSoTimeout(0);		//turn off socket timeout
	to_linda('R',templates);	//set command code 'R' & templates
	return(from_linda());		//get answer from Linda
	}



//
// 'rd' without blocking with timeout value specified
//

public String rd_noblock(String template,int this_timeout) throws IOException
	{
	String answer;

	to_linda('r',template);				//it is still an "rd"
	try	{
		socket.setSoTimeout(this_timeout);	//turn on socket timeout detection
		answer=from_linda();			//read from Linda server
		} catch (InterruptedIOException e)
			{
			disconnect();			//close connectioin
			connect();			//and re-connect
			return(null);			//return null
			}
	socket.setSoTimeout(0);				//turn off timeout
	return(answer);					//return the reply string
	}

//
// rd without blocking
//
public String rd_noblock(String template) throws IOException
	{
	ReplyWithCode answer;

	to_linda('s',template);
	try	{
		socket.setSoTimeout(timeout);		//turn on socket timeout detection
		answer=from_linda_with_code();		//read from Linda server
		} catch (InterruptedIOException e)
			{
			socket.setSoTimeout(0);		//turn off timeout
			return(null);			//if timeout, return null
			}
	socket.setSoTimeout(0);				//turn off timeout
	if (answer.code=='s')				//if code is 's' for success
		{
		return(answer.reply);			//return the reply string
		}
	return(null);					//otherwise, return null for failure
	}




//
// 'out' an item to the Linda server
//

public void out(String item) throws IOException
	{
	to_linda('o',item);		//send item with the code 'o'
	}


}





class ReplyWithCode
{
String reply;
char code;


public void ReplyWithCode()
	{
	reply=null;
	code=' ';
	}

public void ReplyWithCode(String s,char c)
	{
	reply=new String(s);
	code=c;
	}
}

