# -*- coding: utf-8 -*-
try:
    from kodi_six import xbmcaddon, xbmcplugin, xbmc, xbmcgui, xbmcvfs
except ImportError:
    import xbmcaddon, xbmcplugin, xbmc, xbmcgui, xbmcvfs
import os
import math
import socket
import threading
import timeit
import xml.etree.ElementTree as ET
import six

try:
    from urllib.request import urlopen, Request  # Python 3
    from urllib.parse import urlparse
except ImportError:
    from urllib2 import urlopen, Request  # Python 2
    from urlparse import urlparse


class SpeedtestCliServerListError(Exception):
    pass


def distance(origin, destination):
    (lat1, lon1) = origin
    (lat2, lon2) = destination
    radius = 6371  # km
    dlat = math.radians(lat2 - lat1)
    dlon = math.radians(lat2 - lon1)
    a = math.sin(dlat / 2) ** 2 + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.sin(dlon / 2) ** 2
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    return radius * c


def build_request(url, data=None, headers={}):
    headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
    return Request(url, data=data, headers=headers)


def catch_request(request):
    try:
        uh = urlopen(request)
        return uh
    except Exception as e:
        return None


def getConfig():
    request = build_request('http://www.speedtest.net/speedtest-config.php')
    uh = catch_request(request)
    if not uh:
        raise Exception("Could not retrieve speedtest.net configuration.")
    configxml = uh.read()
    uh.close()
    root = ET.fromstring(configxml)
    return {
        'client': root.find('client').attrib,
    }


def closestServers(client, progress_dialog):
    urls = [
        'https://www.speedtest.net/speedtest-servers-static.php',
        'http://c.speedtest.net/speedtest-servers-static.php',
    ]
    servers = {}

    for url in urls:
        try:
            progress_dialog.update(20, "Fetching server list from {}".format(url))
            request = build_request(url)
            uh = catch_request(request)

            if not uh:
                continue

            serversxml = uh.read()
            uh.close()

            root = ET.fromstring(serversxml)

            for server in root.findall('.//server'):
                attrib = server.attrib
                try:
                    lat, lon = float(attrib['lat']), float(attrib['lon'])
                except KeyError:
                    continue

                d = distance(
                    (float(client['lat']), float(client['lon'])),
                    (lat, lon)
                )
                attrib['d'] = d
                if d not in servers:
                    servers[d] = [attrib]
                else:
                    servers[d].append(attrib)
        except Exception as e:
            continue

    if not servers:
        raise SpeedtestCliServerListError("No servers found.")

    sorted_servers = sorted(servers.items(), key=lambda x: x[0])
    closest = []
    for _, server_list in sorted_servers[:5]:
        closest.extend(server_list)
    return closest

def ping_test(url):
    parsed_url = urlparse(url)
    host = parsed_url.hostname
    port = parsed_url.port or 80
    ping_times = []

    for _ in range(4):  # Vamos fazer 4 medições para obter uma média
        start = timeit.default_timer()
        try:
            s = socket.create_connection((host, port), timeout=1)
            s.close()
            ping_time = (timeit.default_timer() - start) * 1000
            ping_times.append(ping_time)
        except Exception:
            ping_times.append(float('inf'))

    return sum(ping_times) / len(ping_times)    


def download_test(url, progress_dialog, threads=4, duration=20):
    url = url.rsplit('/', 1)[0] + '/random7000x7000.jpg'
    url = url.replace('https://', 'http://')
    start = timeit.default_timer()
    total_downloaded = [0]

    def worker():
        while timeit.default_timer() - start < duration:
            try:
                request = build_request(url)
                f = urlopen(request)
                while timeit.default_timer() - start < duration:
                    #chunk = f.read(10240)
                    chunk = f.read(3000)
                    if not chunk:
                        break
                    total_downloaded[0] += len(chunk)
                    speed = (total_downloaded[0] * 8) / (timeit.default_timer() - start) / 1e6
                    progress_dialog.update(
                        int((timeit.default_timer() - start) / duration * 100),
                        "Testando download... Velocidade: {:.2f} Mbps".format(speed)
                    )
            except Exception:
                break

    thread_list = []
    for _ in range(threads):
        t = threading.Thread(target=worker)
        t.start()
        thread_list.append(t)

    for t in thread_list:
        t.join()

    progress_dialog.update(70, "Download test completed.")
    return total_downloaded[0] / duration


def upload_test(url, progress_dialog, size=1000000, threads=4, duration=20):
    url = url.replace('https://', 'http://')
    start = timeit.default_timer()
    total_uploaded = [0]

    def worker():
        data = b'x' * size
        while timeit.default_timer() - start < duration:
            try:
                request = build_request(url, data=data)
                f = urlopen(request)
                f.read()
                total_uploaded[0] += len(data)
                speed = (total_uploaded[0] * 8) / (timeit.default_timer() - start) / 1e6
                progress_dialog.update(
                    int((timeit.default_timer() - start) / duration * 100),
                    "Testando upload... Velocidade: {:.2f} Mbps".format(speed)
                )
            except Exception:
                break

    thread_list = []
    for _ in range(threads):
        t = threading.Thread(target=worker)
        t.start()
        thread_list.append(t)

    for t in thread_list:
        t.join()

    progress_dialog.update(90, "Upload test completed.")
    return total_uploaded[0] / duration


def run_speedtest():
    if six.PY2:
        run = xbmcgui.Dialog().yesno(heading='SPEEDTEST', line1='Deseja testar a velocidade da internet?', nolabel='Nao', yeslabel='Sim')
    else:
        run = xbmcgui.Dialog().yesno(heading='SPEEDTEST', message='Deseja testar a velocidade da internet?', nolabel='Nao', yeslabel='Sim')        
    if run:
        progress_dialog = xbmcgui.DialogProgress()
        progress_dialog.create("Medição de Velocidade", "Inicializando...")

        progress_dialog.update(10, "Fetching configuration...")
        config = getConfig()

        progress_dialog.update(30, "Finding closest servers...")
        servers = closestServers(config['client'], progress_dialog)
        best_server = servers[0]

        progress_dialog.update(40, "Calculando ping...")
        ping = ping_test(best_server['url'])    

        progress_dialog.update(50, "Testing with server: {} at {}".format(best_server['host'], best_server['name']))

        download_speed = download_test(best_server['url'], progress_dialog) * 8 / 1e6  # Convert to Mbps
        progress_dialog.update(80, "Download Speed: {:.2f} Mbps".format(download_speed))

        upload_speed = upload_test(best_server['url'], progress_dialog) * 8 / 1e6  # Convert to Mbps
        progress_dialog.update(100, "Upload Speed: {:.2f} Mbps".format(upload_speed))

        progress_dialog.close()

        # Converta 'lat' e 'lon' para float para garantir que são números
        #ping = float(best_server.get('lat', 0))
        downlaod_text = str('{:.2f}'.format(float(download_speed)))
        upload_text = str('{:.2f}'.format(float(upload_speed)))
        ping_text = str('{:.2f} ms'.format(ping))

        xbmcgui.Dialog().ok("Resultado do Speedtest", "Download: {0} Mbps\nUpload: {1} Mbps\nPing: {2}".format(downlaod_text, upload_text, ping_text))