import os
import io
import socket
import json
import requests
import xbmc
import xbmcgui

from PIL import Image
from pathlib import Path
from requests.adapters import HTTPAdapter, Retry

from . import config
from . import utils
from . import weather

# DNS cache
old_getaddrinfo = socket.getaddrinfo

def new_getaddrinfo(*args):
	try:
		return config.dnscache[args]
	except KeyError:
		r = old_getaddrinfo(*args)
		config.dnscache[args] = r
		return r

socket.getaddrinfo = new_getaddrinfo

# Requests
r = Retry(total=2, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
s = requests.Session()
s.headers.update(config.addon_ua)
s.mount('https://', HTTPAdapter(max_retries=r))

# Network
def network():
	if config.neterr > 10:
		return False
	else:
		return True

# Get url
def geturl(url, head=False):

	# Network timeout
	if network():
		timeout = 6
	else:
		timeout = 1

	# Download
	try:
		if head:
			utils.log(f'Checking: {url}', 3)
			r = s.head(url, timeout=timeout)
		else:
			utils.log(f'Download: {url}', 3)
			r = s.get(url, timeout=timeout)

	except Exception as e:
		utils.log(f'Download: {url} ({e})', 3)
		config.dnscache = {}
		config.neterr  += 1
		return None

	else:
		config.neterr = 0

		if r.ok:
			utils.log(f'Download: {url} ({r.status_code})', 3)
			return r.content
		else:
			utils.log(f'Download: {url} ({r.status_code})', 2)
			config.dnscache = {}
			return None

# Get data
def getdata(type, loc, map=None):

	# URL
	if type == 'weather':
		url = config.map_api.get(type).format(map[0], map[1])
	elif type == 'airquality':
		url = config.map_api.get(type).format(map[0], map[1])
	elif type == 'sun':
		url = config.map_api.get(type).format(map[0], map[1], map[2])
	elif type == 'moon':
		url = config.map_api.get(type).format(map[0], map[1], map[2])

	# Weather
	file = f'{config.addon_cache}/{loc}/{type}.json'
	data = geturl(url)

	if data:
		with open(Path(file), 'wb') as f:
			f.write(data)

# Get Map ( 0:loc, 1:type, 2:count, 3:z, 4:x, 5:y, 6:xtile 7:ytile, 8:path, 9:time, 10-13 bbox )
def getmap(map, head=False):

	if map[1] == 'osm':
		url = config.map_api.get(map[1]).format(map[3], map[4], map[5])
	elif map[1] == 'rvradar':
		url = config.map_api.get(map[1]).format(map[8], map[3], map[4], map[5])
	elif map[1] == 'rvsatellite':
		url = config.map_api.get(map[1]).format(map[8], map[3], map[4], map[5])
	elif map[1] == 'gctemp':
		url = config.map_api.get(map[1]).format(map[10], map[11], map[12], map[13])
	elif map[1] == 'gcwind':
		url = config.map_api.get(map[1]).format(map[10], map[11], map[12], map[13])

	# HEAD
	if head:
		data = geturl(url, head=True)
		return data

	# GET
	data = geturl(url)

	if data:
		config.mapcache[map[1]][map[2]] = data
	else:
		with open(Path(f'{config.addon_path}/resources/tile.png'), 'rb') as f:
			tile = f.read()

		config.mapcache[map[1]][map[2]] = tile

# Map merge
def mapmerge(map):
	image = Image.new("RGBA", (756, 756), None)

	for item in map:

		try:
			tile = Image.open(io.BytesIO(config.mapcache[item[1]][item[2]]))
		except:
			tile = Image.open(f'{config.addon_path}/resources/tile.png')
		else:
			image.paste( tile, (item[6], item[7]))

	if map[0][1] == 'osm':
		image.save(f'{config.addon_cache}/{map[0][0]}/{map[0][1]}.png')
	else:
		image.save(f'{config.addon_cache}/{map[0][0]}/{map[0][1]}_{map[0][9]}.png')

# Get rvdata
def getrvindex(type):
	try:
		data = json.loads(geturl(config.map_api.get('rvindex')))
		map  = config.map_layers.get(type)
		time = data[map[0]][map[1]][-1]['time']
		path = data[map[0]][map[1]][-1]['path']
	except:
		return None, None
	else:
		return time, path

# Get location (GeoIP)
def getloc(locid):
	utils.log(f'Geolocation ...')
	utils.setsetting('geoip', 'true')

	# Get location
	try:
		data    = json.loads(geturl(config.map_api.get('geoip')))
		city    = data['city']
		region  = data.get('region_name')
		country = data.get('country_code')

		# Search
		data     = json.loads(geturl(config.map_api.get('search').format(city)))
		location = data['results'][0]

		for item in data['results']:

			if country and region:
				if country in location['country_code'] and region in location['admin1']:
					location = item
					break

			if country:
				if country in location['country_code']:
					location = item
					break
	except Exception as e:
		utils.log(f'Geolocation: Unknown ({e})')
	else:
		utils.log(f'Geolocation: {location["name"]}, {location["admin1"]}, {location["country_code"]} [{location["latitude"]}, {location["longitude"]}]')
		utils.setsetting(f'loc{locid}', f'{location["name"]}, {location["admin1"]}, {location["country_code"]}')
		utils.setsetting(f'loc{locid}lat', str(location["latitude"]))
		utils.setsetting(f'loc{locid}lon', str(location["longitude"]))
		utils.setsetting(f'loc{locid}tz', str(location["timezone"]))

# Clear location
def clearloc(locid, last=False):
	if last:
		utils.setsetting(f'loc{locid}data', '321318000')
		utils.setsetting(f'loc{locid}map', '321318000')
		utils.setsetting(f'loc{locid}rv', '321318000')
		utils.setsetting(f'loc{locid}gc', '321318000')
	else:
		utils.setsetting(f'loc{locid}', '')
		utils.setsetting(f'loc{locid}user', '')
		utils.setsetting(f'loc{locid}alert', 'true')
		utils.setsetting(f'loc{locid}utz', 'false')
		utils.setsetting(f'loc{locid}tz', '')
		utils.setsetting(f'loc{locid}lat', '0')
		utils.setsetting(f'loc{locid}lon', '0')

# Set location
def setloc (locid):
	utils.log(f'Search dialog ...')

	dialog   = xbmcgui.Dialog()
	input    = utils.setting(f'loc{locid}')
	keyboard = xbmc.Keyboard(input, utils.loc(14024), False)
	keyboard.doModal()

	if keyboard.isConfirmed():
		search = keyboard.getText()

		# No changes
		if search == input:
			utils.log(f'[LOC{locid}] No changes')

		# Remove location
		elif search == '':
			check = utils.setting(f'loc{int(locid)+1}')

			if not check:
				utils.log(f'[LOC{locid}] Removed')
				clearloc(locid)
				clearloc(locid, True)

		# Search location
		else:
			try:
				locs   = []
				url    = config.map_api.get('search').format(search)
				data   = json.loads(geturl(url))['results']
			except:
				utils.log('[LOC{locid}] No results')
				dialog.ok('Open-meteo', utils.loc(284))
			else:
				for item in data:
					li = xbmcgui.ListItem(f'{item.get("name")}, {item.get("admin1")}, {item.get("country_code")} (Lat: {item.get("latitude")}, Lon: {item.get("longitude")})')
					locs.append(li)

				select = dialog.select(utils.loc(396), locs, useDetails=True)

				if select != -1:

					# Cleanup cache dir
					dir   = f'{config.addon_cache}/{locid}'
					files = sorted(list(Path(dir).glob('*')))

					for file in files:
						os.remove(file)

					# Set location
					utils.log(f'Location {locid}: {data[select].get("name")}, {data[select].get("admin1")}, {data[select].get("country_code")} {data[select].get("latitude")} {data[select].get("longitude")}')
					utils.setsetting(f'loc{locid}', f'{data[select].get("name")}, {data[select].get("admin1")}, {data[select].get("country_code")}')
					utils.setsetting(f'loc{locid}lat', data[select]["latitude"])
					utils.setsetting(f'loc{locid}lon', data[select]["longitude"])
					utils.setsetting(f'loc{locid}tz', data[select]["timezone"])

					# Wait for settings dialog
					while xbmcgui.getCurrentWindowDialogId() == 10140:
						utils.log(f'Waiting for settings dialog ...')
						utils.monitor.waitForAbort(1)

						if utils.monitor.abortRequested():
							return

					# Cleanup lastupdate
					clearloc(locid, True)

					# Refresh
					if int(utils.settingrpc("weather.currentlocation")) == int(locid):
						weather.Main(str(locid), mode='download')
						weather.Main(str(locid), mode='update')
					else:
						weather.Main(str(locid), mode='download')
						weather.Main(str(locid), mode='updatelocs')

