/*
 Eteria IRC Client, an RFC 1459 compliant client program written in Java.
 Copyright (C) 2000-2001  Javier Kohen <jkohen at tough.com>
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
 import java.io.*;
 import java.net.Socket;
 import java.net.SocketException;
 import java.text.ParseException;
 import java.util.Observable;
 import java.util.Observer;
 
 import ar.com.jkohen.irc.*;
 import ar.com.jkohen.net.*;
 import ar.com.jkohen.util.ConfigurationProperties;
 
 public class ServerThread extends Thread implements ServerProcess, Observer
 {
 	private Socket sock;
 	private ClientProcess client;
 	private BufferedInputStream reader;
 	private BufferedOutputStream writer;
 	private CharsConverter converter;
 	
 	private boolean disconnected;
 
 	/* Configuration properties. */
 	private boolean filter_mirc_attribs = false;
 	
 	byte buffer[] = new byte[512];
 	public long time;
 	public long delay;
 
 	public ServerThread(ClientProcess cl, Socket sk, String decoding, String encoding, int ping) throws IOException
 	{
 		client = cl;
 		sock = sk;
 		converter = new CharsConverter(decoding, encoding);
 		delay = ping * 1000;
 		
 		reader = new BufferedInputStream(sock.getInputStream());
 		writer = new BufferedOutputStream(sock.getOutputStream());
 		
 		try
 		{
 			// FIXME: this doesn't seem to be working.
 			// Prevents the lock-up that occurs when the route to the peer is dropped.
 			sock.setSoLinger(true, 30);
 		}
 		catch (SocketException ex) { System.err.println(ex); }
 	}
 	
 	public void run()
 	{
 		while (!disconnected)
 		{
 			try
 			{
 				processNextLine();
 			}
 			catch (Exception ex)
 			{
 				ex.printStackTrace();
 				client.reportException(ex);
 			}
 		}
 	}
 
 	private void processNextLine() throws Exception
 	{
 		String str = readLine();
 		time = System.currentTimeMillis();
 
 		if (str != null)
 		{
 			try
 			{
 				Message m;
 				if (filter_mirc_attribs)
 					m = new MircMessage(str);
 				else
 					m = new Message(str);
 				client.processMessage(m);
 			}
 			catch (ParseException e)
 			{
 				System.err.println(e);
 			}
 		}
 	}
 
 	public String readLine() throws Exception
 	{
 		boolean eol = false;
 		int i = 0, num = 0;
 		while (!eol)
 		{
 			try
 			{
 				i = reader.read();
 			}
 			catch (IOException ex)
 			{
 				i = -1;
 				if (!disconnected)
 					client.reportIOException(ex);
 			}
 			
 			if (i == -1 || ((i == 13 || i == 10) && num > 0) || num == 512)
 			{
 				eol = true;
 			}
 			else if (i != 13 && i != 10)
 			{
 				buffer[num++] = (byte)i;
 			}
 		}
 		
 		if (i == -1)
 		{
 			client.disconnect();
 			return(null);
 		}
 		
 		return(converter.decode(buffer, num));
 	}
 
 	public void enqueueMessage(Message m) throws IOException
 	{
 		String [] slices = m.slices();
 		for (int i = 0; i < slices.length; i++)
 			writer.write(converter.encode(slices[i]));
 
 		writer.flush();
 	}
 
 	public boolean timedOut()
 	{
 		return((System.currentTimeMillis() - time) > delay);
 	}
 	
 	public void disconnect()
 	{
 		if (disconnected)
 			return;
 
 		disconnected = true;
 		
 		try
 		{
 			writer.close();
 		}
 		catch (IOException e) {}
 
 		try
 		{
 			reader.close();
 		}
 		catch (IOException e) {}
 		
 		try
 		{
 			sock.close();
 		}
 		catch (IOException e) {}
 	}
 	
 	public Socket getSocket()
 	{
 		return(sock);
 	}
 	
 	public void setCharsConverter(CharsConverter c)
 	{
 		converter = c;
 	}
 
 	public void update(Observable o, Object arg)
 	{
 		ConfigurationProperties props = (ConfigurationProperties) o;
 
 		if (arg == null || arg.equals("filter_mirc_attribs"))
 			this.filter_mirc_attribs = props.getBoolean("filter_mirc_attribs");
 	}
 }