
import os
from subprocess import PIPE, Popen
import ctypes
import ssl
import urllib2
import datetime
import time
import shutil
import zipfile

LOG_DIR = os.path.join(os.environ.get('PROGRAMDATA', r'C:\ProgramData'), 'SpeedTest')
LOG_FILE = os.path.join(LOG_DIR, 'speedtest_log.txt')

MAX_RETRIES = 3
RETRY_DELAY_SEC = 5


def log(msg):
    ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    line = "%s - %s" % (ts, msg)
    print line
    try:
        if not os.path.exists(LOG_DIR):
            os.makedirs(LOG_DIR)
        f = open(LOG_FILE, 'a')
        f.write(line + "\n")
        f.close()
    except:
        pass


class DownloadFailure(Exception):
    pass


class ExtractFailure(Exception):
    pass


class ExecutionFailure(Exception):
    pass


class disable_file_system_redirection:
    _disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirection
    _revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirection

    def __enter__(self):
        self.old_value = ctypes.c_long()
        self.success = self._disable(ctypes.byref(self.old_value))

    def __exit__(self, type, value, traceback):
        if self.success:
            self._revert(self.old_value)


url = r"https://script-downloads.itarian.com/ookla-speedtest/ookla-speedtest-1.2.0-win64.zip"

Down_path = os.environ['TEMP'] + r"\speedtest_files"
fileName = url.split('/')[-1]
DownTo = os.path.join(Down_path, fileName)


def ecmd(command):
    with disable_file_system_redirection():
        obj = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)
    out, err = obj.communicate()
    ret = obj.returncode
    if ret == 0:
        if out:
            return out.strip()
        else:
            return "couldn't get the speedtest output. returncode: %s" % (ret)
    else:
        if err:
            return err.strip()
        else:
            return "something went wrong while testing internet speed. returncode: %s" % (ret)


def downloadFile(DownTo, fromURL):

    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'}
    context = ssl._create_unverified_context()
    last_error = "unknown error"

    for attempt in range(1, MAX_RETRIES + 1):
        try:
            request = urllib2.Request(fromURL, headers=headers)
            req = urllib2.urlopen(request, context=context, timeout=30)
            with open(DownTo, 'wb') as f:
                while True:
                    chunk = req.read(100 * 1000 * 1000)
                    if chunk:
                        f.write(chunk)
                    else:
                        break
            if os.path.isfile(DownTo) and os.path.getsize(DownTo) > 0:
                return ('%s - %sKB' % (DownTo, os.path.getsize(DownTo) / 1024), None)
            else:
                last_error = "downloaded file missing or empty"
                log("Attempt %s/%s: %s" % (attempt, MAX_RETRIES, last_error))
        except Exception as e:
            last_error = str(e)
            log("Attempt %s/%s: download failed - %s" % (attempt, MAX_RETRIES, last_error))
            if os.path.isfile(DownTo):
                try:
                    os.remove(DownTo)
                except:
                    pass

        if attempt < MAX_RETRIES:
            time.sleep(RETRY_DELAY_SEC)

    return (None, last_error)


def run():
    try:
        if not os.path.exists(Down_path):
            os.makedirs(Down_path)

        with disable_file_system_redirection():
            result, download_error = downloadFile(DownTo, url)

            if not result:
                msg = "FAIL - DOWNLOAD: unable to retrieve %s after %s attempts. Last error: %s" % (url, MAX_RETRIES, download_error)
                log(msg)
                raise DownloadFailure(msg)

            log("Download OK: %s" % result)

            try:
                with zipfile.ZipFile(DownTo, "r") as z:
                    z.extractall(Down_path)
            except (DownloadFailure, ExtractFailure, ExecutionFailure):
                raise
            except Exception as e:
                msg = "FAIL - EXTRACT: could not extract %s - %s" % (DownTo, str(e))
                log(msg)
                raise ExtractFailure(msg)

            speedtest_result = ecmd('"%s\\speedtest.exe" --accept-license --accept-gdpr --progress=no' % (Down_path))
            if speedtest_result and speedtest_result.lower().startswith(("couldn't", "something went wrong")):
                msg = "FAIL - EXECUTION: %s" % speedtest_result
                log(msg)
                raise ExecutionFailure(msg)

            log(speedtest_result)
    finally:
        if os.path.exists(Down_path):
            try:
                shutil.rmtree(Down_path)
            except Exception as e:
                log("Warning: cleanup of %s failed - %s" % (Down_path, str(e)))


run()