// File: abnetvrml.js
// Version: 1.05
// Date: Sept-2-2008
// Desc: javascript file containing functions to
//       support the abnet to 3dplayer communication
//       elements required for ABNet 2.0
//
// Copyright (c) 2002-2008 KimballSoftware 
// var browser = null; // vrml browser activex control

var DEF_STATUS=' ';

ABNetApp.Comm = {
    connectAttempts: 0,
    bABNetState: 0x0,
    personList: new Dictionary(),

    STATE_UNINITIALIZED:0x0,
    STATE_CONNECTING: 0x1,
    STATE_CONNECTED: 0x2,
    STATE_JOINING: 0x3,
    STATE_JOINED: 0x4,
    STATE_LEAVING: 0x5,
    STATE_NOSERVER: 0x6
};

//-- waitForConnect --

ABNetApp.Comm.waitForConnect = function() {
  switch( ABNetApp.Comm.bABNetState ) {
  case ABNetApp.Comm.STATE_UNINITIALIZED:
  case ABNetApp.Comm.STATE_CONNECTING:
    ABNetApp.Chat.append('[connecting to ABNet chat server...]');
    setTimeout('ABNetApp.Comm.waitForConnect()',1000);
    break;

  case ABNetApp.Comm.STATE_CONNECTED:
    ABNetApp.Comm.bABNetState=ABNetApp.Comm.STATE_JOINING;
    ABNetApp.Chat.append('[3d scene loaded. joining chat...]');
    ABNet.Join(ABNetApp.the3DURL.replace(/\?/g,'%3F'));
    break;

  case ABNetApp.Comm.STATE_LEAVING:
    //ABNetApp.Chat.append('waiting for leave to complete');
    setTimeout('ABNetApp.Comm.waitForConnect()',500);
    break;

  default:
    //ABNetApp.Chat.append('[Error:! Abandoning connection bABNetState=' + bABNetState + ']');
  }
};

ABNetApp.Comm.clearPersonList = function() {
    ABNetApp.Comm.personList = new Dictionary();
    ABNetApp.Chat.gUsersComposing=[];
};

// -- netEventHandler -- this is where we handle the inbound events from
//     the abnet communication plugin.  
// Note: this is a javascript object to so we can quickly
// dispatch the events for each type of message

ABNetApp.netEventHandler = {

  'onConnect': function(f,e,v) {
    ABNetApp.Comm.bABNetState=ABNetApp.Comm.STATE_CONNECTING;
    ABNet.Attach(escape(ABNetApp.User.me.username), 'nopassword', escape(ABNetApp.User.me.avatarURL), "1");
  },

  'onAttach': function(f,e,v) {
    ABNetApp.Comm.bABNetState=ABNetApp.Comm.STATE_CONNECTED;
    ABNetApp.User.me.uniqID=v;
    ABNetApp.Chat.append('[connected to ABNet server]');
  },

  'onDetach': function(f,e,v) {
    // new abnet server supports this
    // need to dump the user list 
    // and our info
  },

  // we get notification for ourselves and also for others
  // the chat server sends us a unique message for each user
  'onJoin': function(f,e,v) {
    var theUser;
    
    if ( f == ABNetApp.User.me.uniqID ) {
      ABNetApp.Comm.personList[f]=ABNetApp.User.me;
      theUser = ABNetApp.Comm.personList[f];
      ABNetApp.Comm.bABNetState=ABNetApp.Comm.STATE_JOINED;
    }
    else {
      ABNetApp.Comm.personList[f]=new Person();
      theUser = ABNetApp.Comm.personList[f];
      theUser.fromString(v);
      theUser.uniqID=f;
    }
    
    ABNetApp.Chat.uiUpdateUserList();

    setTimeout(function(){ ABNetApp.Scene.loadAV(f); }, 100); // delay

    if ( theUser.isMe ) {
      ABNetApp.Chat.append('[you have joined as ' + theUser.username + ']');
    }
    else {
      ABNetApp.Chat.append('[' + unescape(theUser.username) + ' joined]');
    }
  },

  'onLeave': function(f,e,v) {
    var theUser = ABNetApp.Comm.personList[f];

    if ( theUser ) {
      if ( theUser.isMe ) {
	// revert the COMM state back to being CONNECTED but not JOINED
	// this occurs when the 3d scene changes, see the ABNetApp.Scene
	ABNetApp.Comm.bABNetState=ABNetApp.Comm.STATE_CONNECTED;

	// don't bother to unload our avatar as we are going to refresh the scene
	// create a new person Dictionary;
	ABNetApp.Comm.clearPersonList();
	ABNetApp.Chat.append('[3d scene changing. you left chat in that world]');
      }
      else {
	// unload the users avatar and clear out any pending stuff
	ABNetApp.Scene.unloadAV(f);
	ABNetApp.Chat.append('[' + theUser.username + ' left]');
	if ( theUser && theUser.typing ) {
	  theUser.typing=false;
	  ABNetApp.Chat.chatStatusMsg(theUser);
	}
	var bItemDelete = delete ABNetApp.Comm.personList[f];
	if ( !bItemDelete ) {
	  debugger; // programming error 
	}
      }

      // update our user interface with new info
      ABNetApp.Chat.uiUpdateUserList();
    }
  }, 

  'onUserCountChanged': function(f,e,v) {
    try {
      usercnt.innerText = v;
    }
    catch(e) {
      worldRefresh(2000);
    }
  },

  'onZoneCountChanged': function(f,e,v) {
    try {
      zonecnt.innerText = v;
    }
    catch(e) {
      worldRefresh(2500);
    }
  },

  'onDisconnect': function(f,e,v) {
    if ( ABNetApp.Comm.bABNetState == ABNetApp.Comm.STATE_CONNECTING || ABNetApp.Comm.bABNetState == ABNetApp.Comm.STATE_UNINITIALIZED )
      ABNetApp.Chat.append('[Error! unable to connect to ABNet chat server]');
    else
      ABNetApp.Chat.append('[ABNet chat server shutting down, you have been disconnected]');

    ABNetApp.Comm.bABNetState=ABNetApp.Comm.STATE_NOSERVER;
    try {
      usercnt.innerText = '0';
      zonecnt.innerText = '0';
    }
    catch(e) {
    }
  },

  // location message from other users as they move
  'onPC': function(f,e,v) {
    var theUser=ABNetApp.Comm.personList[f];

    if ( theUser ) {
	var avFldName="__av"+f;
	ABNetApp.Scene.setFieldValue(avFldName,'translation', v);
    }
  },
  
  // orientation message from other users
  'onOC': function(f,e,v) {
    var theUser=ABNetApp.Comm.personList[f];

    if ( theUser ) {
      var avFldName="__av"+f;

      // we have to flip the avatar around because they
      // are built so that they face you so they are
      // really going the wrong way from the moment they are born
      if ( false ) {
      	var avOrientation = ABNet.RotateY180(v);
      	ABNetApp.Scene.setFieldValue(avFldName,'rotation',avOrientation);
      } else {
      	ABNetApp.Scene.setFieldValue(avFldName,'rotation',v);
      }
    }
  },

  // tell another user where we are in the scene
  'getLocation': function(f,e,v) {

    // once our avatar loads in someone elses 3d plugin
    // the user will request our position. We might have
    // moved since they requested the load and by the time
    // it actually finishes loading on their computer it
    // might be a long time depending on where your avatar is
    // hosted, their network speed to that web server and
    // the power and speed of their cpu and graphics card.
    // We just send our latest position and orientation so
    // they can catch up with our current state in case 
    // we never moved again.

    ABNet.SendTo(f,'onOC',ABNetApp.User.me.o);
    ABNet.SendTo(f,'onPC',ABNetApp.User.me.p);
  },
    
  //-- chat related messages --
  'onCompose': function(f,e,v) {
    var theUser = ABNetApp.Comm.personList[f];

    if ( theUser ) {
      if ( parseInt(v) == 1 ) {
	theUser.typing=true;
      }
      else {
	theUser.typing=false;
      }
    }
    ABNetApp.Chat.chatStatusMsg(theUser);
  },

  'onChat': function(f,e,v) {
    var chatMsg=unescape(v);

    ABNetApp.Chat.append(chatMsg);
    var justMsg=chatMsg;
    var jmi = justMsg.indexOf(': ');
    if ( jmi != -1 ) justMsg=justMsg.substring(jmi+2);
    ABNetApp.Scene.setFieldValue('ABNetSharedEvents','groupChat',justMsg);

    var theUser = ABNetApp.Comm.personList[f];
    if ( theUser ) {
      theUser.typing=false;
      ABNetApp.Chat.chatStatusMsg(theUser);
    }
  },

  //-- avatar related messages --
  'onGesture': function(f,e,v) {
    var avFldName="__av"+f+"";

    ABNetApp.Scene.setFieldValue(avFldName,'set_gesture',v);
    var theUser = ABNetApp.Comm.personList[f];
    theUser.typing=false;
    ABNetApp.Chat.chatStatusMsg(theUser);
  },
    
  //-- shared chat message from the vrml scene --
  'onSharedChat': function(f,e,v) {

    ABNetApp.Chat.append(unescape(v));
  }

  // TBD: add a ping timer so that the speed of the room
  // is adjusted for the slowest person in the room
  // also figure out the shortest walk cycle of the
  // of all avatars and adjust the timer in the ABNet interface
  // the ABNet Avatar Timer needs to be adjusted so it 
  // is the minmum of those 2 ping time and the shortest avatar walk cycle
};

function OnABNetEvent(f, e, v)
{
  if ( bDebug & DEBUG_ABNET ) { ABNetApp.Chat.append('AB::<<event=' + e + ',from=' + f + ',value=' + v); }

  // see if the message is one of the abnet base ones
  if ( ABNetApp.netEventHandler.hasOwnProperty(e) ) {
    ABNetApp.netEventHandler[e](f,e,v);
    return;
  }
  else { 
    // we pass all unrecognized net events to the vrml
    // scene so it can try and do something with them
    // you can use any event name as long as it doesn't
    // override the default netevents names
    ABNetApp.Scene.setFieldValue('ABNetSharedEvents', e, unescape(v));
  }
};
