/**
 * @fileOverview MapJack integration.
 */

/**
 * HViewer.
 * 
 * @class MapJack integration.
 *
 * @property {HMarker} HViewer.avatar Use HViewer.avatar to get HMarker instance of avatar.
 */
function HViewer() { }

/**
 * Padding in percent when to pan map to center on avatar.
 * @constant
 * @private
 */
HViewer.SCROLL_PADDING = 0.90;

/**
 * URL where avatar images are located.
 * @constant
 * @private
 */
HViewer.AVATAR_IMG_BASE_URL = "http://www.hitta.se/Images/hmap/gatubild/";

/**
 * File extension of avatar images.
 * @constant
 * @private
 */
HViewer.AVATAR_IMG_FILE_EXT = ".gif";

/**
 * Stroke (outline) color of beam.
 * @constant
 * @private
 */
HViewer.AVATAR_BEAM_STROKE_COLOR = "102, 102, 255";

/**
 * Stroke (outline) opacity of beam.
 * @constant
 * @private
 */
HViewer.AVATAR_BEAM_STROKE_OPACITY = 1;

/**
 * Line width of stroke (outline) of beam.
 * @constant
 * @private
 */
HViewer.AVATAR_BEAM_STROKE_WEIGHT = 1;

/**
 * Fill color of beam.
 * @constant
 * @private
 */
HViewer.AVATAR_BEAM_FILL_COLOR = "170, 170, 255";

/**
 * Fill color of beam.
 * @constant
 * @private
 */
HViewer.AVATAR_BEAM_FILL_OPACITY = 0.5;

/**
 * Length (radius) of beam.
 * @constant
 * @private
 */
HViewer.AVATAR_BEAM_LENGTH = 35;

/**
 * Width (in degrees) of of beam.
 * @constant
 * @private
 */
HViewer.AVATAR_BEAM_ANGLE_DEGREES = 90;
HViewer.AVATAR_BEAM_ANGLE = HViewer.AVATAR_BEAM_ANGLE_DEGREES * Math.PI / 180;

/**
 * Number of points to build the arc. More makes it smoother.
 * @constant
 * @private
 */
HViewer.AVATAR_BEAM_ARC_POINTS = HViewer.AVATAR_BEAM_ANGLE_DEGREES / 10;


/**
 * Event triggered when Flash callback is made du to change of position or
 * heading.
 * 
 * @param {HPointRT90} rt90point Avatar RT90 position.
 * @param {Number} heading Avatar heading.
 * @private
 * @event
 */
HViewer.EVENT_STATUS_UPDATE = "viewerstatusupdate";

/**
 * Initialize viewer and associated HMap.
 *
 * @param {Element} element Flash client DOM element.
 * @param {HMap} hmap HMap instance to use with Flash client.
 */
HViewer.initialize = function(element, hmap) {
    HViewer.hmap = hmap;
    HViewer.viewer = element;

    HViewer.initMap();

    HViewer.isLoaded = true;
    HViewer.shouldRegisterClick = true;

    //Raden nedan gör att det går jättesegt att växla från karta till gatubild i IE
    //Lösningen är att kommentera bort den just nu. Det verkar ha något med att flash inte är
    //färdigladdat när focus försöker sättas på flashdelen.
    //HViewer.viewer.focus();
};

/**
 * Initialize listeners of HMap.
 */
HViewer.initMap = function () {
    var hmap = HViewer.hmap;

    // map configuration
    hmap.disableDoubleClickZoom();

    // add dots layer
    HViewer.dotsLayer = new StreetViewDotsMapProvider();
    hmap.getMapProviderManager().add(HViewer.dotsLayer);

    // preload avatar images
    HViewer.avatarImages = [];
    try {
        for (var i = 0; i < 8; i++) {

            var imgSrc = HViewer.AVATAR_IMG_BASE_URL + "avatar" + i + HViewer.AVATAR_IMG_FILE_EXT;

            try {
                var img = new Image();
                img.src = imgSrc;
                HViewer.avatarImages[i] = img;
            }
            catch (ex) {               
                HViewer.avatarImages[i] = imgSrc;
            }
        }
    }
    catch (ex) {

    }

    // avatar
    var icon = new HIcon(HViewer.AVATAR_IMG_BASE_URL + "avatar0" + HViewer.AVATAR_IMG_FILE_EXT);
    icon.anchor = new HPoint(9, 35);
    icon.infoBoxAnchorTop = new HPoint(9, -2);
    icon.infoBoxAnchorBottom = new HPoint(9, 40);
    icon.size = new HSize(18, 39);

    HViewer.avatar = new HMarker(new HPointRT90(6588685, 1621660), icon);
    HViewer.avatar.enableDragging();

    HViewer.avatarData = {};
    HViewer.avatarData.position = HViewer.avatar.getRT90Point();
    HViewer.avatarData.heading = 0;

    // beam drag marker
    var beamDragIcon = new HIcon(HViewer.AVATAR_IMG_BASE_URL + "beamdrag.png");
    beamDragIcon.anchor = new HPoint(35, 35);
    beamDragIcon.size = new HSize(70, 70);
    HViewer.beamDrag = new HMarker(new HPointRT90(6588685, 1621660), beamDragIcon);

    // listeners
    HViewer.listeners = [];

    HViewer.listeners.push(HEvent.addListener(hmap, HMap.EVENT_MAP_MOVE, HViewer.mapMove));
    HViewer.listeners.push(HEvent.addListener(hmap, HMap.EVENT_MAP_CLICK, HViewer.mapClick));

    HViewer.listeners.push(HEvent.addListener(HViewer.avatar, HMarker.EVENT_DRAG_START, HViewer.avatarDragStart));
    HViewer.listeners.push(HEvent.addListener(HViewer.avatar, HMarker.EVENT_DRAG_END, HViewer.avatarDragEnd));

    var beanDragElement = HViewer.beamDrag.getElement();
    HViewer.listeners.push(HEvent.addDomListener(beanDragElement, "mousedown", HViewer.beamDragStart));
    HViewer.listeners.push(HEvent.addDomListener(hmap.map, "mousemove", HViewer.beamDragMove));
    HViewer.listeners.push(HEvent.addDomListener(hmap.map, "mouseup", HViewer.beamDragEnd));

    hmap.addOverlay(HViewer.beamDrag);
    hmap.addOverlay(HViewer.avatar);
};

/**
 * Clean up when removing Flash. Removes listeners, overlays, dot layer
 * and re-enables zoom double click.
 */
HViewer.remove = function() {
    // remove listeners
    HEvent.removeListeners(HViewer.listeners);

    // clear beam
    HViewer.clearBeam();

    // remove overlays
    HViewer.hmap.removeOverlay(HViewer.beamDrag);
    HViewer.hmap.removeOverlay(HViewer.avatar);

    // remove dots layer
    HViewer.hmap.getMapProviderManager().remove(HViewer.dotsLayer);

    HViewer.hmap.enableDoubleClickZoom();
};

/**
 * Flash client status update callback.
 *
 * @param {Array} status Status array from Flash client.
 */
HViewer.statusUpdateCallback = function(status) {
    var message = String(status[0]);
    var urlEncoded = String(status[1]);
    var dotType = parseInt(status[2]);
    var showDots = Boolean(status[3]);

    // position
    var index = Number(status[4]);
    var lat = Number(status[5]);
    var lng = Number(status[6]);
    var heading = Number(status[7]);
    var viewAngle = Number(status[8]);

    HViewer.avatarData.heading = heading;
    HViewer.avatarData.position = new HPointLatLng(lat, lng);

    var rt90point = HViewer.hmap.fromLatLngToRT90(HViewer.avatarData.position);
    if (rt90point.north > 0 && rt90point.east > 0) {
        HViewer.updateAvatar();
        HEvent.trigger(HViewer, HViewer.EVENT_STATUS_UPDATE, rt90point, heading);
    } else {
        // workaround for Safari bug with weird coordinates (resulting in negative RT90 coordinates)
        if (HViewer.avatar.getRT90Point() !== null) {
            HViewer.navigateToRT90(HViewer.avatar.getRT90Point(), true);
        }
    }
};

// Hardcoded callback function by flash-client, map to own function.
var panoramaStatus = HViewer.statusUpdateCallback;

/**
 * Set location in viewer to given lat/lng coordinate.
 * 
 * @param {HPointLatLng} pointLatLng Location to set.
 * @param {Boolean} defaultHeading True if default heading should be used.
 */
HViewer.navigateTo = function(pointLatLng, defaultHeading) {
    HViewer.viewer.navigateTo(pointLatLng.getLat(), pointLatLng.getLng(), defaultHeading);
};

/**
 * Set location in viewer to given RT90 coordinate.
 *
 * @param {HPointRT90} rt90point Location to set.
 * @param {Boolean} defaultHeading True if default heading should be used.
 */
HViewer.navigateToRT90 = function(rt90point, defaultHeading) {
    HViewer.setAvatarPosition(rt90point);
    var pointLatLng = HViewer.hmap.fromRT90ToLatLng(rt90point);
    HViewer.navigateTo(pointLatLng, defaultHeading);
};

/**
 * Set location in viewer given URL encoded lat/lng coordinate.
 *
 * @param {String} code URL encoded coordinate.
 */
HViewer.navigateToEncoded = function(code) {
    HViewer.viewer.navigateToEncoded(code);
};

/**
 * Make view in viewer turn to given heading.
 *
 * @param {Number} heading Heading in degrees.
 */
HViewer.turnTo = function(heading) {
    HViewer.viewer.turnTo(heading);
};

/**
 * Make view turn to given point.
 * 
 * @param {HPointRT90} rt90point Point to turn to.
 */
HViewer.turnToRT90 = function(rt90point) {
    var current = HViewer.avatar.getRT90Point();

    var tan = Math.atan2(
        current.east - rt90point.east,
        current.north - rt90point.north
    );

    HViewer.turnTo(180 + 180 * tan / Math.PI);
};

/**
 * Returns current avatar heading.
 *
 * @returns {Number} Heading in degrees.
 */
HViewer.getHeading = function() {
    if (HViewer.avatarData && HViewer.avatarData.heading) {
        return HViewer.avatarData.heading;
    } else {
        return 0;
    }
};

/**
 * Update avatar (using HViewer.avatarData).
 */
HViewer.updateAvatar = function () {
    if (!HViewer.avatarData.position) {
        return;
    }

    var rt90point = HViewer.hmap.fromLatLngToRT90(HViewer.avatarData.position);

    // update avatar position
    HViewer.setAvatarPosition(rt90point);

    // get visible bounds
    var bounds = HViewer.hmap.getBounds();

    // shrink it slightly to make map pan when avatar is near edge
    bounds.scale(HViewer.SCROLL_PADDING);

    if (!bounds.containsRT90(rt90point)) {
        // pan to make avatar center in map

        if (!(HViewer.panMapToPoint && rt90point.equals(HViewer.panMapToPoint))) {
            HViewer.panMapToPoint = rt90point;
            HViewer.hmap.panTo(rt90point);
        }
    }

    // update avatar heading
    var avatarImage = HViewer.avatarImages[Math.round(HViewer.avatarData.heading / 45) % 8];

    if (typeof (avatarImage) == "object") {
        HViewer.avatar.setImage(avatarImage.src);
    }
    else if (typeof (avatarImage == "string")) {
        HViewer.avatar.setImage(avatarImage);
    }
    HViewer.drawBeam();
};

/**
 * Set avatar position.
 *
 * @param {HPointRT90} rt90point
 */
HViewer.setAvatarPosition = function(rt90point) {
    HViewer.avatar.setRT90Point(rt90point);
    HViewer.beamDrag.setRT90Point(rt90point);
};

/**
 * Draw beam based on HViewer.avatarData.heading.
 */
HViewer.drawBeam = function() {
    var polyLayer = HViewer.hmap.getPolyLayer();

    var ctx = polyLayer.getCtxVolatile();

    polyLayer.clear(ctx);
    
    var rt90point = HViewer.avatar.getRT90Point();
    
    var center = HViewer.hmap.fromRT90toContainerPixel(rt90point);

    var heading = HViewer.avatarData.heading * Math.PI / 180;

    var beam = [];
    beam.push(center);
    for (var i = 0; i <= HViewer.AVATAR_BEAM_ARC_POINTS; i++)
    {
        var ang = heading + (HViewer.AVATAR_BEAM_ANGLE * i / HViewer.AVATAR_BEAM_ARC_POINTS) - (HViewer.AVATAR_BEAM_ANGLE / 2);
        var x = Math.sin(ang) * HViewer.AVATAR_BEAM_LENGTH;
        var y = -Math.cos(ang) * HViewer.AVATAR_BEAM_LENGTH;
        beam[i + 1] = new HPoint(x + center.x, y + center.y);
    }
    beam[beam.length] = beam[0];

    // fill
    ctx.save();
    polyLayer.setStyle(ctx, {
        fillColor: HViewer.AVATAR_BEAM_FILL_COLOR,
        fillOpacity: HViewer.AVATAR_BEAM_FILL_OPACITY,
        strokeColor: HViewer.AVATAR_BEAM_STROKE_COLOR,
        strokeOpacity: HViewer.AVATAR_BEAM_STROKE_OPACITY,
        weight: HViewer.AVATAR_BEAM_STROKE_WEIGHT
    });

    polyLayer.drawPixelPoly(ctx, beam, true);
    polyLayer.drawPixelPoly(ctx, beam, false);
    ctx.restore();
};

/**
 * Clear beam.
 */
HViewer.clearBeam = function() {
    var polyLayer = HViewer.hmap.getPolyLayer();
    polyLayer.clear(polyLayer.getCtxVolatile());
};

/**
 * Update location in viewer.
 *
 * @param {HPointRT90} rt90point Position to set in viewer.
 */
HViewer.updateViewerPosition = function(rt90point) {
    HViewer.navigateToRT90(rt90point, true);
    HViewer.viewer.focus();
};

/**
 * Update viewer heading.
 *
 * @param {Number} heading Heading in degrees (0-360).
 */
HViewer.updateViewerHeading = function(heading) {
    HViewer.avatarData.heading = heading;
    HViewer.updateAvatar();
    HViewer.turnTo(heading);
};

/**
 * Avatar drag start event listener.
 * 
 * @private
 */
HViewer.avatarDragStart = function() {
    HViewer.clearBeam();
};

/**
 * Avatar drag end event listener.
 *
 * @param {HPointRT90} rt90point Position where avatar drag stopped.
 * @private
 */
HViewer.avatarDragEnd = function(rt90point) {
    // delay setting dragging state to false to allow click event handler
    // to check for dragging (to prevent click to register when
    // dragging stopped)
    HViewer.shouldRegisterClick = false;
    setTimeout(function() { HViewer.shouldRegisterClick = true; }, 100);

    HViewer.beamDrag.setRT90Point(rt90point);
    HViewer.updateViewerPosition(rt90point);
};

/**
 * Map move event listener. Redraw beam when moving map.
 * @private
 */
HViewer.mapMove = function() {
    HViewer.drawBeam();
};

/**
 * Map click event listener. Position avatar where clicked.
 * 
 * @param {HPointRT90} rt90point Map click position.
 * @private
 */
HViewer.mapClick = function(rt90point) {
    if (HViewer.shouldRegisterClick) {
        HViewer.beamDrag.setRT90Point(rt90point);
        HViewer.updateViewerPosition(rt90point);
    }
};

/**
 * DOM Event listener to start dragging of beam.
 *
 * @param {Event} event Mouse event.
 * @private
 */
HViewer.beamDragStart = function(event) {
    var e = event || window.event;

    HViewer.isDraggingBeam = true;
    HViewer.shouldRegisterClick = false;
    HViewer.beamDragAvatarCenter = HViewer.avatar.getContainerPixelPoint();

    HViewer.updateViewerHeading( HViewer.getBeamDragAngle(e) );

    return HEvent.stopEvent(e);
};

/**
 * DOM Event listener to rotate beam.
 *
 * @param {Event} event Mouse event.
 * @private
 */
HViewer.beamDragMove = function(event) {
    if (HViewer.isDraggingBeam) {
        var e = event || window.event;

        HViewer.updateViewerHeading( HViewer.getBeamDragAngle(e) );

        return HEvent.stopEvent(e);
    }

    return true;
};

/**
 * DOM Event listener to end beam drag.
 *
 * @param {Event} event Mouse event.
 * @private
 */
HViewer.beamDragEnd = function(event) {
    if (HViewer.isDraggingBeam) {
        var e = event || window.event;

        HViewer.isDraggingBeam = false;

        // delay setting dragging state to false to allow click event handler
        // to check for dragging (to prevent click to register when
        // dragging stopped)
        HViewer.shouldRegisterClick = false;
        setTimeout(function() { HViewer.shouldRegisterClick = true; }, 100);

        return HEvent.stopEvent(e);
    }
    
    return true;
};

/**
 * Calculate angle of dragged beam based on mouse coordintes.
 *
 * @param {Event} event Mouse event.
 * @return {Number} Angle of beam (0-360 where 0 is north).
 * @private
 */
HViewer.getBeamDragAngle = function(event) {
    var pos = HMapHelpers.getMousePosition(event);
    var G = HViewer.hmap.globals;

    var tan = Math.atan2(
        HViewer.beamDragAvatarCenter.x - (pos.x - G.mapPosition.x),
        -(HViewer.beamDragAvatarCenter.y - (pos.y - G.mapPosition.y))
    );

    return 180 + 180 * tan / Math.PI;
};

/**
 * StreetViewDotsMapProvider constructor.
 *
 * @class Transparent layer with street view photo locations marked as dots.
 *
 * @augments HMapProvider
 * @constructor
 */
function StreetViewDotsMapProvider() {
    this.elements = [];
}

// StreetViewDotsMapProvider inherits from HMapProvider.
StreetViewDotsMapProvider.prototype = new HMapProvider();

StreetViewDotsMapProvider.DOT_TILE_BASE_URL = "http://map4.static.hitta.se/tile/v1/2/";

/**
 * Initiates DOM elements representing tiles and adds them to
 * parent container. Depends on HMap.getPaneMap() and
 * HMap.getTileCount().
 *
 * @param {HMap} hmap HMap instance.
 */
StreetViewDotsMapProvider.prototype.initTiles = function(hmap) {
    this.hmap = hmap;
    var parent = this.hmap.getPaneMap();
    var tileCount = this.hmap.getTileCount();
    
    this.elements.length = tileCount;
    this.container = parent;

    for (var i = 0; i < tileCount; i++) {
        var img = document.createElement("img");

        HMapHelpers.applyStyle([img], {
            position: "absolute",
            top: -HTile.TILE_WIDTH + "px",
            left: -HTile.TILE_HEIGHT + "px",
            width: HTile.TILE_WIDTH + "px",
            height: HTile.TILE_HEIGHT + "px"
        });

        this.elements[i] = img;

        parent.appendChild(img);
    }
};

/**
 * Update tile content.
 *
 * @param {Number} tileIndex Index of tile to update.
 * @param {Number} resolution Current map resolution.
 * @param {Number} mapRow Current map row of tile.
 * @param {Number} mapColumn Current map column of tile.
 */
StreetViewDotsMapProvider.prototype.updateTile = function(tileIndex, resolution, mapRow, mapColumn) {
    var srcUrl = StreetViewDotsMapProvider.DOT_TILE_BASE_URL + resolution + "/" + mapRow + "/" + mapColumn;

    if (HMapHelpers.isIEVersionBelow(7)) {
        // png fix for IE < v7
        HMapHelpers.applyIEPNGFix(this.elements[tileIndex], srcUrl);
    } else {
        this.elements[tileIndex].src = srcUrl;
    }
};

