﻿
function Pos(latitude, longitude) {
	this.lat = latitude;
	this.lon = longitude;
}

Pos.prototype = {

	equals: function(p) {
		return this.lat == p.lat && this.lon == p.lon;
	},

	clone: function() {
		return new Pos(this.lat, this.lon);
	},

	distance: function(p) {
		var dy = this.lat - p.lat;
		var dx = Math.cos(this.lat / 20626480.62) * (this.lon - p.lon);
		return Math.sqrt(dx * dx + dy * dy);
	},

	toString: function() {
		return $.map([{coor:this.lat,dir:'N',rev:'S'},{coor:this.lon,dir:'E',rev:'W'}], function(c){
			var s = '" ' + (c.coor < 0 ? c.rev : c.dir);
			var coor = Math.abs(c.coor)
			$.each([{div:100,sep:'.'},{div:60,sep:"'"},{div:60,sep:'°'}], function(i, f) {
				var n = coor % f.div;
				s = f.sep + (n < 10 ? '0' : '') + n + s;
				coor = Math.floor(coor / f.div);
			});
			return coor + s;
		}).join(', ');
	},

	toLatLng: function() {
		return new google.maps.LatLng(this.lat / 360000.0, this.lon / 360000.0);
	}

}
	
function area(places) {
	var min, max, first = true;
	$.each(places.items, function(){
		if (first) {
			min = this.pos.clone();
			max = this.pos.clone();
			first = false;
		} else {
			if (this.pos.lat < min.lat) min.lat = this.pos.lat;
			if (this.pos.lat > max.lat) max.lat = this.pos.lat;
			if (this.pos.lon < min.lon) min.lon = this.pos.lon;
			if (this.pos.lon > max.lon) max.lon = this.pos.lon;
		}
	});
	return min.distance(max);
}

function Photo(prop) {
	this.id = prop[0];
	this.pos = new Pos(prop[1], prop[2]);
	this.vertical = prop[3] === 1;
	this.rep = prop[4];
	this.year = prop[5] + 1980;
	this.info = null;
}

function Place(pos) {
	this.pos = pos;
	this.images = [];
}

Place.prototype = {

	getBestRep: function() {
		var best = null;
		$.each(this.images, function(){
			if (best == null || this.rep > best.rep) best = this;
		});
		return best;
	},

	imageCount: function() {
		return this.images.length;
	}

}

function PlaceList(places) {
	this.items = places;
}

PlaceList.prototype = {

	getPlace: function(pos) {
		var place = null;
		for (var i = 0; i < this.items.length; i++) {
			if (pos.equals(this.items[i].pos)) {
				return this.items[i];
			}
		}
		place = new Place(pos);
		this.items.push(place);
		return place;
	},

	count: function() {
		return this.items.length;
	},

	merge: function(list) {
		return new PlaceList(this.items.concat(list.items));
	},

	getCenter: function() {
		var lat = 0, lon = 0, cnt = 0;
		$.each(this.items, function(){
			var c = this.imageCount();
			lat += this.pos.lat * c;
			lon += this.pos.lon * c;
			cnt += c;
		});
		return new Pos(Math.round(lat / cnt), Math.round(lon / cnt));
	}

}

function Group(pos, places, cnt, best) {
	this.pos = pos;
	this.places = places;
	this.cnt = cnt;
	this.best = best;
}

Group.prototype = {

	merge: function(g2) {
		var p = this.places.merge(g2.places);
		return new Group(p.getCenter(), p, this.cnt + g2.cnt, this.best.rep > g2.best.rep ? this.best : g2.best);
	}

}

function GroupList(places) {
	this.items = $.map(places.items, function(p){
		return new Group(p.pos, new PlaceList([p]), p.images.length, p.getBestRep());
	});
}

GroupList.prototype = {

	count: function() {
		return this.items.length;
	},

	merge: function(dist) {
		var g = this.items[dist.idx1].merge(this.items[dist.idx2]);
		this.items[dist.idx1] = null;
		this.items[dist.idx2] = null;
		return g;
	},

	getClosest: function(start, pos, idx, maxDistance) {
		var closest = maxDistance, cidx;
		for (var i = start; i < this.items.length; i++) {
			if (this.items[i] != null) {
				var d = pos.distance(this.items[i].pos);
				if (d < closest) {
					closest = d;
					cidx = i;
				}
			}
		}
		return closest < maxDistance ? new Distance(closest, idx, cidx) : null;
	},

	getDistances: function(maxDistance) {
		var distances = new DistanceList();
		var groups = this;
		$.each(this.items, function(i){
			distances.addClosest(groups, i + 1, this.pos, i, maxDistance);
		});
		return distances;
	}

}

function Distance(dist, idx1, idx2) {
	this.dist = dist;
	this.idx1 = idx1;
	this.idx2 = idx2;
}

function DistanceList() {
	this.items = [];
}

DistanceList.prototype = {

	count: function() {
		return this.items.length;
	},

	addClosest: function(groups, start, pos, idx, maxDistance) {
		var dist = groups.getClosest(start, pos, idx, maxDistance);
		if (dist != null) this.items.push(dist);
	},

	getShortest: function() {
		var closest = 1e8, idx, dist;
		$.each(this.items, function(i) {
			if (this.dist < closest) {
				closest = this.dist;
				idx = i;
				dist = this;
			}
		});
		this.items.splice(idx, 1);
		return dist;
	},

	removeConnected: function(dist) {
		this.items = $.grep(this.items, function(d2){ return d2.idx1 == dist.idx1 || d2.idx1 == dist.idx2 || d2.idx2 == dist.idx1 || d2.idx2 == dist.idx2; }, true);
	}

}

