window.appState = {
	month: $.cookie('month'),
	selectedTrip: null,
    selectedTrade: null,
	tempTrade: null,
    editTrade: null,
    shopMode: 'tripShopping',   // tripShopping, staticTradeShopping, dynamicTradeShopping
	detailsWindows: {},
    timestampUpdater: null
};

window.cluetipOptions = {
	local: true
};

var trinfoUpdater = new TripDetailsUpdater();

function clearSelectedItems() {
   window.appState.selectedTrip = null;
   window.appState.selectedTrade = null;
}

// convert trip object (like the one in window.appState.selectedTrip) to a string
// that can be used in page parameters.
function tripToParamStr(t) {
	if (typeof(t.getParams) == 'function') t = t.getParams();
	return ''+t.id+'_'+t.date+'_'+t.m+'_'+t.d+'_'+t.f+'_'+t.s;
}
function isSameTrip(t1, t2) {
	if (t1 === t2) return true;
	if (!t1 || !t2) return false;
	return tripToParamStr(t1) == tripToParamStr(t2);
}

function showTripDetailsWindow(t) {
    if(!t)
        return;
    var url = "tripdetails.php?"+$.param(t.getParams());
	var key = tripToParamStr(t)
    var width = 490, height = 450, scroll = 'yes';
    showWindow(url, key, width, height, scroll);
}

function contactOwnerWindow(t) {
    if(!t)
        return;
    var url = "message-contactowner.php?"+$.param(t.getParams());
    var key = tripToParamStr(t)+'_co'
    var width = 590, height = 450, scroll = 'no';
    showWindow(url, key, width, height, scroll);
}

function showRawCalendarWindow() {
    var url = "calendar-raw.php";
    var month = getCookie('month');
	var key = 'raw-calendar_' + month;
    var width = 550, height = 450, scroll = 'yes';
    showWindow(url, key, width, height, scroll);
}

function showGcalExportWindow() {
    var month = getCookie('month');
    var url = "gcal-export.php?selmonth=" + month;
    var key = 'gcal-export' + month;
    var width = 500, height = 420, scroll = 'yes';
    showWindow(url, key, width, height, scroll);
}

function showWindow(url, key, width, height, scroll) {
	if (appState.detailsWindows[key]) {
		var wnd = appState.detailsWindows[key];
		if (typeof wnd.closed == 'boolean')
		{
			// mozilla
			if (wnd.closed)
				openDetailsWindow(url, key, width, height, scroll);
			else
				appState.detailsWindows[key].focus();
		}
		else
		{
			//console.log('focusing');
			try {
				appState.detailsWindows[key].focus();
			}
			catch (e) {
				openDetailsWindow(url, key, width, height, scroll);
			}
		}
	} else
		openDetailsWindow(url, key, width, height, scroll);
}

function openDetailsWindow(url, key, width, height, scroll){
	var wnd = window.open(url, key.replace(/-/g,''), "status=1,toolbar=0,menubar=0,width=" + width + ",height=" + height + ",scrollbars=" + scroll + ",resizable=yes");
	appState.detailsWindows[key] = wnd;
	if(wnd)	// can be null in IE7
		wnd.appState = appState;
}

// this gets called when inline trip info is injected into the DOM
function trip_inline($tripinfo) {
	$tripinfo.find('li > .title').wrap('<div class="event-catcher" style="position: relative; z-index: 100; margin-left: -22px; padding-left: 22px; cursor: pointer;"/>');
	$tripinfo.find('li > div.event-catcher').click(function(event) {
		var $li = $(this).parent();
		$li.toggleClass('expanded')
			.find('.title > img.expander')
				.attr('src', ($li.hasClass('expanded') ? 'images/minus.gif' : 'images/plus.gif'))
				.end()
			.find('> .content').toggle();  //.slideToggle('fast');
	});

	$tripinfo.find('a.popout').click(function() {
		var trid = $(this).parents('tr.tripinfo').attr('id');
		var trip = Trip.fromString(/^trinfo_(.*)$/i.exec(trid)[1]);
		showTripDetailsWindow(trip);
		return false;
	});

    $tripinfo.find('a.popout2').click(function() {
        var trid = $(this).parents('tr.tripinfo').attr('id');
        var trip = Trip.fromString(/^trinfo_(.*)$/i.exec(trid)[1]);
        contactOwnerWindow(trip);
        return false;
    });

	// update trips details handler(when there are no trip details)
	$tripinfo.find('.update-request a.trip-details-update').click(function() {
        var $trinfo = $(this).parents('tr.tripinfo');
		requestTripDetailUpdate($trinfo);
	});

    function requestTripDetailUpdate($trinfo) {
        if($trinfo.hasClass('requesting-update'))
            return;

        var trid = $trinfo.attr('id');
        var trip = Trip.fromString(/^trinfo_(.*)$/i.exec(trid)[1]);
        var url = "clienttaskrequest.php?request=tripdetails&"+$.param(trip.getParams());

        $trinfo.addClass('requesting-update');
        var $lupd = $trinfo.find('a.trip-details-update');
        $lupd.addClass('action-progress');
        $trinfo.find('.update-request-failed').addClass('hidden');

        $.ajax({
            type: 'POST',
            url: url,
            dataType: "json",
            timeout: 35000,
            success: function(result, textStatus) {
                $trinfo.removeClass('requesting-update');
                $lupd.removeClass('action-progress');
                switch(result.error) {
                    case 'success':
                        requestSent($trinfo, result.task);
                        break;
                    case 'rejected':
                        requestFailed($trinfo, 'You have already requested the maximimum allowed trip details updates per hour!');
                        break;
                    default:
                        requestFailed($trinfo, "Failed to request trip's details update!");
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                $trinfo.removeClass('requesting-update');
                $lupd.removeClass('action-progress');
                requestFailed("Failed to request trip's details update!");
            }
        });
    }

    function requestSent($trinfo, task) {
        $trinfo.find('.update-request').addClass('hidden');
        $trinfo.find('.update-request-failed').addClass('hidden');
        $trinfo.find('.update-request-succ').removeClass('hidden');

        $trinfo.find('#detailsTaskGuid').val(task);
        trinfoUpdater.addTrip($trinfo);
    }

    function requestFailed($trinfo, msg) {
        $trinfo.find('.update-request').removeClass('hidden');
        $trinfo.find('.update-request-failed').html(msg).removeClass('hidden');
        $trinfo.find('.update-request-succ').addClass('hidden');
    }
}

function calPaneTripsMap() {
    attachTripsMapHandler('#calendar-pane map area.popout');
}

function myCalendarTripsMap() {
    attachTripsMapHandler('#my-calendar map area.popout');
}

function attachTripsMapHandler(selector){
    $(selector).live('click', function() {
        var href = $(this).attr('href');
        var trip = Trip.fromUrl(href);
        showTripDetailsWindow(trip);
        return false;
    });
}

function triplist($table, clientSorting) {
	if ($table.length > 1) throw "triplist() should be called with only a single element";

	if(clientSorting)
		$table.tablesorter();

	var numcols = $table.find('th').length;

	$table.find('tr.trip')
		.click(function(event) {
			if (event.button > 1) return true;

			var $tr = $(this);
			var trid = $tr.attr('id');
			var trinfoid = trid.replace(/^t_/,'trinfo_');
			var $trinfo = $('#'+trinfoid);
			if (!$tr.hasClass('selected') || $tr.hasClass('error')) {
				$table.find('.selected').removeClass('selected');
				$tr.addClass('selected');

				var trip = Trip.fromString(/^t_(.*)$/i.exec(trid)[1]);
                trip.mine = $tr.hasClass('mine');
				appState.selectedTrip = trip;

				if (settings.expandTripOn != 'click2')
					toggleTrip($tr);

				$table.triggerHandler('tripSelected', appState.selectedTrip);
			} else {
				toggleTrip($tr);
			}
		});

	function toggleTrip($tr) {
		var trid = $tr.attr('id');
		var trinfoid = trid.replace(/^t_/,'trinfo_');
		var $trinfo = $('#'+trinfoid);

		// first check to see if this trip already has a hidden info row
		if ($trinfo.length == 0) {
			$tr.after(	'<tr id="'+trinfoid+'" class="tripinfo hidden">'+
							'<td colspan="'+numcols+'">'+
								'<div class="container">'+
									//'<div class="loading">&nbsp;<\/div>'+
								'<\/div>'+
							'<\/td>'+
						'<\/tr>');
			$trinfo = $('#'+trinfoid);
			var $cont = $trinfo.find('.container');

            $trinfo.bind('task-completed', function() {
                setTimeout(function() {
                        loadTripDetails($tr, $trinfo, $cont)
                    }, 15000); // the actual data comes with a small delay after confirming task completion
            });

			loadTripDetails($tr, $trinfo, $cont);
		} else {
			if ($trinfo.hasClass('hidden')) {

				if($trinfo.find('.notfound .update-request').length > 0) {
					// in case we have requested trip details previously,
					// but the trip doesn't have any trip details yet,
					// request them again
					var $cont = $trinfo.find('.container');
					loadTripDetails($tr, $trinfo, $cont)
				}
				else
					showtrinfo($trinfo);
			}
			else
				hidetrinfo($trinfo);
		}
	}

	function loadTripDetails($tr, $trinfo, $cont) {
		$tr.removeClass('error').addClass('loading');

		$.ajax({
			type: 'GET',
			url: 'trip-inline.php',
			data: appState.selectedTrip.getParams(),
			timeout: 15000,
			success: function(data, textStatus) {
				$tr.removeClass('loading');

				var $tripinfo = $(data);
				$cont.html($tripinfo);
				trip_inline($tripinfo, trinfoUpdater);
				$tripinfo.find('li > div.event-catcher').click();
				$tripinfo.find('.quicktip').cluetip(cluetipOptions);

				showtrinfo($trinfo);

                var updateTask = $tripinfo.find('#detailsTaskGuid').val();
                if(updateTask)
                    trinfoUpdater.addTrip($trinfo);
			},
			error: function(xhr, textStatus, errorThrown) {
				$tr.removeClass('loading').addClass('error');
				$trinfo.remove();
				//alert('error loading trip info');
			}
		});
	}

	var trinfoContainer = document.createDocumentFragment();

	$table
		.bind('sortStart', function() {
			// remove trip info rows before sorting
			$table.find('tr.tripinfo').addClass('hidden').each(function() {
				trinfoContainer.appendChild(this);
			});
		})
		.bind('sortEnd', function() {
			// fix the location of all the tripinfo rows after sorting
			$.each($.makeArray(trinfoContainer.childNodes), function() {
				var $trinfo = $(this);
				var trid = $trinfo.attr('id').replace(/^trinfo_/,'t_');
				$('#'+trid).after($trinfo);
			});
		});

	function showtrinfo($trinfo) {
		$trinfo.removeClass('hidden');
	}

	function hidetrinfo($trinfo) {
		$trinfo.addClass('hidden');
	}
}

function closeNote(noteEl, noteCookie) {
    var $this = $(noteEl);
    $this.parents('div.note').addClass('hidden');
    if(noteCookie) {
        setCookie(noteCookie, 1);
    }
}

function calendarPane($cp) {
	$cp = $cp || $('#calendar-pane');

	$cp.find('.quicktip').cluetip(cluetipOptions);

	$cp.find('.toggle-minor')
        .unbind('click')
        .click(function(e) {
		    if (e.button > 1) return true;

		    var $a = $(this);
		    var shown = $a.data('shown');
		    // .toggle() doesn't work in IE8
            $cp.find('.monthly-summary .minor').each(function() {
                var $elem = $(this);
                if($elem.css('display') == 'none')
                     $elem.show();
                else
                     $elem.hide();
            });

		    if (!shown) {
			    $a.text('Show less').data('shown', true);
		    } else {
			    $a.text('Show more').data('shown', false);
		    }
		    return false;
	    })
        .preventFocusRect();;

    $cp.find('table tbody td a.popout')
        .unbind('click')
        .click(function() {
            var trid = $(this).parents('tr.trip').attr('id');
            var trip = Trip.fromString(/^t_(.*)$/i.exec(trid)[1]);
            showTripDetailsWindow(trip);
            return false;
    });

    $cp.find('#extras a.toggle-my-trips')
        .unbind('click')
        .click(function() {
            var $a = $(this);
            var $tripsCont = $a.next();
            if($tripsCont) {
                $tripsCont.toggleClass('hidden');
                if($tripsCont.hasClass('hidden')) {
                    $a.text('Show My Trips');
                    setCookie('expandedCalPaneTrips', false, {expires:null});
                }
                else {
                    $a.text('Hide My Trips')
                    setCookie('expandedCalPaneTrips', true, {expires:null});
                }
            }
        })
        .preventFocusRect();

	return $cp;
}

$(function() {
	// disable focus rects on certain elements (for browsers that have this ability)
	$('#toplinks a, #menu a, #submenu a').preventFocusRect();

	// legend link
	$('#legend-link').click(function(e) {
		var wnd = window.open($(e.target).attr('href'), "Legend", "status=0,toolbar=0,menubar=0,width=530,height=530,resizable=yes");
		return false;
	});


	// closure to manage menu state
	(function() {
		var mouseLeaveTimeout;
		var cursorOnMenuElement = false;
		var anyActive = false;
		var $defsm = $('#submenu > .submenu-items.default');

		function showSubmenu($submenu) {
			var id = $submenu.attr('id');
			$submenu.removeClass('hidden').addClass('active');
			$('.submenu-items:not(#'+id+')').addClass('hidden').removeClass('active');
			anyActive = true;
		}

		function reset() {
			if (anyActive) {
				$defsm.stop().removeClass('hidden').removeClass('active');
				$('#submenu > .submenu-items:not(.default)').stop().addClass('hidden').removeClass('active');
				anyActive = false;
			}
		};

		function menuMouseLeave() {
			cursorOnMenuElement = false;
			if (anyActive) {
				clearTimeout(mouseLeaveTimeout);
				mouseLeaveTimeout = setTimeout(function() {
					if (!cursorOnMenuElement)
                        reset();
				}, 500);
			}
		};

		$('#menu li:not(.right) a')
			.hoverIntent({
				timeout: 0, // timeout handled manually
				over: function() {
					showSubmenu($($(this).attr('rel')));
				},
				out: menuMouseLeave
			})
			.mousemove(function() {
				cursorOnMenuElement = true;
			});

		$('#submenu')
			.bind('mouseleave', menuMouseLeave)
			.mousemove(function() {
				cursorOnMenuElement = true;
			});

		$('#submenu > .submenu-items a')
			.click(function(e) {
				if (e.button > 1) return;
				$('#submenu > .submenu-items.default').removeClass('default');
				$('#submenu > .submenu-items a.active').removeClass('active');
				$defsm = $(this).addClass('active').parent().addClass('default');
				setTimeout(function() { reset(); }, 100);
			});

	})();

	// month selector
	$('#month-selector a').preventFocusRect()
		.click(function() {
			var month = /month=([0-9]+)/.exec($(this).attr('href'))[1];
			$.cookie('month', month);
			location.href = location.href;
			return false;
		});

	// anything that has the float class should follow the viewport.
	// typically only the calendar pane does this
	$('.float').each(function() {
		var $e = $(this).css({position:'absolute'});
		var padViewport = 5;
		var panelWindowOffset = Math.floor($e.offset().top);
		var lastScrollTop = $(document).scrollTop();
		$(window).scroll(function() {
			var newTop = 0,
				scrollTop = $(document).scrollTop(),
				wndHeight = $(window).height(),
				panelHeight = $e.height();

			if (wndHeight > panelHeight) {
				// just float the panel at the top if the viewport is tall enough to show all of it
				newTop = Math.max(0, scrollTop - panelWindowOffset + padViewport);
			} else {
				// if the viewport isn't tall enough to show all of the panel, things get complicated
				newTop = null; // leave the panel where its at by default
				var ptop = $e.position().top + panelWindowOffset,
					pbottom = ptop + panelHeight,
					vpheight = wndHeight - (padViewport * 2),
					vptop = scrollTop + padViewport,
					vpbottom = vptop + vpheight,
					ptopShown = ptop - vptop,
					pbottomShown = vpbottom - pbottom;

				// if ptopShown or pbottomShown is negative then that number of pixels is invisible
				// from the respective part of the element.
				//console.log(ptopShown, pbottomShown);

				if (ptopShown > 0 && scrollTop < lastScrollTop) {
					// float to top
					newTop = Math.max(0, vptop - panelWindowOffset);
					//console.log('ptopShown > 0 ::::: newTop='+newTop);
				} else if (pbottomShown > 0 && scrollTop > lastScrollTop) {
					newTop = Math.max(0, scrollTop - panelWindowOffset - panelHeight + vpheight);
					//console.log('pbottomShown > 0 ::::: newTop='+newTop);
				}
			}

			lastScrollTop = scrollTop;
			//console.log('setting top to '+newTop);
			if (newTop !== null) {
				$e.stop(true, false);
				$e.animate({top:newTop},{duration:500,queue:false});
			}
		});
	});

    $('#raw-calendar').click(function() {
        showRawCalendarWindow();
        return false;
    });

	// "quicktip" tooltips
	$('.quicktip').cluetip(cluetipOptions);

	// calendar pane
	calendarPane();
});


// classes
function TripDetailsUpdater() {
    var checkInterval = 40000;
    var running = false;
    var tinfoRows = [];

    this.addTrip = function($trinfo) {
        var id = $trinfo.id();
        for(i=0; i < tinfoRows.length; i++) {
            var $item = tinfoRows[i];
            if($item.id() == id)
                return;
        }

        // item not found
        $trinfo.data('rem-checks', Math.round(60 * 60 * 1000 / checkInterval));
        tinfoRows.push($trinfo);
        run();
    }

    function run() {
        if(!running && tinfoRows.length > 0) {
            running = true;
            process();
        }
    }

    function process() {
        setTimeout(function() {
            var tinfoRowsClone = tinfoRows.slice(0);
            $.each(tinfoRowsClone, function(index, $trinfo) {
                if(taskExpired($trinfo))
                    taskInactive($trinfo);
                else
                    checkDetailsTaskStatus($trinfo, $trinfo.find('#detailsTaskGuid').val());
            });

            process();
        }, checkInterval);
    }

    function taskExpired($trinfo) {
        var checks = $trinfo.data('rem-checks');
        if(checks) {
            $trinfo.data('rem-checks', --checks);
            return false;
        }
        else
            return true;
    }

    function checkDetailsTaskStatus($trinfo, taskGuid) {
        if(!taskGuid || $trinfo.hasClass('checking-task-status'))
            return;

        $trinfo.addClass('checking-task-status');
        var $lupd = $trinfo.find('div.update-request-succ span');
        $lupd.addClass('action-progress');
        $.ajax({
            type: 'POST',
            url: 'clienttaskrequest.php?status=' + taskGuid,
            dataType: "json",
            timeout: 30000,
            success: function(result, textStatus) {
                $trinfo.removeClass('checking-task-status');
                $lupd.removeClass('action-progress');
                switch(result.status) {
                    case 'active':
                        // keep checking
                        break;
                    case 'completed':
                        taskCompleted($trinfo);
                        break;
                    default:
                        taskInactive($trinfo);
                        break;
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                $trinfo.removeClass('checking-task-status');
                $lupd.removeClass('action-progress');
            }
        });
    }

    function taskCompleted($trinfo) {
        removeTask($trinfo);
        $trinfo.triggerHandler('task-completed');
    }

    function taskInactive($trinfo) {
        removeTask($trinfo);
        $trinfo.find('.update-request').removeClass('hidden');
        $trinfo.find('.update-request-failed').addClass('hidden');
        $trinfo.find('.update-request-succ').addClass('hidden');
    }

    function removeTask($trinfo) {
        for (var i=0; i<tinfoRows.length; i++) {
            var $tri = tinfoRows[i];
            if ($trinfo == $tri) {
                delete tinfoRows[i];
                tinfoRows.splice(i, 1);
                break;
            }
        }

        if(tinfoRows.length == 0)
            running = false;
    }
}

