/*
 * Dispatcher.java
 */

package vnet;

import java.util.*;
import java.net.*;
import java.io.*;
import project1.*;

/**
 * Main network dispatcher class providing basic networking for interaction with VNet server.
 * Handles for user remove/add and connect/disconnect issues.
 */
public class Dispatcher implements ClientThreadObserver {
   /**
    * Thread to perform incoming request reads.
   */
   private ClientThread thread;
   /**
    * Reference to the main applet window for referencing other objects.
   */
   private Project1 theApplet;
   /**
    * Currently connected user's id.
   */
   private String m_sUserName;
   /**
    * Whether we connected to the VNet server.
   */
   private boolean connected=false;
   /**
    * VID of the player's object
   */
   private int m_iVid;

   // ------------------------------------------------------------------------------------------------------
   /**
    * Initializer for dispatcher.
    *
    * @param cChatApplet main applet window.
   */
   public Dispatcher(Project1 cChatApplet)
   {
      theApplet= cChatApplet;
   }

   // ------------------------------------------------------------------------------------------------------
   /**
    * Login to the VNet server by certain params.
    *
    * @param hostname server name where is the VNet server resides.
    * @param port server port to connect.
    * @param username name of the current user(his/her nickname).
    * @param avatarURL url where is the avatar can be loaded from.
   */
   public void onLogin(String hostname, int port, String username, String avatarURL)
   {
      onQuit();

      m_sUserName= username;
      System.out.println("Dispatcher: Connecting to VNet server at " + hostname + " @ port " + Integer.toString(port) + " ...");
      thread = new ClientThread(this, hostname, port, username, avatarURL);
      thread.start();
   }

   // ------------------------------------------------------------------------------------------------------
   /**
    * Send information the VNet server in the same thread that accepts incoming data.
    *
    * @param vid user's id to apply information on.
    * @param field field name for that user.
    * @param value field value for that user and field.
   */
   public void send(int vid, short field, VField value)
   {
      try {
         if (thread != null && thread.connected)
            thread.send(vid, field, value);
      } catch (IOException e) {
         // Disconnect current user
         theApplet.setConnected(-2);
      }
   }

   // ------------------------------------------------------------------------------------------------------
   /**
    * Send broadcast message from current user to the VNet server.
    *
    * @param txt string to send.
   */
   public void sendMessage(String txt)
   {
      try {
         if (thread != null && thread.connected) {
            //System.out.println("Sent message out in Dispatcher");
            thread.send(m_iVid, VIP.MESSAGE, new VSFString(txt));
         }
      } catch (IOException e) {
         // Disconnect current user
         theApplet.setConnected(-2);
      }
   }

   // ------------------------------------------------------------------------------------------------------
   /**
    * Send private message from current user to the selected user through the VNet server.
    *
    * @param vid user's id to send message
    * @param txt string to send.
   */
   public void sendPrivateMessage(int vid, String txt)
   {
      try {
         if (thread != null && thread.connected)
            thread.send(vid, VIP.PRIVATE_MESSAGE, new VSFString(txt));
      } catch (IOException e) {
         // Disconnect current user
         theApplet.setConnected(-2);
      }
   }

   // ------------------------------------------------------------------------------------------------------
   /**
    * Send quit command to the VNet server.
   */
   public synchronized void onQuit()
   {
      if (thread != null && thread.connected) {
         try {
            thread.send(m_iVid, VIP.QUIT, null);
         } catch (IOException e) {

         }

         thread.connected = false;
         thread = null;

         theApplet.removeUser(m_iVid);
      }
   }

   // ------------------------------------------------------------------------------------------------------
   /**
    * Print error message to the current error device.
    *
    * @param msg message to print.
   */
   public void onError(String msg)
   {
      System.out.println("Error:" + msg);
   }

   // ---------------------------------------------------------------------------
   public String getUserName()
   {
      return m_sUserName;
   }

   // ------------------------------------------------------------------------------------------------------
   /**
    * Connection status receiver function.
    *
    * @param vid id for user that requested for connection(-1 if error occured).
   */
   public void onNetConnect(int vid)
   {
      m_iVid= vid;

      if (m_iVid< 0) {
          theApplet.setStatus("Can't log in. Try again with another name.");
      }
      else {
          m_sUserName= thread.getUserName();
          if (m_sUserName.length() > 64) {
              m_sUserName = m_sUserName.substring(0,63);
          }
      }

      theApplet.setConnected(m_iVid);
      connected= (m_iVid< 0) ? false: true;
   }

   // ------------------------------------------------------------------------------------------------------
   /**
    * Main receiver for the incoming messages from teh VNet server. It handles the following message types:
    * <OL>
    * 	<LI>POSITION - provides position for user's avatar by its id.
    * 	<LI>ORIENTATION - provides orientation for user's avatar by its id.
    * 	<LI>SCALE - provides scale for user's avatar by its id.
    * 	<LI>ADD_OBJECT - requests to add another user to the scene(world). Provides with id and url where is
    * 									this object can be loaded from.
    * 	<LI>REMOVE_OBJECT - requests to remove user from scene(world).
    * 	<LI>MESSAGE - signals message to the user
    * 	<LI>PRIVATE_MESSAGE - signals private message to the user
    * 	<LI>CREATE_OBJECT - ???????
    * 	<LI>USER_INFO - update user's name by its id.
    * </OL>
   */
   public void onNetInput(int vid, short field, VField value)
   {
      // If there was no such user, return null
      Chatter cUser= theApplet.getUser(vid);
      switch (field) {
      case VIP.POSITION:
         System.out.println("Getting Position");
         break;

      case VIP.ORIENTATION:
         System.out.println("Getting Orientation");
         break;

      case VIP.SCALE:
         System.out.println("Getting Scale");
         break;

         // New User joined the chat
      case VIP.ADD_OBJECT:
         System.out.println("Getting Add Object");
         String url = ((VSFString) value).getValue();
         if (cUser==null) {
             theApplet.addUser(vid, url, true);
         }
         break;

         // User left the chat
      case VIP.REMOVE_OBJECT:
         System.out.println("Getting Remove Object");
         theApplet.removeUser(vid);
         break;

         // The message was received
      case VIP.MESSAGE:
      case VIP.PRIVATE_MESSAGE:
         String msg=((VSFString)value).getValue();
         //If incoming message is not from me
         if (vid != m_iVid) {
             //System.out.println("Incoming Message from VID " + vid);
             theApplet.getChatPanel().appendText(msg);
         }
         break;

         // User information arrives
      case VIP.USER_INFO:
         System.out.println("Getting User Info");
         String uname = ((VSFString) value).getValue();
         if (uname.length() > 64) {
             uname = uname.substring(0,63);
         }
         cUser.setName(uname);
         break;
      }
   }

   // ------------------------------------------------------------------------------------------------------
   /**
   *
   */
   public void onNetDisconnect()
   {
   }
}
