Jriver/
Jriver/src/org/jriver/core/
Jriver/src/org/jriver/core/interactive/
Jriver/src/org/jriver/lib/
Jriver/src/org/jriver/lib/daemon/
Jriver/src/org/jriver/lib/events/
Jriver/src/org/jriver/lib/interfaces/
Jriver/src/org/jriver/lib/std/
Jriver/src/org/jriver/telnet/
Jriver/src/org/jriver/telnet/.svn/
Jriver/src/org/jriver/telnet/.svn/prop-base/
Jriver/src/org/jriver/telnet/.svn/text-base/
/*
 * Copyright 2007 Kevin Roe, Daniel Mccarney
 * This file is part of Jriver.
 *
 * Jriver 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 3 of the License, or
 * (at your option) any later version.
 *
 * Jriver 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, see <http://www.gnu.org/licenses/>.
 */

/* A logon class for gathering user information
 * -=Z=- Aug07
 */

package org.jriver.lib;

import java.io.*;
import java.net.*;
import java.util.*;
import org.jriver.core.interactive.*;
import org.jriver.core.*;
import org.jriver.lib.daemon.CommandD;
import org.jriver.lib.events.ConnectionEvent;
import org.jriver.lib.events.ConnectionEvent.ConnectionState;
import org.jriver.lib.interfaces.ConnectionListener;
import org.jriver.lib.interfaces.Talkable;
import org.jriver.telnet.TelnetProtocol;

public class Player extends Living implements Interactive, Talkable, Runnable, Serializable
{
	public String username = null;
	private String password = null;
	private ArrayList<String> pendingMessages = new ArrayList<String>();
	
	transient private boolean disconnectMe = false;
	transient private BufferedReader input = null;
	transient private PrintWriter output = null;
	transient private Socket socket = null;
	transient private MudDriver driver = null;
	transient private boolean linkConnected = false;
	transient private ArrayList<Player> listeners = null;
	transient private ArrayList<ConnectionListener> connectionListeners = null;

	public static String capString(String s)
	{
		char c = Character.toUpperCase(s.charAt(0));
		return c + s.substring(1);
	}
    
	public static String processAlias(String str)
	{
		if(str.equals("l")) return "look";
		else if(str.equals("i")) return "inventory";
		else return str;
	}

	public Player()
	{
    	init();
	}
	
	public void addConnectionListener(ConnectionListener listener)
	{
		connectionListeners.add(listener);
	}

	public void addSnooper(Player snooper)
	{
		listeners.add(snooper);
	}

	public void catchTell(String message, MessageLevel level, MudObject source) 
	{
		String finalMessage = "["+ level.toString() +"] "+ message;
		
		if(!linkConnected)
			pendingMessages.add(finalMessage);
		else
			output.println(finalMessage);	
		
		//Spew it to our snoopers as well
		for(Player p : listeners)
		{
			p.catchTell(username+ ": \""+ finalMessage +"\"", 
				MessageLevel.SNOOP, this);
		}
	}

	public void disconnect()
	{
		disconnectMe = true;
	}
	
	public void fireConnectionEvent(ConnectionState newState)
	{
		ConnectionEvent e = new ConnectionEvent(this, newState);
		
		//Notify the listeners with the event
		for(ConnectionListener c : connectionListeners)
			c.connectionStateChanged(e);
	}

	public void fireConnectionEvent(ConnectionState newState, String message)
	{
		ConnectionEvent e = new ConnectionEvent(this, newState, message);
		
		//Notify the listeners with the event
		for(ConnectionListener c : connectionListeners)
			c.connectionStateChanged(e);		
	}

	public Socket getSocket() {
		return socket;
	}

	public boolean isLinkDead() {
		return !linkConnected;
	}

	public boolean isPlayer() { return true; }

	public void linkDeath() 
	{
		disconnectMe = true;
		linkConnected = false;
		socket = null;
		output = null;
		input = null;
		
		//Create a connection event
		ConnectionEvent e = new ConnectionEvent(this, ConnectionState.DISCONNECTED, 
				username + " loses link.");
		//Notify the listeners with the event
		for(ConnectionListener c : connectionListeners)
			c.connectionStateChanged(e);
		
		System.out.println(username + " loses link.\n");
    	shout(username + " loses link.\n", MessageLevel.INFO);
	}
	
	// this is an md5Hash, not the actual password	
	public String queryPassword() { return password; }

	public ArrayList<Player> querySnoopers()
	{
		return listeners;
	}

	public void recoverLink(Socket socket) 
	{
		try
		{
			this.socket = socket;
			input = new BufferedReader(
                    		new InputStreamReader(socket.getInputStream()));
			output = new PrintWriter(socket.getOutputStream(), true);
			
			//Create a connection event
			ConnectionEvent e = new ConnectionEvent(this, ConnectionState.RECONNECTED, 
					username + " regains link.");
			//Notify the listeners with the event
			if(connectionListeners != null)
				for(ConnectionListener c : connectionListeners)
					c.connectionStateChanged(e);
			
			output.println("You regain link.");
			shout(username + " regains link.", MessageLevel.INFO);
			System.out.println(username + " regains link.");
			spewMessages();
			linkConnected = true;
		} catch(IOException e) {
			e.printStackTrace();
		}
	}

	public void removeConnectionListener(ConnectionListener listener)
	{		
		if(!connectionListeners.contains(listener))
			return;
		
		connectionListeners.remove(listener);
	}

	public void removeSnooper(Player snooper)
	{
		listeners.remove(snooper);
	}
	
	public void run()
    {
        String theInput = "";

        try
        {
	        while (linkConnected && !disconnectMe && ((theInput = input.readLine()) != null))
	        {
        		theInput = TelnetProtocol.sanitize(theInput);
        		theInput = processAlias(theInput);
        		
                if (theInput.equals("who"))
                	who();
                else
                {
                	if (theInput.equals("exit"))
                    {
                        try
                        {
	                        output.println("Goodbye.\r");
	                        input.close();
	                        output.close();
	                        driver.removePlayer(this);
	                        socket.close();
                        	this.getRootNode().removeFromParent();
                        	HeartBeat.getHeartBeat().removeLiving((Living) this);
                        	
                        	//Create a connection event
        	        		ConnectionEvent e = new ConnectionEvent(this, ConnectionState.DISCONNECTED, 
        	        				username + " quits.");
        	        		//Notify the listeners with the event
        	        		for(ConnectionListener c : connectionListeners)
        	        			c.connectionStateChanged(e);
	
                            shout("DISCONNECT", MessageLevel.INFO);
                        	System.out.println("Connection [" + socket.getInetAddress()
	                                                        + "] terminated.");
                        	System.out.println("User " + username + " logged out.");
	
                            return;
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    } else {
                        CommandD.parseCommand(theInput, this);
                    }
	
                	showPrompt(output);
                }
	        }
	        
	        if(disconnectMe) //We have been requested to disconnect
	        	return;
	        
	        if(theInput == null) //EOF read
	        	linkDeath();
	        
        } catch (IOException e) {
        	System.out.println("IO Exception forced linkdeath for "+ username);
        	linkDeath();	
            return;
        }
    }
	
	public void savePlayer(File outputFile)
	{
		try
		{
			FileOutputStream fileOut = new FileOutputStream(outputFile);
			ObjectOutputStream objOut = new ObjectOutputStream(fileOut);
			objOut.writeObject(this);
			objOut.close();
		} catch(IOException e) {
			System.err.println("Unable to serialize player: "+
					username == null ? "noname" : username);
			e.printStackTrace();
		}
	}
	
	public void setInteractivity(Socket socket)
    {
            try
            {
                    input = new BufferedReader(
                                    new InputStreamReader(socket.getInputStream()));
                    output = new PrintWriter(socket.getOutputStream(), true);
                    this.socket = socket;
                    linkConnected = true;
                    return;
            } catch (IOException e) {
                    e.printStackTrace();
            }
    }
	
	//Need to add some security here in the future
	public void setPassword(String hash) { password = hash; }
	
	public void shout(String message, MessageLevel level)
    {
		Player user;
	    Iterator<Player> e = driver.getUsers().iterator();
	                    
	    while (e.hasNext())
	    {
			user = e.next();
			
			if (message.equals("DISCONNECT"))
			{
	            message = username + " has left.\r";
			}
	        else
	        {
	            if (message.equals("CONNECT"))
	            	message = username + " arrives.\r";
	            else if(level == MessageLevel.GENERAL)
	            	message = "[shout] "+username+": "+message+"\r"; 
	        }
			
			user.catchTell(message, level, this);
	    }
	    return;
    }
	
	public void showPrompt(PrintWriter out)
	{
		out.print("Jriver> ");
		out.flush();
	}
	
	public void tellObject(String message) {
		this.catchTell(message, MessageLevel.GENERAL, this);
	}
	
	public void who()
    {
            output.println("       Jriver v0.1\r");
            output.println("=------------------------=\r");
            Iterator<Player> userIterator = driver.getUsers().iterator();
            
            int numUsers;

            numUsers = driver.getUsers().size();
                        
            while(userIterator.hasNext())
            {
        		Player p = userIterator.next();
                if((p.queryName() != null))
                	output.println("  " + p.queryName() + "\r");
            }

            output.println("=------------------------=\r");
            output.println(numUsers + " users online\r");
    }
	
	private void init()
	{
		driver = MudDriver.getMudDriver();
    	driver.registerPlayer(this);
    	listeners = new ArrayList<Player>();
    	connectionListeners = new ArrayList<ConnectionListener>();
	}
	
	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
	{
		in.defaultReadObject();
		init();
	}
	
	private void spewMessages()
	{
		if(!linkConnected) return;
		
		String displayProperty = driver.configuration.getProperty("displayLinkDeathText");
		
		if(displayProperty.equals("true"))
		{
			output.println("[\"While you were gone\"...]");
			Iterator<String> pendingMessageIterator = pendingMessages.iterator();
			
			while(pendingMessageIterator.hasNext())
				output.println(pendingMessageIterator.next());
			
			pendingMessages.clear();
			output.println("[End of \"While you were gone\"...]");
		}
	}
}