// The client-side game state

// Handles user actions to relay to the server, as well as maintaining internal state that can be used to save trips.

var PQGameState = Class.create({
	initialize: function()
	{
		this._window_drag_bonus = 10000;	// How much higher than this window should it be when dragged?
		this._in_combat = false;
		this._avatar_cache = new Array();
		this._is_facebook = false;
		this._player_stats = false;
		this._player_avail_skills = {};
	},
	
	ShowWindow: function( window_id )
	{
		if( !g_window_cache[window_id] )
			return;
		g_window_cache[window_id].show();
	},
	
	CloseWindow: function( window_id )
	{
		if( !g_window_cache[window_id] )
			return;
		g_window_cache[window_id].hide();
	},
	
	SetCombatMode: function()
	{
		this._in_combat = true;
		
		this.ShowWindow( 'combatmap' );
		this.ShowWindow( 'combatpanel' );
		this.CloseWindow( 'targetselectionpanel' );
		this.CloseWindow( 'pvppanel' );
	},
	
	SetNormalMode: function()
	{
		this._in_combat = false;
		
		this.CloseWindow( 'combatmap' );
		this.CloseWindow( 'combatpanel' );
		this.CloseWindow( 'targetselectionpanel' );
	},
	
	ExtractStateInformation: function( response )
	{
		// Pull out data from the response for internal use later
		var temp = response.getAttribute( 'in_combat' );
		if( temp != undefined )
		{
			if( temp == true )
			{
				this.SetCombatMode();
			}
			else
			{
				this.SetNormalMode();
			}
		}
	},
	
	// based on the stored information for the player's clothing, construct a paperdoll in a div for that player
	// Or, pass in a layer array as the second argument to make one from that set instead
	ConstructPaperdoll: function( is_large, layerset )
	{
		var av_zindex = 0;
		var av_imgname = "";
		if( is_large == true )
		{
			var av_width = 64;
			var av_height = 128;
			var av_ext = ".png";
			var av_classname = "paperdoll-large";
		}
		else
		{
			var av_width = 32;
			var av_height = 32;
			var av_ext = ".gif";
			var av_classname = "paperdoll-small";
		}
		
		var wrapper = new Element( 'div' );
		wrapper.style.width = av_width;
		wrapper.style.height = av_height;
		
		if( layerset == undefined )
			layerset = this._clothing_layers;
		
		if( layerset == undefined )
			return;	// Still got nothing?  bail now.
		
		var len = layerset.length;
		for( var i = 0; i < len; i++ )
		{
			layer = layerset[i];
			
			av_zindex = layer['layer'];
			av_imgname = layer['imagename'];

			var av_fullpath = "tiles/" + av_imgname + av_ext;
			
			theImg = new Element( "img", {	'class': av_classname,
											'src': av_fullpath,
											'width': av_width,
											'height': av_height } );
			theImg.style.zIndex = av_zindex;
			wrapper.appendChild( theImg );
		}
		
		return wrapper;
	},
	
	SetClothing: function( clothing )
	{
		this._clothing_layers = clothing;
	},
	
	UnequipConfirm: function( the_event, name, itemid )
	{
		// Construct an unequip confirmation window
		// Create the window
		if( !g_window_cache['unequip_confirm'] ) {
			g_window_cache['unequip_confirm'] = new Window({'id':'pqwindow_js_unequip_confirm', className:'pq_theme', title: "Are you sure?", 'left': 287, 'top': 98, 'width':424, 'height':345});
			g_window_cache['unequip_confirm'].show();
			g_window_cache['unequip_confirm'].setCookie( 'pqwindow_js_unequip_confirm', new Date( 'Thu, 31 Dec 2099 23:59:59 UTC' ), '', '', false );
		}
		var frame = g_window_cache['unequip_confirm'];

		// Actually construct the HTML
		var wrapper = new Element( 'div', { 'id': 'unequip_confirm_wrapper' } );

		var div = new Element( 'div' );
		div.update( "<p>Equipping the " + htmlentities( unescape( name ) ) + " costs XP.</p><p>Given that you already spent XP on this in the past, are you sure you want to take it off now?</p>" );
		wrapper.insert( div );
		
		var input = new Element( 'input', { 'type': 'button', 'value': 'Toss it!' } );
		Event.observe( input, 'click', game_state.Unequip.bindAsEventListener( game_state, itemid ) );
		wrapper.insert( input );
		
		var input = new Element( 'input', { 'type': 'button', 'value': 'Nevermind' } );
		Event.observe( input, 'click', game_state.EventCloseWindow.bindAsEventListener( game_state, 'unequip_confirm' ) );
		wrapper.insert( input );
		
		// Kill the previous wrapper, if there
		frame.getContent().update( wrapper );
		frame.show();
		frame.toFront();
	},
	
	ShowAttachPackages: function()
	{
		$( 'attachbutton' ).style.display = 'none';
		$( 'attachlist' ).style.display = 'block';
	},
	
	// Action Response Handler relay
	HandleResponse: function( action, response )
	{
		// Pull out data from the response for internal use later
		this.ExtractStateInformation( response );
		
		// Pick an action handler
		switch( action )
		{
			case 'heartbeat':
				this._HandleHeartbeat();
				break;
			case 'refresh_ingame':
				this._HandleRefreshIngame();
				break;
			case 'show_consumables':
				this._HandleShowConsumables();
				break;
			case 'show_skillgempanel':
				this._HandleShowSkillgems();
				break;
			case 'show_characterext':
				this._HandleShowCharacterExtDetails();
				break;
			case 'show_inventory':
				this._HandleShowInventory();
				break;
			case 'show_clothes':
				this._HandleShowClothes();
				break;
			case 'show_prefs':
				this._HandleShowPrefs();
				break;
			case 'address_book':
				this._HandleShowAddressBook();
				break;
			case 'current_effects':
				this._HandleShowCurrentEffects();
				break;
			case 'open_quests':
				this._HandleShowOpenQuests();
				break;
			case 'show_effect':
				this._HandleShowEffectDetail();
				break;
			case 'show_itemdetail':
				this._HandleShowItemDetail();
				break;
			case 'character_detail':
				this._HandleShowCharacterDetail();
				break;
			case 'enter_area':
				this._HandleEnterArea();
				break;
			case 'equip':
				this._HandleEquip();
				break;
			case 'unequip':
				this._HandleUnequip();
				break;
			case 'healer':
				this._HandleHealer();
				break;
			case 'monster_detail':
				this._HandleShowMonsterDetail();
				break;
			case 'quest_detail':
				this._HandleShowQuestDetail();
				break;
			case 'quests':
				this._HandleQuests();
				break;
			case 'shop':
				this._HandleShop();
				break;
			case 'show_change_password':
				this._HandleShowChangePassword();
				break;
			case 'show_make_banner':
				this._HandleShowMakeBanner();
				break;
			case 'show_customization':
				this._HandleShowCustomization();
				break;
			case 'show_fb_invite':
				this._HandleShowFacebookInvite();
				break;
			case 'use_item':
				this._HandleUseItem();
				break;
			case 'use_skillgem':
				this._HandleUseSkillgem();
				break;
			case 'view_mall':
				this._HandleShowMall();
				break;
			case 'view_side_quest':
				this._HandleShowSideQuestDetail();
				break;
			case 'buy_player_shop':
				this._HandleBuyPlayerShop();
				break;
			case 'complete_quest':
				this._HandleCompleteQuest();
				break;
			case 'complete_side_quest':
				this._HandleCompleteSideQuest();
				break;
			case 'side_quest_accept':
				this._HandleSideQuestAccept();
				break;
			case 'enter_mall_shop':
				this._HandleEnterMallShop();
				break;
			case 'post_office':
				this._HandlePostOffice();
				break;
			case 'gold_shop':
				this._HandleGoldShop();
				break;
			case 'pvp_panel':
				this._HandlePvPPanel();
				break;
			case 'accept_challenge':
				this._HandleAcceptChallenge();
				break;
			case 'manage_shop':
				this._HandleManageShop();
				break;
			case 'player_shop':
				this._HandlePlayerShop();
				break;
			case 'show_select_target':
				this._HandleShowSelectTarget();
				break;
			case 'show_target_player':
				this._HandleShowTargetPlayer();
				break;
			case 'address_book_search':
				this._HandleAddressBookSearch();
				break;
			case 'show_customize_shop':
				this._HandleShowShopCustomization();
				break;
			case 'show_decorations_shop':
				this._HandleShowDecorationsShop();
				break;
			case 'builder_tools':
				this._HandleBuilderTools();
				break;
			case 'builder_tiles_refresh':
				this._HandleBuilderTilesRefresh();
				break;
			case 'customize_player_house':
				this._HandleCustomizeHouse();
				break;
			case 'admintools':
				this._HandleAdminTools();
				break;
			case 'refresh_character':
				this._HandleRefreshCharacter();
				break;
			default:
				// No need to do anything - message simply doesn't require a response handler.
				// TODO: handle these the same as anything else
//				if( action != "load_tooltip" )
//				{
//					alert( "Hello!  Sorry for the intrusion, but it seems that Xaroth missed one during his conversion.\n\nPlease post in the forums to let him know he missed '" + action + "', along with a note about what you were doing just now.\n\nThanks!" );
//				}
				break;
		}
	},
	
	// Actions / Action Response Handlers
	RefreshIngame: function()
	{
		// Show the first couple of windows, and pre-load some important data for later
		
		// Give us a starting set of data
		SendRequest( "refresh_ingame" );
	},
	
	_HandleRefreshIngame: function()
	{
		this.ShowWindow( 'mainmap' );
		this.ShowWindow( 'quickbelt' );
		this.ShowWindow( 'characterstats' );
		this.ShowWindow( 'chattext' );
		this.ShowWindow( 'addressbook' );
		
		// Init the spinner...
		Spin();
		
		// Start the ad rotation
		UpdateAd();
		
		// Start up the heartbeat
		this.Heartbeat();
		
		// Open a default set of windows
		this.ShowOpenQuests();
//		this.ShowAddressBook();
	},
	
	ShowConsumables: function()
	{
		var temp = g_window_cache['consumablespanel'];
		if( temp )
			this._HandleShowConsumables();
		else
			SendRequest( 'show_consumables' );
	},
	
	_HandleShowConsumables: function()
	{
		this.ShowWindow( 'consumablespanel' );
	},
	
	ShowSkillgems: function()
	{
		var temp = g_window_cache['skillgempanel'];
		if( temp )
			this._HandleShowSkillgems();
		else
			SendRequest( 'show_skillgempanel' );
	},
	
	_HandleShowSkillgems: function()
	{
		this.ShowWindow( 'skillgempanel' );
	},
	
	ShowCharacterExtDetails: function()
	{
		var temp = g_window_cache['characterextdetails'];
		if( temp )
			this._HandleShowCharacterExtDetails();
		else
			SendRequest( 'show_characterext' );
	},
	
	_HandleShowCharacterExtDetails: function()
	{
		this.ShowWindow( 'characterextdetails' );
	},
	
	ShowInventory: function()
	{
		var temp = g_window_cache['inventory_equipped'];
		var temp2 = g_window_cache['inventory_pack'];
		if( temp && temp2 )
			this._HandleShowInventory();
		else
			SendRequest( 'show_inventory' );
	},
	
	_HandleShowInventory: function()
	{
		this.ShowWindow( 'inventory_equipped' );
		this.ShowWindow( 'inventory_pack' );
	},
	
	ShowClothes: function()
	{
		var temp = g_window_cache['clothespanel'];
		if( temp )
			this._HandleShowClothes();
		else
			SendRequest( 'show_clothes' );
	},
	
	_HandleShowClothes: function()
	{
		this.ShowWindow( 'clothespanel' );
	},
	
	ShowPrefs: function()
	{
		var temp = g_window_cache['characterprefs'];
		if( temp )
			this._HandleShowPrefs();
		else
			SendRequest( 'show_prefs' );
	},
	
	_HandleShowPrefs: function()
	{
		this.ShowWindow( 'characterprefs' );
	},
	
	ShowAddressBook: function()
	{
		var temp = g_window_cache['addressbook'];
		if( temp )
			this._HandleShowAddressBook();
		else
			SendRequest( 'address_book' );
	},
	
	_HandleShowAddressBook: function()
	{
		this.ShowWindow( 'addressbook' );
	},
	
	ShowCurrentEffects: function()
	{
		var temp = g_window_cache['currenteffects'];
		if( temp )
			this._HandleShowCurrentEffects();
		else
			SendRequest( 'current_effects' );
	},
	
	_HandleShowCurrentEffects: function()
	{
		this.ShowWindow( 'currenteffects' );
	},
	
	ShowOpenQuests: function()
	{
		var temp = g_window_cache['openquests'];
		if( temp )
			this._HandleShowOpenQuests();
		else
			SendRequest( 'open_quests' );
	},
	
	_HandleShowOpenQuests: function()
	{
		this.ShowWindow( 'openquests' );
	},
	
	Move: function( x, y )
	{
		if( this._in_combat == true )
			return;
		
		// Check with the builder tiles window to see if the mode is something other than move
		var tools_window = GetWindow( 'BuilderTiles' );
		if( tools_window == false || tools_window.GetMode() == "move" )
		{
			if( x == 0 && y == 0 )
			{
				// This is really an enter/exit command, from clicking ourselves
				this.EnterArea();
				return;
			}
			
			SendRequest( 'move', { 'x': x, 'y': y } );
		}
		else
		{
			alert( tools_window.GetMode() );
		}
	},
	
	ShowEffectDetail: function( the_event, effectsid )
	{
		SendRequest( 'show_effect', { 'effectsid': effectsid } );
	},
	
	_HandleShowEffectDetail: function()
	{
		this.ShowWindow( 'effectdetail' );
	},

	ShowItemDetail: function( the_event, itemid )
	{
		SendRequest( 'show_itemdetail', { 'itemid': itemid } );
	},
	
	_HandleShowItemDetail: function()
	{
		this.ShowWindow( 'itemdetail' );
	},
	
	AcceptQuest: function( the_event, questid )
	{
		SendRequest( 'accept_quest', { 'questid': questid } );
	},
	
	Attack: function( the_event, target, token )
	{
		var combatpanel = GetWindow( "CombatPanel" );
		combatpanel.UpdateAttacked( target );
		SendRequest( 'attack', { 'combat': 'attack', 'target': target, 'token': token } );
	},
	
	BuyHealing: function()
	{
		SendRequest( 'buy_healing' );
	},
		
	ShowCharacterDetail: function( the_event, userid )
	{
		SendRequest( 'character_detail', { 'userid': userid } );
	},
	
	_HandleShowCharacterDetail: function()
	{
		this.ShowWindow( 'characterdetail' );
	},
	
	CombatWait: function( the_event, token )
	{
		SendRequest( 'combat_wait', { 'combat': 'wait', 'token': token } );
	},
	
	EnterArea: function()
	{
		if( this._in_combat == true )
			return;

		SendRequest( 'enter_area' );
	},
	
	_HandleEnterArea: function()
	{
		this.CloseWindow( 'healerpanel' );
		this.CloseWindow( 'questspanel' );
		this.CloseWindow( 'shoppanel' );
		this.CloseWindow( 'mallpanel' );
		this.CloseWindow( 'postoffice' );
		this.CloseWindow( 'goldshoppanel' );
		this.CloseWindow( 'pvppanel' );
		this.CloseWindow( 'manageshop' );
		this.CloseWindow( 'playershop' );
		this.CloseWindow( 'confirmbuyshop' );
		this.CloseWindow( 'customizeshop' );
		this.CloseWindow( 'customizehouse' );
	},
	
	Equip: function( the_event, itemid, verified )
	{
		if( verified != undefined )
			SendRequest( 'equip', { 'item': itemid, 'verified': verified } );
		else
			SendRequest( 'equip', { 'item': itemid } );
	},
	
	_HandleEquip: function()
	{
		this.ShowWindow( 'inventory_equipped' );
		this.ShowWindow( 'inventory_pack' );
		this.CloseWindow( 'unequip_confirm' );
	},
	
	Unequip: function( the_event, itemid, verified )
	{
		if( verified != undefined )
			SendRequest( 'unequip', { 'item': itemid, 'verified': verified } );
		else
			SendRequest( 'unequip', { 'item': itemid } );
	},
	
	_HandleUnequip: function()
	{
		this.ShowWindow( 'inventory_equipped' );
		this.ShowWindow( 'inventory_pack' );
		this.CloseWindow( 'unequip_confirm' );
	},
	
	Healer: function()
	{
		SendRequest( 'healer' );
	},
	
	_HandleHealer: function()
	{
		this.ShowWindow( 'healerpanel' );
	},
	
	ShowMonsterDetail: function( the_event, monsterid )
	{
		SendRequest( 'monster_detail', { 'monsterid': monsterid } );
	},
	
	_HandleShowMonsterDetail: function()
	{
		this.ShowWindow( 'monsterdetail' );
	},
	
	ShowQuestDetail: function( the_event, questid )
	{
		SendRequest( 'quest_detail', { 'questid': questid } );
	},
	
	_HandleShowQuestDetail: function()
	{
		this.ShowWindow( 'questdetail' );
	},
	
	Quests: function()
	{
		SendRequest( 'quests' );
	},
	
	_HandleQuests: function()
	{
		this.ShowWindow( 'questspanel' );
	},
	
	Shop: function()
	{
		SendRequest( 'shop' );
	},
	
	_HandleShop: function()
	{
		this.ShowWindow( 'shoppanel' );
	},
	
	ShowChangePassword: function()
	{
		SendRequest( 'show_change_password' );
	},
	
	_HandleShowChangePassword: function()
	{
		this.ShowWindow( 'changepassword' );
	},
	
	ShowMakeBanner: function()
	{
		SendRequest( 'show_make_banner' );
	},
	
	_HandleShowMakeBanner: function()
	{
		this.ShowWindow( 'makebanner' );
	},
	
	ShowCustomization: function()
	{
		SendRequest( 'show_customization' );
	},
	
	_HandleShowCustomization: function()
	{
		this.ShowWindow( 'customization' );
	},

	ShowFacebookInvite: function()
	{
		SendRequest( 'show_fb_invite' );
	},
	
	_HandleShowFacebookInvite: function()
	{
		this.ShowWindow( 'facebookinvite' );
	},
	
	UseItem: function( the_event, itemid, target, token )
	{
		var temp = {};

		if( itemid != '' )
			temp['item'] = itemid;
		if( target != '' )
			temp['target'] = target;
		if( token != '' )
			temp['token'] = token;

		SendRequest( 'use_item', temp );
	},
	
	_HandleUseItem: function()
	{
		this.CloseWindow( 'targetselectionpanel' );
	},
	
	UseSkillgem: function( the_event, effectsid, target, targetplayer, token )
	{
		var temp = {};
		
		if( effectsid != '' )
			temp['sge'] = effectsid;
		if( target != '' )
			temp['target'] = target;
		if( targetplayer != '' )
			temp['targetplayer'] = targetplayer;
		if( token != '' )
			temp['token'] = token;

		if( this._in_combat == true )
			GetWindow( "CombatPanel" ).UpdateLastSkill( effectsid );
			
		SendRequest( 'use_skillgem', temp );
	},
	
	_HandleUseSkillgem: function()
	{
		this.CloseWindow( 'targetselectionpanel' );
		this.CloseWindow( 'targetplayer' );
	},
	
	ShowMall: function()
	{
		SendRequest( 'view_mall' );
	},
	
	_HandleShowMall: function()
	{
		this.ShowWindow( "mallpanel" );
	},
	
	ShowSideQuestDetail: function()
	{
		SendRequest( 'view_side_quest' );
	},
	
	_HandleShowSideQuestDetail: function()
	{
		this.ShowWindow( "sidequestdetail" );
	},
	
	BuyGoldItem: function( the_event, itemid, confirmed )
	{
		SendRequest( 'buy_gold_item', { 'item': itemid, 'confirmed': confirmed } );
	},
	
	BuyGoldStyle: function( the_event, itemid, confirmed )
	{
		SendRequest( 'buy_gold_style', { 'item': itemid, 'confirmed': confirmed } );
	},
	
	Run: function( the_event, token )
	{
		SendRequest( 'combat_run', { 'combat':'run', 'token': token } );
	},
	
	BuyPlayerShop: function( the_event, price, confirm )
	{
		var temp = {};
		if( price != '' )
			temp['price'] = price;
		if( confirm != '' )
			temp['confirm'] = confirm;
		
		SendRequest( 'buy_player_shop', temp );
	},
	
	_HandleBuyPlayerShop: function()
	{
		this.ShowWindow( 'confirmbuyshop' );
	},
	
	SetCSS: function( typeid, value )
	{
		// NOTE: "this" refers to the generator of the event, not game_state!
		SendRequest( 'set_css', { 'type': this.getAttribute( 'typeid' ), 'value': this.value } );
	},
	
	SetChatPref: function( the_event )
	{
		// NOTE: "this" refers to the generator of the event, not game_state!
		SendRequest( 'set_chatpref', { 'value': this.value } );
	},
	
	CompleteQuest: function( the_event, questid )
	{
		SendRequest( 'complete_quest', { 'questid': questid } );
	},
	
	_HandleCompleteQuest: function()
	{
		this.ShowWindow( 'questdetail' );
	},
	
	CompleteSideQuest: function()
	{
		SendRequest( 'complete_side_quest' );
	},
	
	_HandleCompleteSideQuest: function()
	{
		this.ShowWindow( 'sidequestdetail' );
	},
	
	BuyPlayerShopItem: function( the_event, itemid, price )
	{
		SendRequest( 'buy_player_shop_item', { 'itemid': itemid, 'price': price } );
	},
	
	SideQuestAccept: function()
	{
		SendRequest( 'side_quest_accept' );
	},
	
	_HandleSideQuestAccept: function()
	{
		this.ShowWindow( 'sidequestdetail' );
	},
	
	EnterMallShop: function( the_event, player_shopid )
	{
		if( this._in_combat == true )
			return;
			
		SendRequest( 'enter_mall_shop', { 'shopid' : player_shopid } );
	},
	
	_HandleEnterMallShop: function()
	{
		// This is the same as enter/exit from an area, so treat accordingly
		this._HandleEnterArea();
	},
	
	PostOffice: function()
	{
		SendRequest( 'post_office' );
	},
	
	_HandlePostOffice: function()
	{
		this.ShowWindow( 'postoffice' );
	},
	
	GoldShop: function()
	{
		SendRequest( 'gold_shop' );
	},
	
	_HandleGoldShop: function()
	{
		this.ShowWindow( 'goldshoppanel' );
	},
	
	PvPPanel: function()
	{
		SendRequest( 'pvp_panel' );
	},
	
	_HandlePvPPanel: function()
	{
		this.ShowWindow( 'pvppanel' );
	},
	
	ManageShop: function()
	{
		SendRequest( 'manage_shop' );
	},
	
	_HandleManageShop: function()
	{
		this.ShowWindow( 'manageshop' );
	},
	
	PlayerShop: function()
	{
		SendRequest( 'player_shop' );
	},
	
	_HandlePlayerShop: function()
	{
		this.ShowWindow( 'playershop' );
	},
	
	AcceptChallenge: function( the_event, userid )
	{
		SendRequest( 'accept_challenge', { 'userid': userid } );
	},
	
	_HandleAcceptChallenge: function()
	{
		this.CloseWindow( 'pvppanel' );
	},
	
	CancelChallenge: function( the_event, userid )
	{
		SendRequest( 'cancel_challenge', { 'userid': userid } );
	},
	
	PvPChallenge: function( the_event, userid )
	{
		SendRequest( 'pvp_challenge', { 'userid': userid } );
	},
	
	PreviewStyle: function( the_event, styleid )
	{
		SendRequest( 'preview_style', { 'styleid': styleid } );
	},
	
	AddressBookNavigate: function( the_event, start, filter, search_string )
	{
		SendRequest( 'address_book_navigate', { 'start': start, 'filter': filter, 'search': search_string } );
	},
	
	AddressBookFilter: function()
	{
		// NOTE: "this" refers to the generator of the event, not game_state!
		SendRequest( 'address_book_filter', collectFormData( 'address_filter' ) );
	},
	
	MakeMail: function( the_event, touser )
	{
		var temp = {};
		if( touser != '' )
			temp['touser'] = touser;
		SendRequest( 'make_mail', temp );
	},
	
	DeleteMail: function( the_event, post_officeid )
	{
		SendRequest( 'delete_mail', { 'messageid': post_officeid } );
	},

	ReplyMail: function( the_event, post_officeid )
	{
		SendRequest( 'reply_mail', { 'messageid': post_officeid } );
	},
	
	EquipClothing: function( the_event, itemid )
	{
		SendRequest( 'equip_clothing', { 'item': itemid } );
	},
	
	UnequipClothing: function( the_event, itemid )
	{
		SendRequest( 'unequip_clothing', { 'item': itemid } );
	},
	
	ReadMail: function( the_event, post_officeid )
	{
		SendRequest( 'read_mail', { 'messageid': post_officeid } );
	},
	
	UnreadMail: function( the_event, post_officeid )
	{
		SendRequest( 'unread_mail', { 'messageid': post_officeid } );
	},
	
	PORefresh: function()
	{
		SendRequest( 'po_refresh' );
	},
	
	CollectPackages: function( the_event, messageid )
	{
		SendRequest( 'collect_packages', { 'messageid': messageid } );
	},
	
	PayShopRent: function()
	{
		SendRequest( 'pay_shop_rent' );
	},
	
	PlayerShopAddItem: function()
	{
		formdata = collectFormData( 'add_item_form' );
		SendRequest( 'player_shop_add_item', formdata );
	},
	
	PlayerShopUpdateItem: function( the_event, itemid )
	{
		formdata = collectFormData( 'item_' + itemid );
		SendRequest( 'player_shop_update_item', formdata );
	},
	
	ChangeShopName: function()
	{
		formdata = collectFormData( 'change_name' );
		SendRequest( 'change_shop_name', formdata );
	},
	
	ChangeShopMessage: function()
	{
		formdata = collectFormData( 'change_message' );
		SendRequest( 'change_shop_message', formdata );
	},
	
	ShowShopCustomization: function()
	{
		SendRequest( 'show_customize_shop' );
	},
	
	_HandleShowShopCustomization: function()
	{
		this.ShowWindow( "customizeshop" );
	},
	
	SellPlayerShop: function()
	{
		formdata = collectFormData( 'sell_shop' );
		SendRequest( 'sell_player_shop', formdata );
	},
	
	UnsellPlayerShop: function()
	{
		SendRequest( 'unsell_player_shop' );
	},
	
	AddressBookSearch: function( formdata )
	{
		SendRequest( "address_book_search", formdata );
	},
	
	_HandleAddressBookSearch: function()
	{
		var element = document.getElementById( "searchbox" );
		if( element )
			setCaretTo( element, element.value.length );
	},
	
	ChatEntry: function()
	{
		var textbox = document.getElementById( 'chatentry' );
		
		SendRequest( 'chat_entry', { 'message':textbox.value } );
		textbox.value = "";
	},
	
	ShowSelectTarget: function( the_event, name, effectid, targets, skillgem )
	{
		SendRequest( "show_select_target", { 'name': name, 'effectid': effectid, 'targets': targets, 'skillgem': skillgem } );
	},
	
	_HandleShowSelectTarget: function()
	{
		this.ShowWindow( "targetselectionpanel" );
	},
	
	ShowTargetPlayer: function( the_event, effectid )
	{
		SendRequest( "show_target_player", { 'sge': effectid } );
	},
	
	_HandleShowTargetPlayer: function()
	{
		this.ShowWindow( 'targetplayer' );
	},

	PostOfficeSendMail: function()
	{
		form_element = $( 'makemail' );
		var message = new Array();
		
		// Add the actual mail portions
		message['to_user'] = form_element.touser.value;
		message['subject'] = form_element.subject.value;
		message['message'] = form_element.message.value;
		message['pyrite'] = form_element.pyrite.value;

		// Get the attached items
		var attached_items_wrapper = document.getElementById( "attachlist" );
		var input_elems = attached_items_wrapper.getElementsByTagName( "input" );
		
		var len = input_elems.length;
		for( var i = 0; i < len; i++ )
		{
			var elem = input_elems[i];
			
			if( elem.value == "" )
				continue;
			
			message['item_' + elem.getAttribute( 'itemid' )] = elem.value;
		}
		
		// Send off the completed letter
		SendRequest( 'send_mail', message );
	},

	Heartbeat: function()
	{
		// If things are already badly lagged, then don't compound things by continuing to request chat updates.
		if( AjaxRequest.numActiveAjaxGroupRequests['PseudoQuest'] >= 3 )
		{
			setTimeout( "game_state.Heartbeat();", 4000 );	// wait a bit longer to allow breathing room
			return;
		}
		
		SendRequest( "heartbeat" );
	},
	
	_HandleHeartbeat: function()
	{
		setTimeout( "game_state.Heartbeat();", 2000 );
	},
	
	// Registered event handlers
	// NOTE: Due to weird scoping rules, 'this' will refer to the object that fired the event, not game_state.
	EventClickMove: function()
	{
		var x = this.getAttribute( 'x' );
		var y = this.getAttribute( 'y' );
		GetWindow( "MainMap" ).TileClicked( x, y );
	},
	
	EventShowTooltip: function()
	{
		ShowTooltip( this );
	},
	
	EventClearTooltip: function()
	{
		ClearTooltip( this );
	},
	
	EventEnableKeywalk: function()
	{
		enableKeyboardMove = 1;
	},
	
	EventDisableKeywalk: function()
	{
		enableKeyboardMove = 0;
	},
	
	EventCloseWindow: function( the_event, window_id )
	{
		game_state.CloseWindow( window_id );
	},
	
	SetQuickBeltItem: function( slot, itemid )
	{
		SendRequest( "set_quickbelt_item", { 'slot': slot, 'itemid': itemid } );
	},
	
	SetDecoration: function( x, y, tileid )
	{
		SendRequest( "set_decoration", { 'x': x, 'y': y, 'tileid': tileid } );
	},

	RemoveDecoration: function( x, y, imagename )
	{
		SendRequest( "remove_decoration", { 'x': x, 'y': y, 'imagename': imagename } );
	},
	
	MoveDecoration: function( old_x, old_y, new_x, new_y, imagename )
	{
		SendRequest( "move_decoration", { 'old_x': old_x, 'old_y': old_y, 'new_x': new_x, 'new_y': new_y, 'imagename': imagename } );
	},
	
	ShowDecorationsShop: function()
	{
		SendRequest( 'show_decorations_shop' );
	},
	
	_HandleShowDecorationsShop: function()
	{
		this.ShowWindow( 'decorationsshop' );
	},
	
	BuyDecorationConfirm: function( the_event, name, cost, tileid )
	{
		// Construct a decoration purchase confirmation window
		// TODO: we can verify Gold cost / player availability here to save a failed trip to the server
		// Create the window
		if( !g_window_cache['buy_decoration_confirm'] ) {
			g_window_cache['buy_decoration_confirm'] = new Window({'id':'pqwindow_js_buy_decoration_confirm', className:'pq_theme', title: "Are you sure?", 'left': 314, 'top': 166, 'width':415, 'height':228});
			g_window_cache['buy_decoration_confirm'].setCookie( 'pqwindow_js_buy_decoration_confirm', new Date( 'Thu, 31 Dec 2099 23:59:59 UTC' ), '', '', false );
		}
		var frame = g_window_cache['buy_decoration_confirm'];

		// Actually construct the HTML
		var wrapper = new Element( 'div', { 'id': 'buy_decoration_confirm_wrapper' } );

		var p = new Element( 'p' );
		p.update( "You are about to purchase <b>" + name + "</b> for <b>" + cost + " gold</b>." );
		wrapper.insert( p );
		
		p = new Element( 'p' );
		p.update( "This item cannot be sold, traded, or refunded.  Just as a reminder, this item costs <u>GOLD</u>, not pyrite." );
		wrapper.insert( p );
	
		p = new Element( 'p' );
		p.update( "Are you sure you're completely happy with this choice?" );
		wrapper.insert( p );
	
		var div = new Element( 'div' );
		var input = new Element( 'input', { 'type': "button", 'value': "Just give it to me already!" } );
		Event.observe( input, 'click', game_state.BuyDecoration.bindAsEventListener( game_state, tileid ) );
		div.insert( input );
		
		input = new Element( 'input', { 'type': "button", 'value': "Nevermind" } );
		Event.observe( input, 'click', game_state.EventCloseWindow.bindAsEventListener( game_state, 'buy_decoration_confirm' ) );
		div.insert( input );
		wrapper.insert( div );

		// Kill the previous wrapper, if there
		frame.getContent().update( wrapper );
		
		// Bring it front and center
		frame.show();
		frame.toFront();
	},
	
	BuyDecoration: function( the_event, tileid )
	{
		this.CloseWindow( "buy_decoration_confirm" );
		SendRequest( "buy_decoration", { 'tileid': tileid } );
	},
	
	ShowBuilderTools: function()
	{
		SendRequest( "builder_tools" );
	},
	
	_HandleBuilderTools: function()
	{
		this.ShowWindow( 'builder_tiles' );
		this.ShowWindow( 'builder_tile_details' );
		this.ShowWindow( 'builder_scope_details' );
		this.ShowWindow( 'builder_map_generate' );
		this.ShowWindow( 'builder_scope_map' );
		
		// Enable the hotkeys to switch modes
		var body = document.getElementsByTagName( 'body' );
		body = body[0];
		Event.observe( body, 'keyup', game_state.HotkeyPressed.bindAsEventListener( game_state ) );
	},
	
	BuilderChangeTileCategory: function( the_event, the_select)
	{
		SendRequest( "builder_tiles_refresh", { 'categoryid': the_select.value } );
	},
	
	_HandleBuilderTilesRefresh: function()
	{
		this.ShowWindow( 'builder_tiles' );
	},
		
	BuilderSetTooltip: function( the_event )
	{
		var the_window = GetWindow( "BuilderTileDetails" );
		var tooltip = the_window.GetActiveTooltip();
		var mapid = the_window.GetActiveMapid();
		SendRequest( "builder_set_tooltip", { 'tooltip': tooltip, 'mapid': mapid } );
	},
	
	BuilderToggleImpassable: function( the_event )
	{
		var the_window = GetWindow( "BuilderTileDetails" );
		var mapid = the_window.GetActiveMapid();
		SendRequest( "builder_toggle_impassable", { 'mapid': mapid } );
	},
	
	BuilderDeleteTile: function( the_event )
	{
		var the_window = GetWindow( "BuilderTileDetails" );
		var mapid = the_window.GetActiveMapid();
		
		SendRequest( "builder_delete_tile", { 'mapid': mapid } );
	},
	
	BuilderDeleteOverlays: function( the_event )
	{
		var the_window = GetWindow( "BuilderTileDetails" );
		var mapid = the_window.GetActiveMapid();
		var tileid = the_window.GetActiveTileid();
		var zindex = the_window.GetActiveZindex();
		
		SendRequest( "builder_delete_overlay", { 'mapid': mapid, 'tileid': tileid, 'zindex': zindex } );
	},
	
	HotkeyPressed: function( the_event )
	{
		if( enableKeyboardMove == 0 )
			return;
		
		switch( the_event.keyCode )
		{
			case 77:
				GetWindow( 'BuilderTiles' ).SetMode( 'move' );
				break;
			case 69:
				GetWindow( 'BuilderTiles' ).SetMode( 'edit' );
				break;
			case 65:
				GetWindow( 'BuilderTiles' ).SetMode( 'add' );
				break;
		}
		
		the_event.preventDefault();
	},
	
	BuilderEditTile: function( x, y )
	{
		// NOTE: x, y are offsets from the character's current position
		SendRequest( "builder_edit_tile", { 'offset_x': x, 'offset_y': y } );
	},
	
	BuilderAddTile: function( x, y )
	{
		var builder_window = GetWindow( "BuilderTiles" );
		if( builder_window == false )
			return;
		
		var tileid = builder_window.GetActiveTileid();
		if( tileid == 0 )
			return;
		
		// NOTE: x, y are offsets from the character's current position
		SendRequest( 'builder_set_tile', { 'x': x, 'y': y, 'tileid': tileid } );
	},
	
	BuilderUpdateTileOrder: function( mapid, new_order )
	{
		SendRequest( 'builder_update_tile_order', { 'mapid': mapid, 'new_order': new_order } );
	},
	
	BuilderNewScope: function( the_event )
	{
		SendRequest( 'builder_new_scope', 
						{	'name': $( 'builder_scope_name' ).value,
							'description': $( 'builder_scope_description' ).value,
							'defaulttileid': $( 'builder_scope_defaulttileid' ).value,
							'ishealer': $( 'builder_scope_ishealer' ).checked,
							'isshop': $( 'builder_scope_isshop' ).checked,
							'isquests': $( 'builder_scope_isquests' ).checked,
							'ispostoffice': $( 'builder_scope_ispostoffice' ).checked
						}
					);
	},
	
	BuilderAutoEdge: function( the_event )
	{
		SendRequest( 'builder_edgeit', { 'groupid': $('builder_edginggroupid').value } );
	},
	
	EnterPlayerHouse: function( the_event, userid )
	{
		SendRequest( 'enter_player_house', { 'owner': userid } );
		this.CloseWindow( 'characterdetail' );
		this.CloseWindow( 'customizehouse' );
	},
	
	BuilderUpdateScopeDetails: function()
	{
		// Gather the form data, and send it along
		var formdata = collectFormData( 'builder_scope_details_form' );
		SendRequest( 'builder_update_scope_details', formdata );
	},
	
	CustomizeHouse: function()
	{
		SendRequest( 'customize_player_house' );
	},
	
	_HandleCustomizeHouse: function()
	{
		this.ShowWindow( 'customizehouse' );
	},
	
	SetFacebookMode: function( mode )
	{
		this._is_facebook = mode;
	},
	
	IsFacebook: function()
	{
		return this._is_facebook;
	},
	
	GetAvailSkillById: function( skillid )
	{
		// TODO: look at available skills, and return an array with appropriate data about this skill
		return false;
	},
	
	UpdatePlayerData: function( player_stats )
	{
		this._player_stats = player_stats;
	},

	GetPlayerStats: function()
	{
		return this._player_stats;
	},
	
	UpdateAvailSkills: function( skills )
	{
		this._player_avail_skills = skills;
	},
	
	GetAvailSkillById: function( skillid )
	{
		return this._player_avail_skills[skillid];
	},
	
	ShowAdminTools: function()
	{
		SendRequest( 'admintools' );
	},
	
	_HandleAdminTools: function()
	{
		this.ShowWindow( 'admintools' );
	},
	
	FindAvatarTileForUser: function( userid )
	{
		for( var i = 0, len = this._avatar_cache.length; i < len; i++ )
		{
			var av_tile = this._avatar_cache[i];
			
			if( av_tile.getAttribute( 'userid' ) == userid )
				return av_tile;
		}
		
		return null;
	},
	
	_HandleRefreshCharacter: function()
	{
		var char_window = GetWindow( 'CharGraph' );
		char_window.Handler( null );
	}
});
