// there must be Google api and jsonrpc library loaded before using this Bridge

//var jsonURL = "http://ayoupov.nulana.com/hhs/jsonrpc";
//var jsonURL = "http://hhs.happyhoured.com/hhs/jsonrpc";
var jsonURL = null;
var jsonRelativeURL = "hhs/jsonrpc";
//var jsonURL = "http://localhost:8880/hhs/jsonrpc";
// var to store hh session id
var guestSessionId = null;
var HH_DEBUG_ENABLED = false;
var DEFAULT_CITY_ID = 2;
// NY, could be changed to anything
var HH_DEFAULT_CENTER = new GLatLng(40.756054, -73.986951);
var HH_DEFAULT_ZOOM_LEVEL = 10;
var HH_ASYNC_TIMEOUT = 0; // in ms
// if message was sent and no statusElement was passed hh responses with these codes would be ignored
var HH_SILENCED_STATUSES = [0,301];
// sorry for ugliness
var HH_TIME_TABLE = [
    [0, "12 AM"],
    [3600,"1 AM"],
    [2 * 3600,"2 AM"],
    [3 * 3600, "3 AM"],
    [4 * 3600,"4 AM"],
    [5 * 3600, "5 AM"],
    [6 * 3600,"6 AM"],
    [7 * 3600, "7 AM"],
    [8 * 3600,"8 AM"],
    [9 * 3600, "9 AM"],
    [10 * 3600,"10 AM"],
    [11 * 3600, "11 AM"],
    [12 * 3600,"12 PM"],
    [13 * 3600, "1 PM"],
    [14 * 3600,"2 PM"],
    [15 * 3600, "3 PM"],
    [16 * 3600,"4 PM"],
    [17 * 3600, "5 PM"],
    [18 * 3600,"6 PM"],
    [19 * 3600, "7 PM"],
    [20 * 3600,"8 PM"],
    [21 * 3600, "9 PM"],
    [22 * 3600,"10 PM"],
    [23 * 3600, "11 PM"],
];

var HH_DAYS = ["SUN","MON","TUE","WED","THU","FRI","SAT","SUN"];
var HH_STRETCH_DAYS = "MON,TUE,WED,THU,FRI,SAT,SUN";

// behavior constants
var HH_LOCATION_MANAGER_CENTER_ON_ADD = false;
var HH_LOCATION_MANAGER_SHOW_NO_HH_FOUND = false;
// max details in nearby
var HH_MAX_DETAIL_ROWS = 10;
// helper function
// make price look like "%d.02"
function padPrice(price)
{
    var p = "" + price / 100;
    var b = p.indexOf(".");
    if (b >= 0)
    {
        if (p.substr(b + 1).length == 1)
            p += "0";
    }
    else
        p += ".00";
    return p;
}

// add trim method to String class
String.prototype.trim = function ()
{
    return this.replace(/^\s*/, "").replace(/\s*$/, "");
};

Date.prototype.addHours = function(h)
{
    this.setTime(this.getTime() + (h * 60 * 60 * 1000));
    return this;
};

function getEntityId(elem)
{
    var entStr = elem.closest("tr").attr("id");
    return entStr.substr(entStr.indexOf('_') + 1);
}

function stretchDays(raw)
{
    if (raw.length < 8)
        return raw;
    var res = "";
    var hh_days = HH_STRETCH_DAYS.split(",");
    var idx, i, map = [];
    for (i in hh_days)
    {
        var hh_day = hh_days[i];
        idx = raw.indexOf(hh_day);
        if (idx >= 0)
            map.push(i);
    }
    var start, prval, val,tres;
    map.push(8);
    map.sort();
    for (i = 0,prval = map[0] - 1; i < map.length; i++)
    {
        if (!start)
            start = map[i];
        val = map[i];
        if (val - prval != 1)
        {
            if (prval - start >= 2)
                tres = hh_days[start] + "-" + hh_days[prval];
            else
                if (prval - start == 1)
                    tres = hh_days[start] + "," + hh_days[prval];
                else
                    tres = hh_days[start];
            res += ((res.length != 0) ? "," : "") + tres;
            start = val;
        }
        prval = val;
    }
    return res;
}

function breakAddress(address)
{
    var sl = [];
    var ad = address.trim().split(",");
    ad.pop(); // remove USA
    sl.push(ad.pop());
    sl.push(ad.pop());
    return ad.join(", ") + "<br>" + sl.join(", ");
}

// a bit ugly realization, could be overwritten
var HH_HH_SEARCH_TYPE = 0,
        HH_LOC_ADDR_NAME_SEARCH_TYPE = 1,
        HH_LOC_TYPE_ADDR_SEARCH_TYPE = 2,
        HH_NEARBY_SEARCH_TYPE = 3;

function HHSearchTypeCollection()
{
    this[HH_HH_SEARCH_TYPE] = new HHSearchType(HH_HH_SEARCH_TYPE, "happyhours");
    this[HH_LOC_ADDR_NAME_SEARCH_TYPE] = new HHSearchType(HH_LOC_ADDR_NAME_SEARCH_TYPE, "locations by address, name");
    this[HH_LOC_TYPE_ADDR_SEARCH_TYPE] = new HHSearchType(HH_LOC_TYPE_ADDR_SEARCH_TYPE, "locations by type, address");
    this[HH_NEARBY_SEARCH_TYPE] = new HHSearchType(HH_NEARBY_SEARCH_TYPE, "locations near");
}

function HHSearchType(id, displayName)
{
    this.id = id;
    this.displayName = displayName;
}

function HHFilter(offerType, timeStamp, day)
{
    //    this.locationType = locationType;
    this.offerType = offerType;
    this.ts = timeStamp;
    this.day = day;
}

// HHDictionary class provides access to HH dictionaries

HHDictionaries.prototype.getOfferTypeIdValue = function(id)
{
    var dic = this.offers;
    for (var i in dic)
    {
        if (dic[i].id == id)
            return dic[i].offer_type;
    }
    return null;
};

HHDictionaries.prototype.makeNamedList = function(dic, def)
{
    var res = [def];
    for (var i in dic)
    {
        res[dic[i].id] = dic[i].name;
    }
    return res;
};

HHDictionaries.prototype.makeOfferTypeList = function(dic, def)
{
    var res = [def];
    for (var i in dic)
        res[dic[i].id] = dic[i].offer_type;
    return res;
};

HHDictionaries.prototype.makeStateList = function(dic, def)
{
    var res = [def];
    for (var i in dic)
        res[dic[i].code] = dic[i].name;
    return res;
};

function HHDictionaries(bridge)
{
    bridge.checkSession();
    this.offerTypes = bridge.JSONRPCClient.Server.getOfferTypeList(guestSessionId).offer_types;
    this.offerTypeNames = this.makeNamedList(this.offerTypes);
    this.cities = bridge.JSONRPCClient.Server.getCityList(guestSessionId).cities;
    this.cityNames = this.makeNamedList(this.cities);
    this.hhTypes = bridge.JSONRPCClient.Server.getHappyHourTypeList(guestSessionId).happyhour_types;
    this.hhTypeNames = this.makeNamedList(this.hhTypes);
    this.hhDealTypes = bridge.JSONRPCClient.Server.getHappyHourDealTypeList(guestSessionId).happyhour_deal_types;
    this.hhDealTypeNames = this.makeNamedList(this.hhDealTypes);
    this.locationTypes = bridge.JSONRPCClient.Server.getLocationTypeList(guestSessionId).location_types;
    this.locationTypeNames = this.makeNamedList(this.locationTypes);
    this.offers = bridge.JSONRPCClient.Server.getOfferList(guestSessionId).offers;
    this.offerNames = this.makeNamedList(this.offers);
    this.offerOfferTypes = this.makeOfferTypeList(this.offers);
    this.states = bridge.JSONRPCClient.Server.getStateList(guestSessionId).states;
    this.stateNames = this.makeStateList(this.states);
}

function HHLocationCache()
{
}

HHLocationCache.prototype.clear = function()
{
    this.length = 0;
};

function HHXYZData()
{
    this.lat = null;
    this.lng = null;
    this.zoom = null;
}

function HHMarkerCache()
{
}

HHMarkerCache.prototype.clear = function()
{
    this.length = 0;
};

HHBridge.prototype.putLocations = function(locations, searchType, hhfilter)
{
    //    var nd = new Date();
    this.statusBusy("Loading...");
    var bridgeInstance = this;
    var marr = [];
    $.eachAsync(locations,
    {
        delay: HH_ASYNC_TIMEOUT,
        bulk:0,
        loop : function(index, location)
        {
            marr.push(bridgeInstance.addLocation(location, false, searchType, hhfilter));
        },
        end : function()
        {
            if (searchType != HH_NEARBY_SEARCH_TYPE)
            {
                bridgeInstance.zoomToMarkers(5, 5);
                //                bridgeInstance.markerManager.addMarkers(marr, 1, 19);
                //                bridgeInstance.markerManager.refresh();
                for (t in marr)
                {
                    bridgeInstance.map.addOverlay(marr[t]);
                }
                bridgeInstance.setMarkersLoaded(true);
            }
            setTimeout(function()
            {
                if (bridgeInstance.markersLoaded && bridgeInstance.tilesLoaded)
                {
                    bridgeInstance.statusBusy();
                }
            }, 5000);
        }
    });
    var detailHtml = "";
    if (!hhfilter)
    {
        var len = locations.length;
        if (len == 0)
            len = "no";
        detailHtml = "<table id='detailsTable'><tr class='hh-highlight-tr'><th>Results (" + len + " locations found)</th></tr>";
    }
    else detailHtml = "<table id='detailsTable'>";
    var detailCount = 0;
    $.eachAsync(locations,
    {
        delay : HH_ASYNC_TIMEOUT,
        bulk : 0,
        loop : function(index, location)
        {
            if (detailCount < HH_MAX_DETAIL_ROWS || searchType != HH_NEARBY_SEARCH_TYPE)
            {
                /*
                 <tr class="item hover" id="loc_xxx">
                 <td><h2>eto header</h2>
                 HH_Data
                 </td></tr>
                 */
                var tg = [];
                for (i in location.locationTypeIds)
                {
                    tg.push(bridgeInstance.dictionaries.locationTypeNames[location.locationTypeIds[i]]);
                }
                var gatheredTypes = tg.join(", ");
                var addSelectedClass = (searchType == HH_NEARBY_SEARCH_TYPE && detailCount == 0) ? " selected-item" : "";
                detailHtml += "<tr class='item event-header" + addSelectedClass + "' id='Loc_" + location.id + "'><td><h3>" + location.name + "</h3>";
                detailHtml += "<div class='location-type-info'>" + gatheredTypes + "</div>";
                detailHtml += "<div class='location-info'>" + location.address + "</div>";
                detailHtml += "<div class='location-info'>" + location.phone + "</div>";
                if (location.happyHours.length != 0)
                    detailHtml += bridgeInstance.htmlHH(location, hhfilter);
                detailHtml += "</td></tr>";
            }
            detailCount++;
        },
        end : function()
        {
            if (!hhfilter)
                detailHtml += "</table>";
            bridgeInstance.details.html(detailHtml);
            if (HH_DEBUG_ENABLED)
                console.log("appended details in " + (new Date() - nd));
            var nd = new Date();
            // make results clickable
            var locres = $(".item");
            var nd = new Date();
            locres.click(function(e)
            {
                var locId = getEntityId($(e.target));
                var loc = bridgeInstance.locationCache[locId];
                // append some info
                var point = new GLatLng(loc.latitude,
                        loc.longitude);
                var marker = bridgeInstance.markerCache[locId];
                if (marker)
                {
                    GEvent.trigger(marker, "click");
                }
                else
                    bridgeInstance.centerMap(point);
            });
            if (HH_DEBUG_ENABLED)
                console.log("added click features in " + (new Date() - nd));
            nd = new Date();
            locres.hover(
                    function ()
                    {
                        $(this).addClass("hover");
                    },
                    function ()
                    {
                        $(this).removeClass("hover");
                    });
            if (HH_DEBUG_ENABLED)
                console.log("added hover features in " + (new Date() - nd));
        }
    });
};

HHBridge.prototype.addLocation = function(loc, toCenter, searchType, hhfilter)
{
    var x = loc.latitude;
    var y = loc.longitude;
    if (this.markerCount == 0)
    {
        this.minX = x;
        this.maxX = x;
        this.minY = y;
        this.maxY = y;
    }
    else
    {
        if (x < this.minX) this.minX = x;
        if (x > this.maxX) this.maxX = x;
        if (y < this.minY) this.minY = y;
        if (y > this.maxY) this.maxY = y;
    }

    if (!toCenter)
        toCenter = HH_LOCATION_MANAGER_CENTER_ON_ADD;
    var point = new GLatLng(x, y);
    //    if (searchType != HH_NEARBY_SEARCH_TYPE || this.locationCache[loc.id] == undefined)
    var description;
    if (this.locationCache[loc.id] == undefined || !this.locationCache[loc.id].description)
    {
        var site = loc.website;
        if (site)
        {
            site = ("" + site).trim();
            if (site.indexOf("http:") != 0)
                site = "http://" + site;
        }
        var phone = (loc.phone + "").trim();
        var hhnum = 0;
        if (loc.happyHours)
            hhnum = loc.happyHours.length;
        var ln = (loc.name + "").trim();
        description = '<p class="balloon-title">' + ((site) ? "<a target='_blank' href='" + site + "'>" + ln + "</a>" : ln) + '</p>';
        //        if (hhnum > 0)
        //            description += " (" + hhnum + " happy hour" + ((hhnum != 1) ? "s" : "") + ")";
        description += "<br>" + breakAddress((loc.address + ""));
        description += ((phone && phone != "") ? "<br>" + phone : "");
        loc.description = description;
    }
    else
        description = this.locationCache[loc.id].description;
    // add some hh info to description

    var marker = this.markerCache[loc.id];
    if (!marker)
    {
        marker = new GMarker(point);
        var bridgeInstance = this;
        GEvent.addListener(marker, "click", function()
        {
            var isNearBySearch = searchType == HH_NEARBY_SEARCH_TYPE;
            marker.openInfoWindowHtml(description, {suppressMapPan:isNearBySearch});
            if (isNearBySearch)
            {
                var c = bridgeInstance.map.getCenter();
                if (point.lat() != c.lat() && point.lng() != c.lng())
                    bridgeInstance.centerMap(point);
            }
            // todo: change if nedeed
            $(".item").removeClass("selected-item");
            var l = $("#Loc_" + loc.id);
            l.addClass("selected-item");
            $(".search-content").scrollTo(l, 800);
        });
        if (!this.nearByPutMarkers[loc.id])
        {
            //            this.map.addOverlay(marker);
            this.nearByPutMarkers[loc.id] = true;
        }
        if (toCenter)
            this.centerMap(point);
        this.markerCache[loc.id] = marker;
    }
    else
    {
        if (!this.nearByPutMarkers[loc.id])
        {
            this.map.addOverlay(marker);
            this.nearByPutMarkers[loc.id] = true;
        }
    }
    this.markerCount++;
    this.locationCache[loc.id] = loc;
    return marker;
};

HHBridge.prototype.zoomToMarkers = function(slopPercentage, heightOffsetPct)
{
    var map = this.map;
    if (this.markerCount == 1)
        map.setCenter(new GLatLng(this.minX, this.minY), map.getZoom());
    else if (this.markerCount > 1)
    {
        var center = new GLatLng((this.minX + this.maxX) / 2, (this.minY + this.maxY) / 2);
        var span = new GSize(Math.abs(this.maxX - this.minX), Math.abs(this.maxY - this.minY));
        var slopWid = 0, slopHgt = 0;
        if (typeof slopPercentage != "undefined")
        {
            slopWid = span.width * slopPercentage / 200;
            slopHgt = span.height * slopPercentage / 200;
            span.width *= 1 + slopPercentage / 100;
            span.height *= 1 + slopPercentage / 100;
        }
        var deltaHgt = 0;
        if (typeof heightOffsetPct != "undefined")
        {
            deltaHgt = span.height * heightOffsetPct / 100;
            center = new GLatLng(center.lat() + deltaHgt, center.lng());
        }
        // needs slop
        var bounds = new GLatLngBounds(new GLatLng(this.minX - slopHgt, this.minY - slopWid),
                new GLatLng(this.maxX + slopHgt, this.maxY + slopWid)); // sw, ne
        var zoom = map.getBoundsZoomLevel(bounds);
        map.setCenter(center, zoom);
    }
};


HHBridge.prototype.centerMap = function (point, detail)
{
    var zoom = this.map.getZoom();
    var pan = false;
    pan = (detail == zoom);
    if (!detail)
        detail = zoom;
    if (!detail)
        detail = HH_DEFAULT_ZOOM_LEVEL;
    if (pan)
        this.map.panTo(point);
    else
        this.map.setCenter(point, detail);
};

function HHBridge(map_element_id, details_element_id, status_element_id)
{
    this.isSearching = false;
    this.geocoder = new GClientGeocoder();
    this.lastNearby = new HHXYZData();
    this.lastSearch = new HHXYZData();
    this.markersLoaded = false;
    this.tilesLoaded = false;
    if (jsonURL != undefined && jsonURL != null)
        this.JSONRPCClient = new JSONRpcClient(jsonURL);
    else
    {
        var l = document.domain + "";
        if (l.lastIndexOf("/") != l.length - 1)
            l = l + "/";
        this.JSONRPCClient = new JSONRpcClient("http://" + l + jsonRelativeURL);
    }
    this.dictionaries = new HHDictionaries(this);
    this.searchType = HH_HH_SEARCH_TYPE;
    this.lastAddress = '';
    this.hhfilter = null;
    this.searchTypeCollection = new HHSearchTypeCollection();
    this.nearByPutMarkers = [];
    this.locationCache = new HHLocationCache();
    this.markerCache = new HHMarkerCache();
    this.details = $("#" + details_element_id);
    this.markerCount = 0;
    var statusElement = (status_element_id) ? $("#" + status_element_id) : null;
    this.statusElement = statusElement;
    this.map_element_id = map_element_id;
    // init map
    if (GBrowserIsCompatible())
    {
        this.geocoder = new GClientGeocoder();
        this.map = new GMap2(document.getElementById(map_element_id), {backgroundColor : 'black'});
        this.map.addControl(new GLargeMapControl());
        this.map.addControl(new GMapTypeControl());
        this.map.enableScrollWheelZoom();
        // init GMap setting new center
        this.centerMap(HH_DEFAULT_CENTER);
        //this.markerManager = new MarkerManager(this.map);
    }
    else
        alert("Either your browser is not compatible with GoogleMaps or Google API is not loaded");
    if (this.geocoder && this.map && this.JSONRPCClient)
        return this;
    else
        return null;
}

HHBridge.prototype.setMarkersLoaded = function(state)
{
    this.markersLoaded = state;
    if (state && this.tilesLoaded)
        this.statusBusy();
};

HHBridge.prototype.clearEvents = function()
{
    GEvent.clearListeners(this.map, "tilesloaded");
};

HHBridge.prototype.tileEvents = function(locId)
{
    if (this.searchType != HH_NEARBY_SEARCH_TYPE)
    {
        var bridgeInstance = this;
        GEvent.addListener(this.map, "tilesloaded", function()
        {
            bridgeInstance.tilesLoaded = true;
            if (bridgeInstance.markersLoaded)
                bridgeInstance.statusBusy();
            if (locId != null && locId != undefined)
                $("#Loc_" + locId).click();
//            bridgeInstance.clearEvents();
        });
    }
};

HHBridge.prototype.hhMoveStartEvent = function()
{
    this.tilesLoaded = false;
};

HHBridge.prototype.hhMovedEvent = function()
{
    // check if we in nearby mode
    if (!this.isSearching && this.searchType == HH_NEARBY_SEARCH_TYPE)
    {
        var center = this.getCenter();
        if (center)
        {
            this.lastNearby.lat = center.lat();
            this.lastNearby.lng = center.lng();
            this.lastNearby.zoom = this.map.getZoom();
            this.nearBy(
                    center.lat(),
                    center.lng(),
                    this.getRadius());
        }
    }
};

HHBridge.prototype.getRadius = function()
{
    var bounds = this.map.getBounds();
    var d = bounds.getSouthWest();
    return this.map.getCenter().distanceFrom(d);
};

HHBridge.prototype.processLocationIds = function(ids)
{
    var status = ids.status;
    var clearMarkers = this.searchType != HH_NEARBY_SEARCH_TYPE;
    //    var clearMarkers = true;
    this.hhError(status);
    if (status == 0)
    {
        var idsParam = [];
        var locations = ids.locations;
        var isNearBySearch = (locations != null && locations != undefined);
        if (!isNearBySearch)
            locations = ids.locationIds;
        for (var i in locations)
        {
            var id;
            if (isNearBySearch)
                id = locations[i].id;
            else
                id = locations[i];
            //            if (!isNearBySearch || this.locationCache[id] == undefined)
            idsParam = idsParam.concat(id);
        }
        if (idsParam.length > 0)
        {
            var locs = this.getLocationsByIdList(idsParam);
            this.processLocations(locs, clearMarkers);
        }
    } else
    {
        if (clearMarkers)
        {
            this.locationCache.clear();
            //            this.markerCache.clear();
            this.map.clearOverlays();
            this.nearByPutMarkers.length = 0;
        }
    }
};

HHBridge.prototype.processLocations = function(locs, clearMarkers)
{
    status = locs.status;
    this.hhError(status);
    if (status == 0)
    {
        if (clearMarkers)
        {
            this.locationCache.clear();
            //                    this.markerCache.clear();
            this.map.clearOverlays();
            this.nearByPutMarkers.length = 0;
        }
        this.putLocations(locs.locations, this.searchType, this.hhfilter);
        if (this.searchType != HH_NEARBY_SEARCH_TYPE)
        {
            var c = this.map.getCenter();
            this.lastSearch.lat = c.lat();
            this.lastSearch.lng = c.lng();
            this.lastSearch.zoom = this.map.getZoom();
        }
    }
};

// search methods
HHBridge.prototype.listLocationsByAddressName = function(lat, lng, searchArea, name)
{
    this.setSearching(true);
    var centerFromRequest = (searchArea && searchArea != '');
    if (!centerFromRequest)
        this.centerMap(new GLatLng(lat, lng));
    var ids = this.JSONRPCClient.Server.listLocationsByAddressName(guestSessionId, lat, lng, searchArea, name);
    if (centerFromRequest)
    {
        this.centerMap(new GLatLng(ids.latitude, ids.longitude));
        this.lastAddress = ids.fullAddress;
    }
    this.processLocationIds(ids);
    this.setSearching(false);
};

HHBridge.prototype.listLocationsByTypeAddressRadius = function(locationType, lat, lng, searchArea, radius)
{
    this.setSearching(true);
    var centerFromRequest = (searchArea && searchArea != '');
    if (!centerFromRequest)
        this.centerMap(new GLatLng(lat, lng));
    var ids = this.JSONRPCClient.Server.listLocationsByTypeStringRadius(guestSessionId, locationType, lat, lng, searchArea, radius);
    if (centerFromRequest)
    {
        this.centerMap(new GLatLng(ids.latitude, ids.longitude));
        this.lastAddress = ids.fullAddress;
    }
    this.processLocationIds(ids);
    this.setSearching(false);
};

HHBridge.prototype.nearBy = function (lat, lng, radius)
{
    this.setSearching(true);
    this.centerMap(new GLatLng(lat, lng));
    var ids = this.JSONRPCClient.Server.listLocationsByLatLngRadius(guestSessionId, lat, lng, radius);
    var nd = new Date();
    this.processLocationIds(ids);
    if (HH_DEBUG_ENABLED)
        console.log("processed in " + (new Date - nd));
    this.setSearching(false);
};

HHBridge.prototype.searchHappyHours = function (locationType, offerType, timeStamp, day, lat, lng, searchArea)
{
    this.setSearching(true);
    this.hhfilter = new HHFilter(offerType, timeStamp, day);
    var centerFromRequest = (searchArea && searchArea != '');
    if (!centerFromRequest)
        this.centerMap(new GLatLng(lat, lng));
    var ids = this.JSONRPCClient.Server.searchHappyHours(guestSessionId, locationType, offerType, timeStamp, day, lat, lng, searchArea);
    if (ids.status == 0 && centerFromRequest)
    {
        this.centerMap(new GLatLng(ids.latitude, ids.longitude));
        this.lastAddress = ids.fullAddress;
    }
    this.processLocationIds(ids);
    this.setSearching(false);
    return ids.status;
};

HHBridge.prototype.getLocationsByIdList = function(ids)
{
    return this.JSONRPCClient.Server.getLocationsByIdList(guestSessionId, ids);
};

HHBridge.prototype.checkSession = function()
{
    if (guestSessionId == null)
        guestSessionId =
        this.JSONRPCClient.Server.login
                ("guest@nulana.com", "084E0343A0486FF05530DF6C705C8BB4", "HHRPCBridge", "HHRPCBridge", null)
                .sessionId;
};

// helper DOM functions

HHBridge.prototype.insertDays = function(elem, inputId, selId)
{
    var days = ["SAT","SUN","MON","TUE","WED","THU","FRI"];
    var h = "<select id='" + inputId + "' name='" + inputId + "'>";
    for (var i in days)
    {
        var day = days[i];
        h += "<option value='" + day + "' " + (day == selId ? "selected" : "") + ">" + day + "</option>";
    }
    h += "</select>";
    $("#" + elem).html(h);
};

HHBridge.prototype.insertTimestamp = function (elem, inputId, selId)
{

    var h = "<select id='" + inputId + "' name='" + inputId + "'>";
    for (var i in HH_TIME_TABLE)
    {
        var val = HH_TIME_TABLE[i][0];
        var sname = HH_TIME_TABLE[i][1];
        h += "<option value='" + val + "' " + (val == selId ? "selected" : "") + ">" + sname + "</option>";
    }
    h += "</select>";
    $("#" + elem).html(h);
};

HHBridge.prototype.insertInput = function(dic, elem, inputId, selId, firstElement)
{
    if (!inputId)
        inputId = elem + "Selector";
    var h = "<select id='" + inputId + "' name='" + inputId + "'" + ">";
    if (firstElement)
        h += "<option value='0' " + (id == selId ? "selected" : "") + ">" + firstElement + "</option>";
    for (var i in dic)
    {
        var id = dic[i].id;
        h += "<option value='" + id + "' " + (id == selId ? "selected" : "") + ">" + dic[i].name + "</option>";
    }
    h += "</select>";
    $("#" + elem).html(h);
};

HHBridge.prototype.getSearchTypes = function(id)
{
    if (!id)
        return this.searchTypeCollection;
    else
        return this.searchTypeCollection[id];
};

HHBridge.prototype.insertSearchTypes = function(elem, inputId, selId, bridgeName, changeEvent)
{
    if (!inputId)
        inputId = elem + "Selector";
    if (!bridgeName)
        bridgeName = "bridge";
    var onchangeEvent = bridgeName + ".searchType=$(\"#" + inputId + "\").val();";
    if (changeEvent)
        onchangeEvent += changeEvent;
    var h = "<select id='" + inputId + "' name='" + inputId + "'" + " onchange='" + onchangeEvent + "'>";
    for (var i in this.searchTypeCollection)
    {
        var searchType = this.searchTypeCollection[i];
        h += "<option value='" + searchType.id + "' " + (searchType.id == selId ? "selected" : "") + ">" + searchType.displayName + "</option>";
    }
    h += "</select>";
    $("#" + elem).html(h);
};

// view helpers

HHBridge.prototype.hhError = function (status)
{
    var msg = "";
    switch (status)
            {
        case 0 : msg = "ok";break;
        case 101 : msg = "Invalid Session";break;
        case 201 : msg = "Internal Error, please contact with developers"; break;
        case 301 : msg = "Location not found"; break;
        case 302 : msg = "User authentication error"; break;
        case 602 : msg = "Area not found"; break;
        case 606 : msg = "Reverse geocoder error"; break;
        default: msg = "Unknown error " + status;
    }
    if (!(status in HH_SILENCED_STATUSES))
        this.statusError(msg);
};

HHBridge.prototype.statusBusy = function(msg)
{
    var status = this.statusElement;
    if (this.statusElement)
    {
        var wrapper = $(this.statusElement).parent();
        var mapElem = $("#" + this.map_element_id);
        if (!msg)
        {
            wrapper.hide(20, function()
            {
                mapElem.fadeTo(200, 1);
            });
        }
        else
        {
            mapElem.fadeTo(200, 0.05, function()
            {
                status.html(msg).show();
                wrapper.show();
            });
        }
    }

};

HHBridge.prototype.statusError = function(msg)
{
    var status = this.statusElement;
    if (this.statusElement)
    {
        var wrapper = $(this.statusElement).parent();
        var mapElem = $("#" + this.map_element_id);
        mapElem.fadeTo(200, 0.05, function()
        {
            status.html(msg);
            wrapper.show(1000, function()
            {
                setTimeout(function()
                {
                    wrapper.hide();
                    mapElem.fadeTo(1000, 1, function()
                    {
                        status.hide();
                    });
                }, 1000);
            });
        });
    }
};

HHBridge.prototype.setSearching = function(isSearching)
{
    this.isSearching = isSearching;
    if (isSearching)
    {
        this.markersLoaded = false;
        var nd = new Date();
        var bridgeInst = this;
        this.markerCount = 0;
        setTimeout(function()
        {
            //            if (bridgeInst.searchType != HH_NEARBY_SEARCH_TYPE)
            //            {
            var infow = bridgeInst.map.getInfoWindow();
            if (infow && !infow.isHidden())
                infow.hide();
            //            }
            if (bridgeInst.searchType != HH_HH_SEARCH_TYPE)
            {
                bridgeInst.hhfilter = null;
            }
            if (bridgeInst.searchType != HH_NEARBY_SEARCH_TYPE)
            {
                bridgeInst.markerCount = 0;
            }
            bridgeInst.details.empty();
        }, 5);
        if (HH_DEBUG_ENABLED)
            console.log("updated search in " + (new Date() - nd));
    }
};

HHBridge.prototype.getCenter = function()
{
    return this.map.getCenter();
};

HHBridge.prototype.htmlHH = function(loc, hhfilter)
{
    var hhs = loc.happyHours;
    var h = "";
    var gathered = "";
    var realHHCount = 0, checkFilter = (hhfilter != null && hhfilter != undefined);
    if (hhs.length == 0 && HH_LOCATION_MANAGER_SHOW_NO_HH_FOUND)
    {
        h += "<div class='no-data'>No happy hours on this location</div>";
    } else
    {
        for (var i in hhs)
        {
            var hh = hhs[i];
            var type = hh.hhTypeId;
            var dealtype = hh.hhDealTypeId;
            var openStart = hh.openStart;
            var openEnd = hh.openEnd;
            var start = 0, end = 0;
            for (var k in HH_TIME_TABLE)
            {
                if (HH_TIME_TABLE[k][0] == hh.startTS)
                    start = HH_TIME_TABLE[k][1];
                if (HH_TIME_TABLE[k][0] == hh.endTS)
                    end = HH_TIME_TABLE[k][1];
            }
            var days = hh.days;
            var filtered = (checkFilter) && ((days.indexOf(hhfilter.day) >= 0) || hhfilter.day == null);
            if (days.split(",").length == 7)
                days = "Every day ";
            else
                days = stretchDays(days);
            // process filtered
            if (checkFilter && filtered)
            {
                filtered = filtered && (
                        hhfilter.ts == null ||
                        (openStart && openEnd) ||
                        (openStart && (hh.endTS - 43200 <= hhfilter.ts && hhfilter.ts <= hh.endTS)) ||
                        (openEnd && (hh.startTS <= hhfilter.ts && hhfilter.ts <= hh.startTS + 43200)) ||
                        (hh.startTS <= hh.endTS && hh.startTS <= hhfilter.ts && hhfilter.ts < hh.endTS)
                                ||
                        (hh.startTS > hh.endTS && (hh.startTS <= hhfilter.ts || hhfilter.ts < hh.endTS)));
//                 "and ((hh.openStart=true AND hh.openEnd=true) " +
//                "OR (hh.openStart=true AND (hh.endTS - 43200 <= :ts and :ts <= hh.endTS)) " +
//                "OR (hh.openEnd=true AND (hh.startTS <= :ts and :ts <= hh.startTS + 43200)) "+
//                "or (hh.startTS <= hh.endTS and hh.startTS <= :ts and  :ts < hh.endTS) " +
//                "or (hh.startTS > hh.endTS and (hh.startTS <= :ts or :ts < hh.endTS))) " +

                if (hhfilter.offerType != null)
                    if (dealtype != 5 && filtered && (hhfilter.offerType != 0))
                    {
                        var found = false;
                        for (var l in hh.offers)
                        {
                            found = this.dictionaries.offerOfferTypes[hh.offers[l]] == hhfilter.offerType;
                            if (found)
                                break;
                        }
                        filtered = filtered && found;
                    }
            }
            if (filtered || hhfilter == null)
            {
                var description = hh.description;
                var price = hh.price;
                var amount = hh.amount;
                var ladies = hh.ladies;
                var offers = hh.offers;
                var fs;
                switch (dealtype)
                        {
                    case 1:
                    {// standard
                        var offerStr = "";
                        if (offers)
                        {
                            for (var j in offers)
                            {
                                var offerId = offers[j];
                                offerStr += this.dictionaries.offerNames[offerId];
                                if (j != offers.length - 1)
                                    offerStr += ", ";
                            }
                        } else
                            offerStr += "No offers selected!";
                        switch (type)
                                {
                            case 1: // offer
                                if (price != 0)
                                    fs = "$" + padPrice(price) + " for " + offerStr;
                                else
                                    fs = "Free " + offerStr;
                                break;
                            case 2: // -$
                                fs = fs = "$" + padPrice(price) + " off " + offerStr;
                                break;
                            case 3: // -%
                                fs = price + "% off " + offerStr;
                                break;
                                break;
                            case 4: // N for 1
                                fs = amount + " for 1 " + offerStr; break;
                            case 5: // N for $
                                fs = amount + " " + offerStr + " for $" + padPrice(price);
                                break;
                            case 6:
                                return description;
                        }
                    }
                        break;
                    case 2:
                    {// food + drink
                        if (offers != null && offers != undefined && offers.length == 2)
                        {
                            var food = this.dictionaries.offerNames[offers[0]];
                            var drink = this.dictionaries.offerNames[offers[1]];
                            switch (type)
                                    {
                                case 1: // offer
                                    if (price != 0)
                                        fs = "$" + padPrice(price) + " for " + food + " + " + drink + " back";
                                    else
                                        fs = "Free " + food + " + " + drink;
                                    break;
                                case 2: // -$
                                    fs = "$" + padPrice(price) + " off " + food + " + " + drink + " back";
                                    break;
                                case 3: // -%
                                    fs = price + "% off " + food + " + " + drink;
                                    break;
                            }
                        } else
                            fs = "Some offers are not selected!";
                    }
                        break;
                    case 3:
                    {// shot + beer
                        if (offers != null && offers != undefined && offers.length == 2)
                        {
                            var shot = this.dictionaries.offerNames[offers[0]];
                            var beer = this.dictionaries.offerNames[offers[0]];
                            switch (type)
                                    {
                                case 1: // offer
                                    if (price != 0)
                                        fs = "$" + padPrice(price) + " for " + shot + " + " + beer + " back";
                                    else
                                        fs = "Free " + shot + " + " + beer;
                                    break;
                                case 2: // -$
                                    fs = "$" + padPrice(price) + " off " + shot + " + " + beer + " back";
                                    break;
                                case 3: // -%
                                    fs = price + "% off " + shot + " + " + beer;
                                    break;
                            }
                        } else
                            fs = "Some offers are not selected!";
                    }
                        break;
                    case 4: // wwb
                        switch (type)
                                {
                            case 1: // offer
                                if (price != 0)
                                    fs = "$" + padPrice(price) + " for Beer, Well, Wine";
                                else
                                    fs = "Free Beer, Well, Wine";
                                break;
                            case 2: // -%
                                fs = padPrice(price) + " off Beer, Well, Wine";
                                break;
                            case 3: // -$
                                fs = price + "% off Beer, Well, Wine";
                        }
                        break;
                    case 5: // other
                        fs = description;
                        break;
                    default:
                        fs = "Bad HH";
                }

                if (ladies)
                    fs += " (Ladies night)";
                /*
                 <div class="start-time-info">from 11 to 12</div>
                 <div class="days-info">Su, Mo, Tu</div>
                 <div class="text-info">some information here</div> repeat
                 */
                var time;
                if (openStart && openEnd)
                    time = "Whole day";
                else
                {
                    if (openStart)
                        start = "opening";
                    if (openEnd)
                        end = "closed";
                    time = 'from ' + start + ' till ' + end;
                }
                gathered += '<div class="days-info">' + days + '</div>' +
                            '<div class="start-time-info">&nbsp;' + time + '</div>' +
                            '<div class="text-info">' + fs + '</div>';
                realHHCount++;
            }
        }
        if (realHHCount == 0 && HH_LOCATION_MANAGER_SHOW_NO_HH_FOUND)
        {
            h += "<div class='no-data'>No happy hours on this location</div>";
        }
        else
        {
            if (realHHCount > 0)
            {
                h += gathered;
            }
        }
    }
    return h;
};

function getUrlParameter(name)
{
    name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
    var regexS = "[\\?&]" + name + "=([^&#]*)";
    var regex = new RegExp(regexS);
    var results = regex.exec(window.location.href);
    if (results == null)
        return "";
    else
    {
        var res = results[1];
        var idx = res.lastIndexOf("/"); if (idx > 0) res = res.substr(0, idx);
        return res;
    }
}
;