var mouse = {
    timer: null,
    ev: null
}

/*********************************/
/* Roll-Overs and dynamic styles */
/*********************************/

// Rollover of box headers
function roll(el, st) {
    if (st) { el.className += " over"; } else { el.className = el.className.replace(/\s?over/, ''); }
}

// Toggle box contents on/off
function onoff(el) {
    // Find body element
    var basename = $(el).id.replace(/_head/, '');
    var body = $(basename + '_body');
    var img = $(basename + '_onoff');

    // Retrieve and set new state
    var st = (body.style.display == 'none');
    body.style.display = (st) ? 'block' : 'none';

    // Update display button
    img.src = (st) ? 'img/onoff-up.gif' : 'img/onoff-down.gif';

    // Toggle uebersicht and legende concurrently
/*
    if (basename == 'uebersicht' && st) {
        $('legende_body').style.display = 'none';
        $('legende_onoff').src = 'img/onoff-down.gif';
    } else if (basename == 'legende' && st) {
        $('uebersicht_body').style.display = 'none';
        $('uebersicht_onoff').src = 'img/onoff-down.gif';
    }
*/
    // Update focus on overview
    if (basename == 'uebersicht' && st) resize_focus();
}

// Set opacity of one object, rollover for moving arrows
function set_op(me, val) {
    hide_level_info();
    $(me).setStyle({ opacity: val });
}

// Set opacity of all arrows
function arrows_opacity(val) {
    $$('.arrows').each(function(el) { el.setStyle({ opacity: val }); });
}

/********************************/
/* Clicking into map and moving */
/********************************/

// Moving one step into the right direction
function fly() {
    // Destination reached?
    if (cur_x == dst_x && cur_y == dst_y) {
        spd_x = 0;
        spd_y = 0;
        update_viewport();
        return;
    }

    if (move_speed_fixed) {
        spd_x = move_speed_fixed * sign(dst_x - cur_x);
        spd_y = move_speed_fixed * sign(dst_y - cur_y);
    } else {
        // Calculate speed
        plus_x = (dst_x - cur_x) / move_divider - spd_x;
        if (Math.abs(plus_x) > move_accel_max && (spd_x == 0 || sign(plus_x) == sign(spd_x))) plus_x = move_accel_max * sign(plus_x);
        spd_x += plus_x;

        plus_y = (dst_y - cur_y) / move_divider - spd_y;
        if (Math.abs(plus_y) > move_accel_max && (spd_y == 0 || sign(plus_y) == sign(spd_y))) plus_y = move_accel_max * sign(plus_y);
        spd_y += plus_y;
    }

    // Set new pos
    if (Math.abs(spd_x) < 3) {
        dst_x = cur_x;
    } else {
        cur_x += spd_x;
    }

    if (Math.abs(spd_y) < 3) {
        dst_y = cur_y;
    } else {
        cur_y += spd_y;
    }


    //cur_x = (Math.abs(spd_x) < 3) ? cur_x : cur_x + spd_x;
    //cur_y = (Math.abs(spd_y) < 3) ? cur_y : cur_y + spd_y;
    $('tiles').setStyle({ left: cur_x + 'px', top: cur_y + 'px'});
    update_focus();

    // Re-trigger
    window.setTimeout('fly()', move_delay);
}

// Move by a click, jump = true jumps instantly, while all other values fly using fly();
function mv(x, y, jump) {
    hide_level_info();

    // Calculate new destination
    if (move_fixed) {
        dst_x += x * move_fixed;
        dst_y += y * move_fixed;
    } else {
        dst_x += Math.round(x * move_relative * map_w);
        dst_y += Math.round(y * move_relative * map_h);
    }

    // Horizontal checks:
    if (limits.w < map_w) {
        // Map width < Viewport width: No scrolling, center horizontally
        dst_x = Math.round((map_w - limits.r - limits.l) / 2);
        jump = true;
    } else if (-dst_x < limits.l - map_padding) {
        // Destination beyond left border: Go to the left border
        dst_x = -limits.l + map_padding;
    } else if (-dst_x + map_w > limits.r + map_padding) {
        // Destination beyound right border: Go to the right border
        dst_x = -limits.r - map_padding + map_w;
    }

    // Vertical checks:
    if (limits.h < map_h) {
        // Map height < Viewport height: No scrolling, center vertically
        dst_y = Math.round((map_h - limits.b - limits.t) / 2);
        jump = true;
    } else if (-dst_y < limits.t - map_padding) {
        // Destination beyond top border: Go to the left border
        dst_y = -limits.t + map_padding;
    } else if (-dst_y + map_h > limits.b + map_padding) {
        // Destination beyound bottom border: Go to the right border
        dst_y = -limits.b /* - map_padding */ + map_h;
    }

    if (jump) {
        cur_x = dst_x;
        cur_y = dst_y;
        $('tiles').setStyle({ left: cur_x + 'px', top: cur_y + 'px'});
        update_focus();
        update_viewport();
    } else {
        fly();
    }
}

// Jump to a certain point after click on minimap
function jump_to(x, y) {
    var mini_map_dim = $('mini_map').getDimensions();
    dst_x = -Math.round((x - foc_w/2) * limits.w / mini_map_dim.width + limits.l);
    dst_y = -Math.round((y - foc_h/2) * limits.h / mini_map_dim.height + limits.t);
    mv(0, 0, true);
}

// Jump to a certain address using coordinates from the DB and set aiming circle
function jump_address(x, y) {
    // Save coordinates for another jump (after zoom)
    last_jump = { x: x, y: y};

    x -= coords.l;
    y -= coords.t;
    dst_x = -Math.round(x * limits.w / coords.w + limits.l - map_w/2);
    dst_y = -Math.round(y * limits.h / coords.h + limits.t - map_h/2);
    mv(0, 0, true);

    var tx = Math.round(x * limits.w / coords.w + limits.l) - target_offset_x;
    var ty = Math.round(y * limits.h / coords.h + limits.t) - target_offset_y;
    $('target').setStyle({ left: tx +'px', top: ty + 'px', display: 'block' });
}

function center_map() {
    // Calculate center of the map
    var x = coords.l + Math.round((coords.r - coords.l) / 2);
    var y = coords.b + Math.round((coords.t - coords.b) / 2);

    // Go there and remove the target
    jump_address(x, y);
    hide_target();
}

function hide_target() {
    $('target').setStyle({ display: 'none' });
}

// Create image link from global layer and zoom and given x and y
function mk_link(x, y, pg) {
    if (x < 0 || y < 0) return 'img/no.gif';

	var s = (pg) ? map_lvl_name : map_img_name;
	while (s.search(/%l/) != -1) s = s.replace(/%l/, String.fromCharCode(97 + layer));
	while (s.search(/%z/) != -1) s = s.replace(/%z/, String.fromCharCode(97 + zoom));
	while (s.search(/%x/) != -1) s = s.replace(/%x/, hex(x, map_img_x_digits));
	while (s.search(/%y/) != -1) s = s.replace(/%y/, hex(y, map_img_x_digits));

    // Hier prüfen, ob die Datei existiert, ansonsten no.gif rein

	return s;
}

function ajax_get_level() {
    var pos = event_xy(mouse.ev);
    var url = 'get_level.php?l=' + layer + '&k=' + kriterium + '&z=' + zoom + '&tx=' + pos.tile_x + '&ty=' + pos.tile_y + '&x=' + pos.x + '&y=' + pos.y;
    new Ajax.Updater($('level_info'), url);
    show_level_info(mouse.ev);
}

// Moves level_info box to x, y
function show_level_info(ev) {
    if (mouse_mode == 0) return;
    $('level_info').show();
    var l = (Event.pointerX(ev) - Math.round(level_info_width/2));
    if (l + level_info_width + 10 > client_width) l = client_width - level_info_width - 10;
    var t = (Event.pointerY(ev) - 35);
    $('level_info').setStyle({ left: l+'px', top: t+'px' });
}

// Hide level info and reset mouse mode
function hide_level_info() {
    $('level_info').hide();
    mouse_mode = 0;
}

// Retrieve noise level from click event on a tile
function get_level(ev) {
    // Hide if visible
    if ($('level_info').visible()) {
        hide_level_info();
        return;
    }

    // Show if hidden
    mouse_mode = 1;
    $('level_info').innerHTML = '';
    mouse.ev = ev;
    ajax_get_level();
}

// Check what to do if the mouse was moved
function mouse_move(ev) {
    switch (mouse_mode) {
        case 0:         // No action
            return;

        case 1:         // Level info follows mouse
            //ev.type = '';
            mouse.ev = ev;
            if (mouse.timer) window.clearTimeout(mouse.timer);
            mouse.timer = window.setTimeout('ajax_get_level()', 200);
            show_level_info(ev);
            $('level_info').update('Beurteilungspegel: ...');
            return;
    }
}

/*******************/
/* Init and resize */
/*******************/

// Init the map
function map_init() {
    update_limits();
    fix_map_size();
    arrows_opacity(op_low);
    set_zoom(zoom);
    center_map();
    init_layer();
    mv(0, 0, true);

    // Observe click on overview map and move accordingly
    $('mini_map').observe('click', function(ev) {
        var pos = event_xy(ev);
        jump_to(pos.x, pos.y);
    });

    // Observer click on overview rectangle and move
    $('focus').observe('click', function(ev) {
        var pos = event_xy(ev);
        var offset = Position.positionedOffset($('focus'));
        jump_to(pos.x + offset[0], pos.y + offset[1]);
    });

    $('level_info').hide();
    $('level_info').setStyle({ width: level_info_width+'px' });

    $('target').observe('click', function(event) { get_level(event); });
    $('target').observe('mousemove', function (event) { mouse_move(event); });
}

// Fixing map size and navi size according to layout, limits hardcoded for the time being
function fix_map_size() {
    // Read client width and height
    client_width = window.innerWidth;
    if (typeof(client_width) != 'number')  client_width = document.documentElement.clientWidth

    // MSIE 6 fix to retrieve screen size by finding the right bottom transparent image
    if (typeof(client_width) != 'number' || client_width == 0) {
        var pos = Position.cumulativeOffset($('rightbottom'));
        client_width = pos[0] + 10;
        client_height = pos[1] + 15;
    } else {
        client_height = window.innerHeight;
        if (typeof(client_height) != 'number') client_height = document.documentElement.clientHeight;
    }


    map_w = (client_width - map_margin_x);    if (map_w < 700) map_w = 700;
    map_h = (client_height - map_margin_y);   if (map_h < 300) map_h = 300;

    $('karte').setStyle({ width: map_w + 'px', height: map_h + 'px' });
    $('navi').setStyle({ width: map_w + 'px' });
    resize_focus();
    mv(0, 0);
    var tp = parseInt($('karte').getStyle('top'));
    var lf = parseInt($('karte').getStyle('left'));
    viewport_pix = { l: lf, t: tp, r: lf + map_w, b: tp + map_h };
}

function resize_focus() {
    // Size
    var mini_map_dim = $('mini_map').getDimensions();
    foc_w = Math.round(map_w * mini_map_dim.width / limits.w);
    foc_h = Math.round(map_h * mini_map_dim.height / limits.h);
    $('focus').setStyle({ width: (foc_w+2) + 'px', height: (foc_h+2) + 'px' });
    $('focus2').setStyle({ width: foc_w + 'px', height: foc_h + 'px' });
    $('focus3').setStyle({ width: (foc_w-2) + 'px', height: (foc_h-2) + 'px' });

    // Visibility
    if (foc_w >= mini_map_dim.width && foc_h >= mini_map_dim.height) { $('focus').hide(); } else { $('focus').show(); }

    var mini_map_pos = Position.positionedOffset($('mini_map'));
    foc_offs_x = mini_map_pos[0];   if (isNaN(foc_offs_x)) foc_offs_x = 0;
    foc_offs_y = mini_map_pos[1];   if (isNaN(foc_offs_y)) foc_offs_y = 0;

    update_focus();
}

// Init layer after szenario, criteria or zoom update
function init_layer() {
    $$('.tile').each(function (el) { el.src='img/no.gif'; el.valid = false; });
    layer = 2 * szenario + kriterium;
    $('mini_map').src = 'img/karte_' + String.fromCharCode(97+layer) + '.gif';

    // Update criteria
    $R(0, 1).each(function (x) {
        $('kriterium_'+x).checked = (x == kriterium);
        //if (x == kriterium) $('legende_'+x).show(); else $('legende_'+x).hide();
    });

    // Update szenario
    $R(0, 4).each(function (x) { $('szenario_'+x).checked  = (x == szenario);  });

    // Delay to give Firefox time to store the el.vali = false
    // Weird, but does not work if called immediately.
    // update_viewport();
    window.setTimeout("update_viewport();", 100);
}

// Add a tile to the container tiles
function set_tile(x, y) {
    // Check if inside limit
    var xp = x * map_tile_size_x;
    var yp = y * map_tile_size_y;
    if (xp > limits.r || yp > limits.b || xp + map_tile_size_x < limits.l || yp + map_tile_size_y < limits.t) return;

	// Check if tile exists and is valid
	var id = 'tile_' + x + '_' + y;
    var type = Object.inspect($(id));

	// New Object needed?
	if (type == 'null' || type == 'undefined') {
        var html = '<img class="tile" id="' + id + '" />';
        new Insertion.Bottom($('tiles'), html);
        $(id).valid = false;
        $(id).observe('click', function(event) { get_level(event); });
        $(id).observe('mousemove', function (event) { mouse_move(event); });
        $(id).setStyle({ left: (x * map_tile_size_x) + 'px', top: (y * map_tile_size_y) + 'px' });
	}

    // If object existed and still valid, do nothing
    if ($(id).valid) return;

    // New or old object must be updated
    $(id).src = mk_link(x, y);
    $(id).valid = true;
}

/****************/
/* Some Helpers */
/****************/

// Convert decimal z to hex with n digits
function hex(z, n) {
	var ziff = '0123456789abcdef', s = '', z;
	var n = Math.pow(16, n-1);
	while (n > 1) { s += ziff.charAt(Math.floor(z/n)); z %= n; n /= 16; }
	s += ziff.charAt(z);
	return s;
}

// Retriving sign of a number
function sign(x) {
    if (x == 0) return 0;
    return (x > 0) ? 1 : -1;
}

// Gets relative clicking values for the object clicked on
function event_xy(ev) {
    //var el = Event.element(ev);
    var el = (ev.target) ? ev.target : Event.element(ev);
    var pos = Position.cumulativeOffset(el);

    if (el.id == 'target') {
        // Mouse over target, find tile underneath
        var found_tile = null;
        var tpos = null;
        $$('.tile').each(function(tile) {
            if (found_tile) return;
            tpos = Position.cumulativeOffset(tile);
            if (tpos[0] <= pos[0] && tpos[0] + map_tile_size_x > pos[0] && tpos[1] <= pos[1] && tpos[1] + map_tile_size_y > pos[1]) found_tile = tile;
        });
        if (found_tile == null) return null;

        // Tile found, calculate position on tile
        pos = tpos;
        el = found_tile;
    }

    // Calculate relative position on map from absolute document position, including browser pixel offset
    var r = { x: Event.pointerX(ev) - pos[0] - pixel_offset, y: Event.pointerY(ev) - pos[1] - pixel_offset }

    // Find Tile coordinates from ID
    var found = el.id.split('_');
    if (found.length == 3) {
        r.tile_x = found[1];
        r.tile_y = found[2];

        // Correct if target overlaps multiple tiles
        if (r.x >= map_tile_size_x) { r.x -= map_tile_size_x; r.tile_x++ }
        if (r.y >= map_tile_size_y) { r.y -= map_tile_size_y; r.tile_y++ }
    }
    return r;
}

/* Noise map specific navigation functions */

// Select new criteria or szenario
function set_kriterium(k) { kriterium = k; init_layer(); }
function set_szenario(s) { szenario = s; init_layer(); }

// Select new zoom
function set_zoom(z) {
    // Update radio button
    $R(0, zoom_max).each(function (x) { $('zoom_'+x).checked = (x == z); });

    // Move to correct position due to the zoom change
    // map_w/2 added before and subtracted after multiplikation to zoom into the center of the map
    var faktor = Math.pow(2, z-zoom);
    dst_x = Math.round((dst_x - map_w/2) * faktor + map_w/2);
    dst_y = Math.round((dst_y - map_h/2) * faktor + map_h/2);
    cur_x = dst_x;
    cur_y = dst_y;
    $('tiles').setStyle({ left: cur_x + 'px', top: cur_y + 'px'});

    var tx = (parseInt($('target').style.left) + target_offset_x) * faktor - target_offset_x;
    var ty = (parseInt($('target').style.top) + target_offset_y) * faktor - target_offset_y;
    if ($('target').style.display == 'block') $('target').setStyle({ left: tx + 'px', top: ty + 'px' });

    // Do the switch
    zoom = z;
    update_limits();
    resize_focus();
    init_layer();

    if ($('target').style.display == 'block') jump_address(last_jump.x, last_jump.y);
}

// Fill visible part of map with tiles
function update_viewport() {
    var from_x = -dst_x;
    var from_y = -dst_y;
    var until_x = from_x + map_w;
    var until_y = from_y + map_h;

    from_x  = Math.floor(from_x / map_tile_size_x);
    from_y  = Math.floor(from_y / map_tile_size_y);
    until_x = Math.ceil(until_x / map_tile_size_x);
    until_y = Math.ceil(until_y / map_tile_size_y);

    // Fill visible part
    $R(from_y, until_y).each(function (y) {
        $R(from_x, until_x).each(function (x) { set_tile(x, y); });
    });

    // Fill frames surrounding visible part as preload
    $R(from_x - 1, until_x + 1).each(function(x) { set_tile(x, from_y - 1); });     // above
    $R(from_x - 1, until_x + 1).each(function(x) { set_tile(x, until_y + 1); });    // below
    $R(from_y - 1, until_y + 1).each(function(y) { set_tile(from_x - 1, y); });     // left
    $R(from_y - 1, until_y + 1).each(function(y) { set_tile(until_x + 1, y); });    // right

    // Trigger clean viewport
    from_x -= 3;    until_x += 3;
    from_y -= 3;    until_y += 3;
    window.setTimeout('clean_viewport(' + from_x + ', ' + until_x + ', ' + from_y + ', ' + until_y + ')', 500);
}

// Clean viewport, checking for each element if necessary
function clean_viewport(from_x, until_x, from_y, until_y) {
    var found, x, y;
    $$('.tile').each(function (el) {
        found = el.id.split('_');
        if (found[1] < from_x || found[1] > until_x || found[2] < from_y || found[2] > until_y) el.remove();
    });
}

// Calculate map boundaries in pixels for the selected zoom level
function update_limits() {
    limits = {};
    var faktor = Math.pow(2, zoom_max - zoom);
    for (i in zoom_max_limits) limits[i] = Math.round(zoom_max_limits[i] / faktor);
    limits.w = limits.r - limits.l;
    limits.h = limits.b - limits.t;
}

function update_focus() {
    var mini_map_dim = $('mini_map').getDimensions();
    var l = Math.round((-cur_x - limits.l) * mini_map_dim.width / limits.w) + foc_offs_x - 2;
    var t = Math.round((-cur_y - limits.t) * mini_map_dim.height / limits.h) + foc_offs_y - 2;
    $('focus').setStyle({ left: l + 'px', top: t + 'px' });
}

