#!/usr/bin/python3
# Copyright 2014 Serhiy Zahoriya

# This file is part of replay.org.ua-OpenStreetMap-sync.

# replay.org.ua-OpenStreetMap-sync
# is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# replay.org.ua-OpenStreetMap-sync
# is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License along with
# replay.org.ua-OpenStreetMap-sync
# If not, see <http://www.gnu.org/licenses/>.

''''''

import logging
import gzip
from sys import stdout
from datetime import datetime
from pprint import PrettyPrinter
from configparser import RawConfigParser

import mysql.connector
from haversine import haversine
from dictdiffer import diff

from constants import TARGET, DIFF_REPR, LOG_DIR, CRED_INI,\
	SHOW_LINK_TEMPLATE, UNUSED_TAGS, NEARBY_DISTANCE, MUTABLE_TARGETS

pformat = PrettyPrinter(indent=2).pformat

logger = logging.getLogger('')
logformatter = logging.Formatter('%(levelname)s: %(message)s')
loghandler = logging.StreamHandler(stdout)
loghandler.setFormatter(logformatter)
logger.addHandler(loghandler)
logger.setLevel(logging.DEBUG)
log = logger

if TARGET in MUTABLE_TARGETS:
	exec_formatter = logging.Formatter('%(asctime)s %(message)s')
	exec_logger = logging.getLogger('CUD_log')
	log_filename = LOG_DIR + datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
	try:
		gz_filehandler = gzip.open(log_filename, mode='wt', encoding='utf-8')
		exec_filehandler = logging.StreamHandler(gz_filehandler)
	except:
		exec_filehandler = logging.FileHandler(log_filename)
	exec_filehandler.setFormatter(exec_formatter)
	exec_logger.addHandler(exec_filehandler)
	exec_log = exec_logger.warning
else:
	exec_log = logging.debug


def replay_db():
	config = RawConfigParser()
	config.read(CRED_INI)
	return mysql.connector.connect(
		user=config['db'][TARGET + '_user'],
		password=config['db'][TARGET + '_pass'],
		host=config['db'][TARGET + '_host'],
		database=config['db'][TARGET + '_db'])


def ask_user(
	prompt="{:s}",
	options=list(),
	default=''):

	keys = set()
	if options:
		options_string = ""
		for option in options:
			k, rest = (option[0], option[1:])
			keys.add(k)
			if k == default:
				k = k.upper()
			options_string += '[' + k + ']' + rest + ' '
		if len(keys) < len(options):
			log.error("There are non-unique keys in options.")
	else:
		options_string = "Press Enter to continue or stop the script."
	reply = None
	while reply not in keys | {''}:
		reply = input(prompt.format(options_string) + "\n").lower().strip()
		if reply:
			reply = reply[0]
	return reply if reply else default


def show_link(elem):
	template = SHOW_LINK_TEMPLATE
	print(template.format(float(elem['lat']), float(elem['lon'])))


def diff_tags(point, elem, ignored_tags):
	differences = list()
	diff_iter = diff(point['tags'], elem['tags'], ignore=ignored_tags)
	for changed in diff_iter:
		differences.append(changed)
	return differences


def show_difference(difference):
	print(
		DIFF_REPR[difference[0]],
		difference[1], '\t',
		pformat(difference[2:]))


def haversine_distance(point, elem):
	return haversine((
		float(point['lat']),
		float(point['lon'])
	), (
		float(elem['lat']),
		float(elem['lon'])
	)) * 1000.0


def warning(message):
	log.warning(message)
	ask_user()


def find_nearest_elem(point, osm, ignored_ids=list()):
	nearest_elem_id = None
	differences = None
	nearest_elem_dist = NEARBY_DISTANCE

	action = None
	options = ['yes', 'next', 'create']

	for e_id in filter(lambda x: x not in ignored_ids, osm):
		elem = osm[e_id]
		distance = haversine_distance(point, elem)
		if (distance < nearest_elem_dist):
			nearest_elem_id = e_id
			nearest_elem_dist = distance
	if nearest_elem_id:
		print("Distance {:d}m:".format(int(nearest_elem_dist)))
		show_link(point)
		differences = diff_tags(point, osm[nearest_elem_id], UNUSED_TAGS)

		for difference in differences:
			show_difference(difference)
		if osm[nearest_elem_id]['tags'].get(
			'recycling_type', None) == 'container':
			print("NB: recycling_type=container")
		action = ask_user("Same point? {:s}", options, 'y')
		if action == 'n':
			ignored_ids.append(nearest_elem_id)
			next_nearest = find_nearest_elem(point, osm, ignored_ids)
			if not next_nearest[0]:
				options.pop(options.index('next'))
				reply = ask_user(
					"No more points. Use the last one?\n{:s}",
					options, 'c')
				if reply == 'c':
					return None, None
			else:
				return next_nearest
		if action == 'c':
			return None, None

	return nearest_elem_id, differences
