#!/usr/bin/env python
# -*- coding: utf-8 -*-

from os.path import join

import sys
import logging
import tarfile
from io import BytesIO
from base64 import b64decode 
from hashlib import md5
from datetime import datetime
import win32api
import wmi
import netifaces

from twisted.internet import reactor

from .network import change_ip_windows
from context import BaseContext
from windows.winutils import (
    is_frozen_exe, copy_running_exe, 
    update_service_binpath, servicePostUpdate
)

try:
    # Python 2: "unicode" is built-in
    unicode
except NameError:
    unicode = str
    
logger = logging.getLogger(__name__)


class Context(BaseContext):
    service_name = "CIRCLE-agent"
    working_dir = r"C:\circle" 
    exe = "circle-agent.exe"

    @staticmethod
    def postUpdate():
        exe_path = join(Context.working_dir, Context.exe)
        return servicePostUpdate(Context.service_name, exe_path)
            
    @staticmethod
    def change_password(password):
        Context.placed = True
        from win32com import adsi
        ads_obj = adsi.ADsGetObject('WinNT://localhost/%s,user' % 'cloud')
        ads_obj.Getinfo()
        ads_obj.SetPassword(password)

    @staticmethod
    def restart_networking():
        pass

    @staticmethod
    def change_ip(interfaces, dns):
        Context.placed = True
        nameservers = dns.replace(' ', '').split(',')
        change_ip_windows(interfaces, nameservers)

    @staticmethod
    def set_time(time):
        t = datetime.utcfromtimestamp(float(time))
        win32api.SetSystemTime(t.year, t.month, 0, t.day, t.hour,
                               t.minute, t.second, 0)

    @staticmethod
    def set_hostname(hostname):
        wmi.WMI().Win32_ComputerSystem()[0].Rename(hostname)

    @staticmethod
    def mount_store(host, username, password):
        import notify
        url = 'cifs://%s:%s@%s/%s' % (username, password, host, username)
        for c in notify.clients:
            logger.debug("sending url %s to client %s", url, unicode(c))
            c.sendLine(url.encode())

    @staticmethod
    def get_keys():
        pass

    @staticmethod
    def add_keys(keys):
        pass

    @staticmethod
    def del_keys(keys):
        pass

    @staticmethod
    def cleanup():
        # TODO
        pass

    @staticmethod
    def start_access_server():
        # TODO
        pass

    @staticmethod
    def append(data, filename, chunk_number, uuid):
        if chunk_number == 0:
            flag = "w"
        else:
            flag = "a"
        with open(filename, flag) as myfile:
            myfile.write(data)

    @staticmethod
    def update(filename, executable, checksum, uuid):
        with open(filename, "r") as f:
            data = f.read()
            logger.debug("type(data) %s" % type(data))
            local_checksum = md5(data.encode()).hexdigest()
            if local_checksum != checksum:
                raise Exception("Checksum missmatch the file is damaged.")
            decoded = BytesIO(b64decode(data))
        try:
            tar = tarfile.TarFile.open("dummy", fileobj=decoded, mode='r|gz')
            tar.extractall(Context.working_dir)
        except tarfile.ReadError as e:
            logger.error(e)
        logger.info("Transfer completed!")
        old_exe = update_service_binpath("CIRCLE-agent", join(Context.working_dir, executable))
        logger.info('%s Updated', old_exe)
        Context.exit_code = 1
        reactor.callLater(0, reactor.stop)

    @staticmethod
    def ipaddresses():
        args = {}
        interfaces = netifaces.interfaces()
        for i in interfaces:
            if i == 'lo':
                continue
            args[i] = []
            addresses = netifaces.ifaddresses(i)
            args[i] = ([x['addr']
                        for x in addresses.get(netifaces.AF_INET, [])] +
                       [x['addr']
                        for x in addresses.get(netifaces.AF_INET6, [])
                        if '%' not in x['addr']])
        return args

    @staticmethod
    def get_agent_version():
        try:
            with open(join(Context.working_dir, 'version.txt')) as f:
                return f.readline()
        except IOError:
            return None
