﻿
function createLocationBox() {
    var _this = new Object();
    _this.lat = null;
    _this.lon = null;
    _this.locationChangeEventHandlers = [];
    // delegate :: LocationBox -> {lat,long} -> void
    _this.AddLocationChangeEventListener = function ( delegate ) {
        _this.locationChangeEventHandlers.push( delegate )
    };
    _this.OnLocationChange = function() {
        for(var i = 0; i < _this.locationChangeEventHandlers.length; i++) {
            _this.locationChangeEventHandlers[i].call( null , _this, {lat: _this.lat, lon: _this.lon});
        }
    };
    _this.SetLocation = function( _lat , _lon ) {
        _this.lat = _lat;
        _this.lon = _lon;
        _this.OnLocationChange();
    };
    return _this;
}

function createInput(parent) {
    var res = document.createElement('input');
    res.setAttribute('type','text');
    res.className = 'editboxShort';
    parent.appendChild(res);
    return res;
}

function createCountryDropDown(parent, data) {
    var res = document.createElement('select');

    $.each(data, function(index, optionData) {
        var option = new Option(optionData.Name, optionData.Capital + ', ' + optionData.Name);
        if ($.browser.msie) {
            res.add(option);
        }
        else {
            res.add(option, null);
        }
    });
    
    res.className = 'editboxShort';
    parent.appendChild(res);
    return res;
}

function createDmsBox( locBox , canvas ) {    
    var tab = canvas;
    
    var latDiv = document.createElement('div');
    latDiv.style.padding = '1px';
    
    var latLabel = document.createElement('span');
    latLabel.className = 'label';
    latLabel.style.paddingRight = '20px';
    latLabel.innerHTML = 'Lat:';
    latDiv.appendChild(latLabel);
    
    tab.edLatDegrees = createInput(latDiv);
    tab.edLatMinutes = createInput(latDiv);
    tab.edLatSeconds = createInput(latDiv);          
    
    var longDiv = document.createElement('div');     
    var longLabel = document.createElement('span');
    longLabel.className = 'label';
    longLabel.style.paddingRight = '12px';
    longLabel.innerHTML = 'Long:';
    longDiv.appendChild(longLabel);

    tab.edLongDegrees = createInput(longDiv);
    tab.edLongMinutes = createInput(longDiv);
    tab.edLongSeconds = createInput(longDiv);    
    
    tab.appendChild(latDiv);
    tab.appendChild(longDiv);
    
    tab.SubmitValues = function(){
        // TODO Validation
            
        var dmsLat = {degrees: parseFloat(DefaultIfEmpty(tab.edLatDegrees.value,'0')), 
                      minutes: parseFloat(DefaultIfEmpty(tab.edLatMinutes.value,'0')), 
                      seconds: parseFloat(DefaultIfEmpty(tab.edLatSeconds.value,'0'))};
        var dmsLon = {degrees: parseFloat(DefaultIfEmpty(tab.edLongDegrees.value,'0')), 
                      minutes: parseFloat(DefaultIfEmpty(tab.edLongMinutes.value,'0')), 
                      seconds: parseFloat(DefaultIfEmpty(tab.edLongSeconds.value,'0'))};
        var dmsCoords = {lat: dmsLat, lon: dmsLon};
        
        var decimalCoords = DmsToDecimal( dmsCoords );

        if (!isValidDecimalLat(decimalCoords.lat)) {
            alert("Invalid Decimal Latitude entered");
            return;
        }

        if (!isValidDecimalLong(decimalCoords.lon)) {
            alert("Invalid Decimal Longitude entered");
            return;
        }
        
        locBox.SetLocation( decimalCoords.lat, decimalCoords.lon );
    };

    locBox.AddLocationChangeEventListener( function( lb , point ) {
        pointDms = DecimalToDms( point );

        tab.edLatDegrees.value = pointDms.lat.degrees;
        tab.edLatMinutes.value = pointDms.lat.minutes;
        tab.edLatSeconds.value = pointDms.lat.seconds;
        
        tab.edLongDegrees.value = pointDms.lon.degrees;
        tab.edLongMinutes.value = pointDms.lon.minutes;
        tab.edLongSeconds.value = pointDms.lon.seconds;
    });
    
    tab.edLatDegrees.onblur = tab.SubmitValues;
    tab.edLatMinutes.onblur = tab.SubmitValues;
    tab.edLatSeconds.onblur = tab.SubmitValues;
    tab.edLongDegrees.onblur = tab.SubmitValues;
    tab.edLongMinutes.onblur = tab.SubmitValues;
    tab.edLongSeconds.onblur = tab.SubmitValues;
}

function createDecimalBox( locBox , canvas ) {

    var tab = canvas;
    
    var latLongDiv = document.createElement('div');
    
    var latLabel = document.createElement('span');
    latLabel.className = 'label';
    latLabel.innerHTML = 'Lat:';
    latLongDiv.appendChild(latLabel);
    
    tab.edDecimalLat = createInput(latLongDiv);
    
    var longLabel = document.createElement('span');
    longLabel.className = 'label';
    longLabel.innerHTML = 'Long:';
    latLongDiv.appendChild(longLabel);

    tab.edDecimalLong = createInput(latLongDiv);
    
    tab.appendChild(latLongDiv);

    tab.SubmitValues = function() {
        var decimalLat = DefaultIfEmpty(tab.edDecimalLat.value, '0');
        var decimalLong = DefaultIfEmpty(tab.edDecimalLong.value, '0');

        if (!isValidDecimalLat(decimalLat)) {
            alert("Invalid Decimal Latitude entered");
            return;
        }

        if (!isValidDecimalLong(decimalLong)) {
            alert("Invalid Decimal Longitude entered");
            return;
        }

        locBox.SetLocation(parseFloat(decimalLat), parseFloat(decimalLong));
    }
    
    locBox.AddLocationChangeEventListener( function( lb , point ) {
        tab.edDecimalLat.value = point.lat;
        tab.edDecimalLong.value = point.lon;
    });
        
    tab.edDecimalLat.onblur = tab.SubmitValues;
    tab.edDecimalLong.onblur = tab.SubmitValues;
}

function createCountryBox(locBox, canvas, countriesJson) {

    var tab = canvas;

    var countryDiv = document.createElement('div');

    var countryLabel = document.createElement('span');
    countryLabel.className = 'label';
    countryLabel.innerHTML = 'Country: ';
    countryDiv.appendChild(countryLabel);

    tab.edCountry = createCountryDropDown(countryDiv, countriesJson);
    
    var searchButton = document.createElement('input');
    searchButton.setAttribute('type', 'button');
    searchButton.setAttribute('value', 'Find');
    searchButton.setAttribute('class', 'btn_text green');
    countryDiv.appendChild(searchButton);

    tab.resultMessage = document.createElement('span');
    countryDiv.appendChild(tab.resultMessage);
    tab.resultMessage.className = 'label';
    tab.resultMessage.style.display = 'none';

    tab.appendChild(countryDiv);

    // event handlers
    tab.LatLongCallback = function(point) {
        //console.log(point);

        if (point != null) {
            locBox.SetLocation(point.y, point.x);

            tab.resultMessage.style.display = "block";
            tab.resultMessage.style.color = "green";
            tab.resultMessage.innerHTML = "Coordinates retrieval from address was successfull.";
        }
        else {
            tab.resultMessage.style.display = "block";
            tab.resultMessage.style.color = "red";
            tab.resultMessage.innerHTML = "Couldn't translate address to coordinates, please try again with a different address.";
        }
    };
    tab.edCountry.onkeypress = function(e) {
        tab.resultMessage.style.display = "none";
        var charPressed;
        if (window.event)
            charPressed = window.event.keyCode;
        else
            charPressed = e.which;
        if (charPressed == 13) { // Enter is pressed?
            tab.Country2LatLong();
            return false;
        }
    }
    tab.Country2LatLong = function() {
        var geocoder = new GClientGeocoder();
        var elem = tab.edCountry;
        countryValue = elem.value;
        tab.mapLoaded = false;
        geocoder.getLatLng(countryValue, tab.LatLongCallback);
    };

    searchButton.onclick = tab.Country2LatLong;
}

function createAddressBox( locBox , canvas , styles ) {
    var tab = canvas;
        
    var addressDiv = document.createElement('div');
    
    var addressLabel = document.createElement('span');
    addressLabel.className = 'label';
    addressLabel.innerHTML = 'Address: ';
    addressDiv.appendChild(addressLabel);

    tab.edAddress = createInput(addressDiv);
    tab.edAddress.style.width = styles.addressBox.width;

    var searchButton = document.createElement('input');        
    searchButton.setAttribute('type', 'button');
    searchButton.setAttribute('value', 'Find');
    searchButton.setAttribute('class', 'btn_text green');
    addressDiv.appendChild(searchButton);
    
    tab.resultMessage = document.createElement('span');
    addressDiv.appendChild(tab.resultMessage);
    tab.resultMessage.className = 'label';
    tab.resultMessage.style.display = 'none';

    tab.appendChild(addressDiv);
    
    // event handlers
    tab.LatLongCallback = function(point) {
        //console.log(point);
    
        if (point != null)
        {
            locBox.SetLocation( point.y, point.x );

            tab.resultMessage.style.display = "block";
            tab.resultMessage.style.color = "green";
            tab.resultMessage.innerHTML = "Coordinates retrieval from address was successfull.";
        }
        else
        {
            tab.resultMessage.style.display = "block";
            tab.resultMessage.style.color = "red";
            tab.resultMessage.innerHTML = "Couldn't translate address to coordinates, please try again with a different address.";
        }    
    };
    tab.edAddress.onkeypress = function(e) {
    	tab.resultMessage.style.display = "none";
    	var charPressed;
    	if (window.event)
    		charPressed = window.event.keyCode;
    	else
    		charPressed = e.which;
    	if (charPressed == 13) { // Enter is pressed?
    		tab.PostalAddress2LatLong();
    		return false;
    	}
    }
    tab.PostalAddress2LatLong = function() {
        var geocoder = new GClientGeocoder();
        var elem = tab.edAddress;
        postalAddressValue = elem.value;
        tab.mapLoaded = false;
        geocoder.getLatLng(postalAddressValue, tab.LatLongCallback);        
    }; 

    searchButton.onclick = tab.PostalAddress2LatLong;
}

function DefaultIfEmpty(source, def) {
    // TODO trim spaces
    if (source == null || source.length == 0) {
        return def;
    } else {
        return source;
    }
}

// Geo Utils

function Sign(a) {
    if(a < 0)
    {
        return -1;
    }
    else
    {
        return 1;
    }
}

function isValidDecimalLat(lat)
{
    if (isNaN(lat) || lat < -90 || lat > 90)
    {
        return false;
    }
    return true;
}

function isValidDecimalLong(lon)
{
    if (isNaN(lon) || lon < -180 || lon > 180)
    {
        return false;
    }
    return true;
}

// DecimalToDms :: {lat:{degrees,minutes,seconds},lon:{degrees,minutes,seconds}} -> {lat,lon}
function DmsToDecimal( dmsPoint )
{
    var result = new Object();
    result.lat = Angle_DmsToDecimal(dmsPoint.lat);
    result.lon = Angle_DmsToDecimal(dmsPoint.lon);
    return result;
}

// Angle_DmsToDecimal :: {degrees,minutes,seconds} -> float
function Angle_DmsToDecimal( coord )
{
    var sign = Sign(coord.degrees);
    var degrees = Math.abs( Math.round(coord.degrees * 1000000.));
    var minutes = Math.abs( Math.round(coord.minutes * 1000000.));
    var seconds = Math.abs( Math.round(coord.seconds * 1000000.));
    return Math.round(degrees + (minutes/60.) + (seconds/3600.) ) * sign/1000000;
}

// DecimalToDms :: {lat,lon} -> {lat:{degrees,minutes,seconds},lon:{degrees,minutes,seconds}} 
function DecimalToDms( point ) 
{
    var result = new Object();
    result.lat = Angle_DecimalToDms(point.lat);
    result.lon = Angle_DecimalToDms(point.lon);
    return result;
}

// Angle_DecimalToDms :: float -> {degrees,minutes,seconds}
function Angle_DecimalToDms(coord)
{
    var result = new Object();
    var sign = Sign(coord);    
    coord = Math.abs( Math.round(coord * 1000000.))
    result.degrees = (Math.floor(coord / 1000000) * sign);
    result.minutes = Math.floor(  ((coord/1000000) - Math.floor(coord/1000000)) * 60);
    result.seconds = ( Math.floor(((((coord/1000000) - Math.floor(coord/1000000)) * 60) - Math.floor(((coord/1000000) - Math.floor(coord/1000000)) * 60)) * 100000) *60/100000 );
    return result;
}