package chris.talker;

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

public class Telnet extends chris.talker.Handler
{
    public Telnet()
    {
        super(); 
        System.out.println("Telnet " + this + " instantiated");
    }
    
    public void handleInput()
    {
        // set up the I/O streams
        in = new BufferedReader(new InputStreamReader(getInputStream()));
        out = new BufferedWriter(new OutputStreamWriter(getOutputStream()));
        
        discardInput();

        if (login())
        {
            while (bLoggedIn)
            {
                try
                {
                    processLine(in.readLine());
                }
                catch (IOException e)
                {
                    errCheck(e);
                }
            }
        }
        
        disconnect();
        
    }
    
    
    public void postMessage(Message message)
    {
        try
        {
            out.write(message.getString(), 0, message.getString().length());
            out.flush();
        }
        catch (IOException e)
        {
            errCheck(e);
        }
    }

    private void errCheck(Exception e)
    {
        System.err.println("[Telnet handler " + this + "] " + e.toString() + " (" + (iErrors + 1) + "/" + ERROR_THRESHOLD + ")");
        iErrors++;
        
        if (iErrors > ERROR_THRESHOLD || e instanceof java.net.SocketException)
        {
            disconnect();
        }
    }
    
    private boolean login()
    {
        // get the welcome message
        postMessage(new Message(TalkerRegistry.getWelcomeMessage()));

        discardInput();
        
        while (!bLoggedIn)
        {
            // ask for the username
            postMessage(new Message(TalkerRegistry.getLoginMessage()));
        
            try
            {
                strUserName = in.readLine();
                strUserName = strUserName.trim();
                if (strUserName.length() > 0)
                {
                    bLoggedIn = TalkerRegistry.register(this);
                }
            }
            catch (IOException e)
            {
                errCheck(e);
            }
        }
        
        return bLoggedIn;
    }
            
    // commands that can be prefaced with @ are declared
    // userCommand (ie, userQuit, userWhisper) etc.
    // each command is responsible for parsing the rest of the line
    
    public void processLine(String strLine)
    {
        if (strLine != null)
        {
            strLine = strLine.trim();
        
            if (strLine.length() > 0)
            {
                // check for the @ commands
                if (strLine.indexOf('@') == 0)
                {
                    // get this class
                    Class clThis = this.getClass();
                    Class clString = null;
                    Class [] claParameters = new Class[1];
                    
                    try
                    {
                        clString = Class.forName("java.lang.String");
                        claParameters[0] = clString;
                    }
                    catch (ClassNotFoundException e)
                    {
                        errCheck(e);
                    }
                    
                    strLine.trim();
                    int iBlank = strLine.indexOf(' ');
                    String strCommand = strLine;
                    String strParameters = "";;
                    if (iBlank != -1)
                    {
                        strCommand = firstUpper(strCommand.substring(1, iBlank));
                        strParameters = strLine.substring(iBlank + 1).trim();
                    }
                    else
                    {
                        strCommand = strCommand.substring(1);
                    }
                    
                    strCommand = firstUpper(strCommand);
                    strCommand = "user" + strCommand;
                    
                    try
                    {
                        Method methodCommand = clThis.getMethod(strCommand, claParameters);
                        Object [] objArgs = new Object[1];
                        objArgs[0] = strParameters;
                        methodCommand.invoke(this, objArgs);
                    }
                    catch (NoSuchMethodException e)
                    {
                        System.err.println("Invalid command:  "+strCommand);
                    }
                    catch (Exception e)
                    {
                        errCheck(e);
                    }
                }
                else
                {
                    // send a message
                    TalkerRegistry.postMessage(this, strLine);
                }
            }
        }
    }

    public String firstUpper(String strCommand)
    {
        if (strCommand.length() > 1)
        {
            return strCommand.substring(0,1).toUpperCase() + strCommand.substring(1).toLowerCase();
        }
        else
        {
            return strCommand.toUpperCase();
        }
    }
        
    
    public String getUserName()
    {
        return strUserName;
    }
    
    private void discardInput()
    {
        // discard any input
        try
        {
            while (in.ready())
            {
                in.read();              // discard input
            }
        }
        catch (IOException e)
        {
            errCheck(e);
        }
    }
        
    private void disconnect()
    {
        if (TalkerRegistry.isRegistered(this))
        {
            TalkerRegistry.unregister(this);
        }
        bLoggedIn = false;

        try
        {
            sockConnection.close();
        }
        catch (IOException e)
        {
            System.err.println(e.toString() + ".  Telnet error closing connection.");
        }
    }
    
    protected boolean register(Vector vRegistry)
    {
        boolean bRegistered = false;
        
        // check to see if user name already in the registry
        if (vRegistry.size() > 0)
        {
            // first, make a copy of the current vector
            Vector vRegistryClone = (Vector)vRegistry.clone();
               
            // now, enumerate the registered users
            for (Enumeration eT = vRegistryClone.elements(); eT.hasMoreElements(); )
            {
                // get the next registered handler and turn it into a handler object
                Telnet tObj = (Telnet)eT.nextElement();
                    
                // if the registred handler user name does not equal the prospective user name
                if (!(tObj.getUserName().equals(getUserName())))
                {
                    // register the prospective user
                    vRegistry.addElement(this);
                    bRegistered = true;
                }
                else
                {
                    // give an error message
                    postMessage(new Message("That name is already in use. Please choose another.\n"));
                }
            }
        }
        else
        {
            // first login
            vRegistry.addElement(this);
            bRegistered = true;
        }

        if (bRegistered)
        {
            System.out.println(this + " logged in.");
            TalkerRegistry.postMessage(this, "Logged in.");
        }

        return bRegistered;
    }


    public void unregister(Vector vRegistry)
    {
        if (vRegistry.indexOf(this) != -1)
        {
            vRegistry.removeElement(this);

            System.out.println(this + " logged out.");
            
            TalkerRegistry.postMessage(this, "Logged out.");
        }
    }
        

    // user commands
    public void userQuit(String strCommandLine)
    {
        bLoggedIn = false;
    }
 
    private BufferedReader in;
    private BufferedWriter out;
    private int iErrors = 0;
    private static final int ERROR_THRESHOLD = 5;
    private boolean bLoggedIn = false;
    private String strUserName;
}

