/**
 * Package: Maps
 * Author: Pavel Janda (www.jandic.com)
 * 
 * Defines class structures for a map visualisation and administration  
 */ 

var TYPE_MARKER = 'M';
var TYPE_ROUTE = 'R';
var TYPE_JOURNEY = 'J';
var storage = null;
 
Function.prototype.method = function (name, func) {
	this.prototype[name] = func;
	return this;
};

Function.prototype.inherits = function(baseClass) { 
  this.prototype = new baseClass; 
  this.prototype.base = baseClass.prototype; 
  //use own constructor 
  this.prototype.constructor = this.constructor; 
}; 

// class Icon
function Icon(id_icn, image, image_width, image_height, shadow, shadow_width, shadow_height, iconAnchor_x, iconAnchor_y, infoWindowAnchor_x, infoWindowAnchor_y) {
  this.id_icn = id_icn;
  
  this.gIcon = new google.maps.Icon();
  this.gIcon.image = image;
  this.gIcon.iconSize = new google.maps.Size(image_width, image_height);
  this.gIcon.shadow = shadow;
  this.gIcon.shadowSize = new google.maps.Size(shadow_width, shadow_height);
  this.gIcon.iconAnchor = new google.maps.Point(iconAnchor_x, iconAnchor_y);
  this.gIcon.infoWindowAnchor = new google.maps.Point(16, 0); //!!! nefunguje z neznameho duvodu infoWindowAnchor_x, infoWindowAnchor_y);
}

Icon.method('getGIcon', function () {
  return this.gIcon;
});


// class Marker
function Marker(id_mrk, title, date, description, lat, lng, min_zoom, zoom, type, id_icn) {
  this.id_mrk = id_mrk;
  this.title;
  this.date;
  this.description;
  this.lat;
  this.lng;
  this.min_zoom;
  this.zoom;
  this.type;
  this.id_icn;
  this.translatedTitle;
  this.translatedDate;
  this.translatedDescription;
  this._sort;
  this.gmarker = null;
  
  this.update(title, date, description, lat, lng, min_zoom, zoom, type, id_icn);
};

/**
 * Updates all parameters of the Marker.
 */ 
Marker.method('update', function (title, date, description, lat, lng, min_zoom, zoom, type, id_icn) {
  this.title = title;
  this.date = date;
  this.description = description;
  this.lat = lat;
  this.lng = lng;
  this.min_zoom = min_zoom;
  this.zoom = zoom;
  this.type = type;
  this.id_icn = id_icn;
  this.translatedTitle = TranslateText(title, maps_lang);
  this.translatedDate = TranslateText(date, maps_lang);
  this.translatedDescription = TranslateText(description, maps_lang);
  this._sort = this.translatedTitle.toUpperCase();
});

Marker.method('toString', function () {
  return this._sort;
});

/**
 * Returns the Icon object for the marker.
 */ 
Marker.method('getIcon', function () {
  var icon = null;
  for (var i = 0; i < storage.icons.length; i++) {
    if (this.id_icn == storage.icons[i].id_icn) {
      icon = storage.icons[i];
      break;
    }
  }
  return icon;
});

// class Route
function Route(id_rts, title, date, description, color, weight, opacity, points, levels, zoom_factor, num_levels) {
  this.id_rts = id_rts;
  this.title;
  this.date;
  this.description;
  this.color;
  this.weight;
  this.opacity;
  this.points;
  this.levels;
  this.zoom_factor;
  this.num_levels;
  this.translatedTitle;
  this.translatedDate;
  this.translatedDescription;
  this._sort;
  this.gpolyline = null;
  this.inJourney = false;   
  
  this.update(title, date, description, color, weight, opacity, points, levels, zoom_factor, num_levels); 
};

/**
 * Updates all parameters of the Route.
 */ 
Route.method('update', function (title, date, description, color, weight, opacity, points, levels, zoom_factor, num_levels) {
  this.title = title;
  this.date = date;
  this.description = description;
  this.color = color;
  this.weight = weight;
  this.opacity = opacity;
  this.points = points;
  this.levels = levels;
  this.zoom_factor = zoom_factor;
  this.num_levels = num_levels;
  this.translatedTitle = TranslateText(title, maps_lang);
  this.translatedDate = TranslateText(date, maps_lang);
  this.translatedDescription = TranslateText(description, maps_lang);
  this._sort = this.translatedTitle.toUpperCase();
});

Route.method('toString', function () {
  return this._sort;
});


// class Journey
function Journey(id_jrn, title, date, description, routes) {
  this.id_jrn = id_jrn;
  this.title;
  this.date;
  this.routes;
  this.description;
  this.translatedTitle;
  this.translatedDate;
  this.translatedDescription;
  this._sort;

  this.update(title, date, description, routes); 
};

/**
 * Updates all parameters of the Route.
 */ 
Journey.method('update', function (title, date, description, routes) {
  this.title = title;
  this.date = date;
  this.description = description;
  this.routes = routes;
  this.translatedTitle = TranslateText(title, maps_lang);
  this.translatedDate = TranslateText(date, maps_lang);
  this.translatedDescription = TranslateText(description, maps_lang);
  this._sort = this.translatedTitle.toUpperCase();
});

Journey.method('toString', function () {
  return this._sort;
});


// *****************************************************************************
// *****************************************************************************
// class Storage
function Storage() {
  storage = this;
  this.icons;
  this.markers;
  this.routes;
  this.journeys;
  
  this.textSelectPlace = (maps_lang == "en" ? "Select a place..." : "Vyberte místo...");
  this.textSelectPlaceEdit = (maps_lang == "en" ? "Select a place to edit..." : "Vyberte místo k editaci...");

  this.textSelectRoute = (maps_lang == "en" ? "Select a route..." : "Vyberte trasu...");
  this.textSelectRouteEdit = (maps_lang == "en" ? "Select a route to edit..." : "Vyberte trasu k editaci...");

  this.textAllJourneys = (maps_lang == "en" ? "<All>" : "<Vše>");
  this.textUncategorizedJourney = (maps_lang == "en" ? "<Uncategorized>" : "<Nezařazené>");
  this.textSelectJourneyEdit = (maps_lang == "en" ? "Select a journey to edit..." : "Vyberte cestu k editaci...");
}

/**
 * Returns an Icon object identified by id_icn.
 */ 
Storage.method('getIcon', function (id_icn) {
  var icon = null;
  for (var i = 0; i < this.icons.length; i++) {
    if (id_icn == this.icons[i].id_icn) {
      icon = this.icons[i];
      break;
    }
  }
  return icon;
});

/**
 * Returns a GIcon object identified by id_icn.
 */ 
Storage.method('getGIcon', function (id_icn) {
  var icon = G_DEFAULT_ICON;
  for (var i = 0; i < this.icons.length; i++) {
    if (id_icn == this.icons[i].id_icn) {
      icon = this.icons[i].getGIcon();
      break;
    }
  }
  return icon;
});

/**
 * Loads marker icons into the Storage object.
 */ 
Storage.method('initializeIcons', function (data) {
  var xml = GXml.parse(data);
  var icons = xml.documentElement.getElementsByTagName("icon");
  this.icons = [];
  for (var i = 0; i < icons.length; i++) {
    var id = icons[i].getAttribute("id");
    var image = icons[i].getAttribute("image");
    var image_width = icons[i].getAttribute("image_width");
    var image_height = icons[i].getAttribute("image_height");
    var shadow = icons[i].getAttribute("shadow");
    var shadow_width = icons[i].getAttribute("shadow_width");
    var shadow_height = icons[i].getAttribute("shadow_height");
    var iconAnchor_x = icons[i].getAttribute("iconAnchor_x");
    var iconAnchor_y = icons[i].getAttribute("iconAnchor_y");
    var infoWindowAnchor_x = icons[i].getAttribute("infoWindowAnchor_x");
    var infoWindowAnchor_y = icons[i].getAttribute("infoWindowAnchor_y");

    var icon = new Icon(id, image, image_width, image_height, shadow, shadow_width, shadow_height, iconAnchor_x, iconAnchor_y, infoWindowAnchor_x, infoWindowAnchor_y);
    this.icons.push(icon);
  }
});

/**
 * Fill structures by data obtained from database.
 */ 
Storage.method('addMapsData', function (data) {
  var xml = GXml.parse(data);
  // fill markers
  var xmlMarkers = xml.documentElement.getElementsByTagName("marker");
  this.markers = [];
  for (var i = 0; i < xmlMarkers.length; i++) {
    var id_mrk = xmlMarkers[i].getAttribute("id_mrk");
    var title = xmlMarkers[i].getAttribute("title");
    var date = xmlMarkers[i].getAttribute("date");
    var description = xmlMarkers[i].getAttribute("description");
    var lat = xmlMarkers[i].getAttribute("lat");
    var lng = xmlMarkers[i].getAttribute("lng");
    var min_zoom = xmlMarkers[i].getAttribute("min_zoom");
    var zoom = xmlMarkers[i].getAttribute("zoom");
    var type = xmlMarkers[i].getAttribute("type");
    var id_icn = xmlMarkers[i].getAttribute("id_icn");
    
    var marker = new Marker(id_mrk, title, date, description, lat, lng, min_zoom, zoom, type, id_icn);
    this.markers.push(marker);
  }
  this.markers.sort();
  
  // fill routes
  var xmlRoutes = xml.documentElement.getElementsByTagName("route");
  this.routes = [];
  for (var i = 0; i < xmlRoutes.length; i++) {
    var id_rts = xmlRoutes[i].getAttribute("id_rts");
    var title = xmlRoutes[i].getAttribute("title");
    var date = xmlRoutes[i].getAttribute("date");
    var description = xmlRoutes[i].getAttribute("description");
    var color = xmlRoutes[i].getAttribute("color");
    var weight = xmlRoutes[i].getAttribute("weight");
    var opacity = xmlRoutes[i].getAttribute("opacity");
    var points = xmlRoutes[i].getAttribute("points");
    var levels = xmlRoutes[i].getAttribute("levels");
    var zoom_factor = xmlRoutes[i].getAttribute("zoom_factor");
    var num_levels = xmlRoutes[i].getAttribute("num_levels");
    
    var route = new Route(id_rts, title, date, description, color, weight, opacity, points, levels, zoom_factor, num_levels);
    this.routes.push(route);
  }
  this.routes.sort();

  // fill journeys
  var xmlJourneys = xml.documentElement.getElementsByTagName("journey");
  this.journeys = [];
  for (var i = 0; i < xmlJourneys.length; i++) {
    var id_jrn = xmlJourneys[i].getAttribute("id_jrn");
    var title = xmlJourneys[i].getAttribute("title");
    var date = xmlJourneys[i].getAttribute("date");
    var description = xmlJourneys[i].getAttribute("description");
    var routesIdsString = xmlJourneys[i].getAttribute("routes");
    var routesIds = routesIdsString.split(",");

    var routes = [];
    if (routesIdsString != '') {
      for (var j = 0; j < routesIds.length; j++) {
        var route = this.getRoute(routesIds[j]);
        route.inJourney = true;
        routes.push(route);
      }    
      routes.sort();
    }
   
    var journey = new Journey(id_jrn, title, date, description, routes);
    this.journeys.push(journey);
  }
  this.journeys.sort();
});

/**
 * Returns a list of icons.
 */ 
Storage.method('getIcons', function () {
  return this.icons;
});

/**
 * Returns a list of markers.
 */ 
Storage.method('getMarkers', function () {
  return this.markers;
});

/**
 * Sets the list of markers and sort it.
 */ 
Storage.method('setMarkers', function (markers) {
  this.markers = markers;
  this.markers.sort();
});

/**
 * Adds a new marker to the list.
 */ 
Storage.method('addMarker', function (marker) {
  this.markers.push(marker);
  this.markers.sort();
});

/**
 * Deletes an existing marker from the list.
 */ 
Storage.method('deleteMarker', function (id_mrk) {
  var index = this.getMarkerIndex(id_mrk);
  if (index != -1) {
    this.markers.splice(index, 1);
  }
});

/**
 * Returns a marker object identified by id_mrk.
 */ 
Storage.method('getMarker', function (id_mrk) {
  var marker = null;
  for (var i = 0; i < this.markers.length; i++) {
    if (id_mrk == this.markers[i].id_mrk) {
      marker = this.markers[i];
      break;
    }
  }
  return marker;
});

/**
 * Returns an index of the marker identified by id_mrk.
 */ 
Storage.method('getMarkerIndex', function (id_mrk) {
  var markerIndex = -1;
  for (var i = 0; i < this.markers.length; i++) {
    if (id_mrk == this.markers[i].id_mrk) {
      markerIndex = i;
      break;
    }
  }
  return markerIndex;
});

/**
 * Fills given list by all marker names and their indexes.
 * Parameter: id = 'all' - list will contain all markers including line 'Select place...'
 *                 'edit' - list will contain all markers including line 'Select place to edit...'
 */ 
Storage.method('fillMarkerList', function (listObj, id) {
  listObj.options.length = 0;
  
  if (id == 'all') {
    listObj.options[0] = new Option(this.textSelectPlace, 0);
  } else {
    listObj.options[0] = new Option(this.textSelectPlaceEdit, 0);
  }

  var j = 1;
  for (var i = 0; i < this.markers.length; i++) {
    if (id == 'all' || this.markers[i].type == TYPE_MARKER) {
      listObj.options[j] = new Option(this.markers[i].translatedTitle, this.markers[i].id_mrk);
      j++;
    }
  }
});


/**
 * Returns a list of routes.
 */ 
Storage.method('getRoutes', function () {
  return this.routes;
});

/**
 * Sets the list of routes and sort it.
 */ 
Storage.method('setRoutes', function (routes) {
  this.routes = routes;
  this.routes.sort();  
});

/**
 * Adds a new route to the list.
 */ 
Storage.method('addRoute', function (route) {
  this.routes.push(route);
  this.routes.sort();
});

/**
 * Deletes an existing route from the list.
 */ 
Storage.method('deleteRoute', function (id_rts) {
  var index = this.getRouteIndex(id_rts);
  if (index != -1) {
    this.routes.splice(index, 1);
  }
  
  for (var i = 0; i < this.journeys.length; i++) {
    for (var j = 0; j < this.journeys[i].routes.length; j++) {
      if (id_rts == this.journeys[i].routes[j].id_rts) {
        this.journeys[i].routes.splice(j, 1);
        break;
      }
    }
  }
});

/**
 * Returns the route object identified by id_rts.
 */ 
Storage.method('getRoute', function (id_rts) {
  var route = null;
  for (var i = 0; i < this.routes.length; i++) {
    if (id_rts == this.routes[i].id_rts) {
      route = this.routes[i];
      break;
    }
  }
  return route;
});

/**
 * Returns an index of the route identified by id_rts.
 */ 
Storage.method('getRouteIndex', function (id_rts) {
  var routeIndex = -1;
  for (var i = 0; i < this.routes.length; i++) {
    if (id_rts == this.routes[i].id_rts) {
      routeIndex = i;
      break;
    }
  }
  return routeIndex;
});


/**
 * Fills given list by specified route names and their indexes.
 * Parameter: id_jrn = '0' - list will contain all routes including the line 'Select route...'
 *                     '-1' - list will contain all routes not included in any journey including the line 'Select route...' 
 *                     number - list will contain only routes for given journey including the line 'Select route...' 
 *                     'edit' - list will contain all routes including line 'Select place to edit...'
 */ 
Storage.method('fillRouteList', function (listObj, id_jrn) {
  listObj.options.length = 0;
  
  if (id_jrn == 'edit')
    listObj.options[0] = new Option(this.textSelectRouteEdit, 0);
  else
    listObj.options[0] = new Option(this.textSelectRoute, 0);

  var j = 1;

  if (id_jrn == 0 || id_jrn == -1 || id_jrn == 'edit') {
    // go through all records
    for (var i = 0; i < this.routes.length; i++) {
      if (id_jrn == 0 || id_jrn == 'edit' || this.routes[i].inJourney == false) {
        listObj.options[j] = new Option(this.routes[i].translatedTitle, this.routes[i].id_rts);
        j++;
      }
    }
  } else {
    // just selected routes
    var journey = this.getJourney(id_jrn);
    // get routes
    for (var i = 0; i < journey.routes.length; i++) {
      listObj.options[j] = new Option(journey.routes[i].translatedTitle, journey.routes[i].id_rts);
      j++;
    }      
  }
});

/**
 * Returns a list of journeys.
 */ 
Storage.method('getJourneys', function () {
  return this.journeys;
});

/**
 * Sets the list of journeys and sort it.
 */ 
Storage.method('setJourneys', function (journeys) {
  this.journeys = journeys;
  this.journeys.sort();  
});

/**
 * Adds a new journey to the list.
 */ 
Storage.method('addJourney', function (journey) {
  this.journeys.push(journey);
  this.journeys.sort();
  
  var index = this.getJourneyIndex(journey.id_jrn);
  if (index != -1) {
    // lock the routes
    for (var i = 0; i < this.journeys[index].routes.length; i++) { 
      var route = this.getRoute(this.journeys[index].routes[i].id_rts);
      route.inJourney = true;
    }
  }
});

/**
 * Deletes an existing journey from the list.
 */ 
Storage.method('deleteJourney', function (id_jrn) {
  var index = this.getJourneyIndex(id_jrn);
  if (index != -1) {
    // release routes
    for (var i = 0; i < this.journeys[index].routes.length; i++) { 
      var route = this.getRoute(this.journeys[index].routes[i].id_rts);
      route.inJourney = false;
    }

    // delete journey
    this.journeys.splice(index, 1);
  }
});

/**
 * Returns journey object identified by id_jrn.
 */ 
Storage.method('getJourney', function (id_jrn) {
  var journey = null;
  for (var i = 0; i < this.journeys.length; i++) {
    if (id_jrn == this.journeys[i].id_jrn) {
      journey = this.journeys[i];
      break;
    }
  }
  return journey;
});

/**
 * Returns an index of the journey identified by id_jrn.
 */ 
Storage.method('getJourneyIndex', function (id_jrn) {
  var JourneyIndex = -1;
  for (var i = 0; i < this.journeys.length; i++) {
    if (id_jrn == this.journeys[i].id_jrn) {
      journeyIndex = i;
      break;
    }
  }
  return journeyIndex;
});

/**
 * Fills given list by all journey names and their indexes.
 * Parameter: id = 'all' - list will contain all journeys including lines '<All>' and '<Uncategorized>'
 *                 'edit' - list will contain all journeys including line 'Select journey to edit...'
 */ 
Storage.method('fillJourneyList', function (listObj, id) {
  listObj.options.length = 0;

  var j = 1;
  if (id == 'all') {
    listObj.options[0] = new Option(this.textAllJourneys, 0);
    listObj.options[1] = new Option(this.textUncategorizedJourney, -1);
    j = 2;
  } else {
    listObj.options[0] = new Option(this.textSelectJourneyEdit, 0);
  }

  for (var i = 0; i < this.journeys.length; i++) {
    if (this.journeys[i].translatedDate != "")
      listObj.options[j] = new Option(this.journeys[i].translatedTitle + ' - ' + this.journeys[i].translatedDate, this.journeys[i].id_jrn);
    else
      listObj.options[j] = new Option(this.journeys[i].translatedTitle, this.journeys[i].id_jrn);

    j++;
  }
});

