#!/usr/bin/python
# -*- coding: utf-8 -*-
import json
import socket
import subprocess
import time
import traceback
import urllib.request, urllib.parse, urllib.error
from OpenSSL import crypto
from socket import gethostname
import os
import urllib.request, urllib.error, urllib.parse
from passlib.hash import pbkdf2_sha512
from homevee import constants
from homevee.Helper import Logger
from homevee.database import get_database_con, set_server_data, get_server_data

'''Prüft, ob der Nutzer die angegebene Berechtigung besitzt'''
def has_permission(username, permission, db):
    permissions = get_permissions(username, db)
    if permissions is None:
        return False
    else:
        return (permission in permissions or 'admin' in permissions)

def get_permissions(username, db):
    with db:
        cur = db.cursor()
        cur.execute("SELECT PERMISSIONS FROM USERDATA WHERE USERNAME = :user", {'user': username})
        result = cur.fetchone()

        if result is not None:
            permissions = json.loads(result['PERMISSIONS'])['permissions']
            return permissions
        else:
            return None

def save_request_to_db(data, reply, db):
    return

    if 'password' in data:
        del data['password']

    if data['action'] == "arcontrol":
        del data['imagedata']

    with db:
        cur = db.cursor()

        cur.execute("INSERT INTO REQUEST_DATA (RESPONSE) VALUES (:response)",
            {'response': reply})

        request_id = cur.lastrowid

        for key in list(data.keys()):
            cur.execute("INSERT INTO REQUEST_DATA_PARAMS (REQUEST_ID, PARAM_KEY, PARAM_VALUE) VALUES(:id, :key, :value)",
                {'id': request_id, 'key': key, 'value': data[key]})

        cur.close();

        return True

def send_to_client(data, conn, is_http):
    if not is_http:
        Logger.log(("Sent Response: "+data))
    elif is_http:
        data = 'HTTP/1.1 200 OK\nContent-Type: text/html\n'+data+'\n'
        Logger.log(("Sent HTTP-Response: "+data))

    #Prüfen, ob alle Daten gesendet wurden
    len_send = conn.send(bytearray(str.encode(data)))
    #len_send = conn.send(compressed_data)
    Logger.log(("Data: "+str(len(data))+" | Sent: "+str(len_send)))

    #if(len_send is 0):
    #    send_to_client(json.dumps({'status': 'error'}), conn, is_http)

def get_rooms(username, db):
    with db:
        cur = db.cursor()
        cur.execute("SELECT * FROM ROOMS")

        rooms = []
        for data in cur.fetchall():
            if has_permission(username, data['LOCATION'], db):
                rooms.append({'name': data['NAME'], 'location': data['LOCATION'], 'icon': data['ICON']})

        cur.close()

        return {'rooms': rooms}

def parse_http_headers(data):
    if data.startswith("GET"):
        Logger.log("HTTP-GET-Request")
        #request = HTTPRequest(data).path
        #path, param_string = request.split('?', 1)
    elif data.startswith("POST"):
        Logger.log("HTTP-POST-Request")
        lines = data.split('\n')
        param_string = lines[-1]
    else:
        raise AttributeError

    params = {}
    param_fragments = param_string.split('&')

    for param in param_fragments:
        if '=' in param:
            key, value = param.split('=')
            params[key] = value

    return params

def hash_password(password):
    salt = os.urandom(12).hex()

    hashed_pw = pbkdf2_sha512.encrypt(password+salt, rounds=200000)

    #print "Password: "+password
    #print "Salt: "+salt
    #print "Hashed: "+hashed_pw

    return (hashed_pw, salt)

def verify_password(password, salt, hashed_pw):
    return pbkdf2_sha512.verify(password+salt, hashed_pw)

def verify_user(username, password):
    db = get_database_con()

    with db:
        cur = db.cursor()
        cur.execute("SELECT * FROM USERDATA WHERE USERNAME == :user", {'user': username})
        user = cur.fetchone()

        if(user is None):
            return False

        hashed_pw = user['PASSWORD']
        salt = user['PW_SALT']
        verified = verify_password(password, salt, hashed_pw)

        #print "Username: "+username
        #print "Password: " + password
        #print "Salt: " + salt
        #print "Hashed: " + hashed_pw
        #print "Verified: "+str(verified)

        return verified

def remote_control_enabled(db):
    REMOTE_CONTROL_ENABLED = get_server_data("USE_REMOTE_CONTROL", db)
    return REMOTE_CONTROL_ENABLED == "true"

def set_remote_control_enabled(username, enabled, db):
    if(not has_permission(username, "admin", db)):
        return {'result': 'noadmin'}
    set_server_data("USE_REMOTE_CONTROL", enabled, db)
    return {'result': 'ok'}

def save_remote_data(username, remote_id, linked_account, db):
    if(not has_permission(username, "admin", db)):
        return {'result': 'noadmin'}

    set_server_data("REMOTE_ID", remote_id, db)
    set_server_data("REMOTE_LINKED_ACCOUNT", linked_account, db)

    return {'result': 'ok'}

def load_remote_data(username, db):
    if(not has_permission(username, "admin", db)):
        return {'result': 'noadmin'}

    remote_id = get_server_data("REMOTE_ID", db)
    access_token = get_server_data("REMOTE_ACCESS_TOKEN", db)
    linked_account = get_server_data("REMOTE_LINKED_ACCOUNT", db)

    #check if account is still linked

    #load premium membership
    url = "https://cloud.homevee.de/server-api.php?action=getpremiumuntil&remoteid="+remote_id+"&accesstoken="+access_token
    contents = urllib.request.urlopen(url).read()
    data = json.loads(contents)
    is_premium = None

    try:
        is_premium = data['is_premium']
        premium_until = data['premium_until']
    except:
        premium_until = None

    return {'remote_id': remote_id,
            'access_token': access_token,
            'linked_account': linked_account, 'premium_until': premium_until, 'is_premium': is_premium,
            'remote_control_enabled': remote_control_enabled(db)}

def connect_remote_id_with_account(username, account_name, account_secret, db):
    if(not has_permission(username, "admin", db)):
        return {'result': 'noadmin'}

    remote_id = get_server_data("REMOTE_ID", db)
    url = "https://homevee.de/connect-remote-id.php?remote_id="+remote_id+"&account_name="\
          +account_name+"&account_secret="+account_secret
    response = urllib.request.urlopen(url).read()

    response = response.decode('utf-8')

    if response == "ok":
        set_server_data("REMOTE_LINKED_ACCOUNT", account_name, db)

    return {'result': response}

def update_ip_thread():
    last_ip = None

    db = get_database_con()

    while(True):
        remote_id = get_server_data("REMOTE_ID", db)
        access_token = get_server_data("REMOTE_ACCESS_TOKEN", db)
        my_ip = get_my_ip()

        if my_ip != last_ip or my_ip != get_my_ip_from_cloud(remote_id):
            update_ip(my_ip, remote_id, access_token)

        last_ip = my_ip

        #wait some time
        time.sleep(5*60) #5 Minuten

def update_cert_thread():
    db = get_database_con()

    while (True):
        remote_id = get_server_data("REMOTE_ID", db)
        access_token = get_server_data("REMOTE_ACCESS_TOKEN", db)

        # generate_cert

        check_cert(None, remote_id, access_token)

        # wait some time
        time.sleep(12 *60 * 60)  # 12 Stunden

def check_cert(db=None, remote_id=None, access_token=None):
    cert_data = get_local_cert()

    if db is not None:
        remote_id = get_server_data("REMOTE_ID", db)
        access_token = get_server_data("REMOTE_ACCESS_TOKEN", db)

    if cert_data is None:
        generate_cert()

    if cert_data != get_my_cert_from_cloud(remote_id):
        update_cert(cert_data, remote_id, access_token)

def update_ip(ip, remote_id, access_token):

    MAX_RETRIES = 10
    retries = 0

    contents = None

    # update local ip in cloud
    try:
        url = "https://cloud.homevee.de/server-api.php?action=updatelocalip&remoteid=" + remote_id + "&accesstoken=" + access_token + "&localip=" + ip
        #Logger.log(remote_id, access_token, ip)
        while (contents != "ok" and retries < MAX_RETRIES):
            try:
                contents = urllib.request.urlopen(url).read()
                return True
            except:
                traceback.print_exc()
            retries += 1
        return False
    except:
        return False

def update_cert(cert_data, remote_id, access_token):
    Logger.log("updating local cert...")

    cert_data = cert_data.replace("\n", "")
    cert_data = cert_data.replace("-----BEGIN CERTIFICATE-----", "")
    cert_data = cert_data.replace("-----END CERTIFICATE-----", "")

    MAX_RETRIES = 10
    retries = 0
    contents = None

    #print(remote_id, access_token, cert_data)

    # update cert in cloud
    try:
        url = "https://cloud.homevee.de/server-api.php?action=updatecert&remoteid=" + remote_id + "&accesstoken=" + access_token + "&cert=" + urllib.parse.quote(
        cert_data)
        #print(url)

        while (contents != "ok" and retries < MAX_RETRIES):
            try:
                contents = urllib.request.urlopen(url).read()
                Logger.log(contents)
                return True
            except:
                traceback.print_exc()
            retries += 1
        return False
    except:
        return False

def get_my_ip():
    #return "192.168.2.110"

    try:
        cmd = "hostname -i"
        data = subprocess.check_output(cmd, shell=True).decode('utf-8')
        #return "192.168.2.110"
        ip, mac = data.split(" ")
    except:
        ip = socket.gethostbyname(socket.gethostname())

    Logger.log("my ip address: " + ip)

    return ip

def generate_cert():
    # create a key pair
    k = crypto.PKey()
    k.generate_key(crypto.TYPE_RSA, 1024)

    # create a self-signed cert
    cert = crypto.X509()
    cert.get_subject().C = "DE"
    cert.get_subject().ST = "Germany"
    cert.get_subject().L = "Germany"
    cert.get_subject().O = "Homevee"
    cert.get_subject().OU = "Homevee"
    cert.get_subject().CN = gethostname()
    cert.set_serial_number(1000)
    cert.gmtime_adj_notBefore(0)
    cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
    cert.set_issuer(cert.get_subject())
    cert.set_pubkey(k)
    cert.sign(k, 'sha1')

    open(constants.LOCAL_SSL_CERT, "wt").write(
        crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode('utf-8'))
    open(constants.LOCAL_SSL_PRIVKEY, "wt").write(
        crypto.dump_privatekey(crypto.FILETYPE_PEM, k).decode('utf-8'))

def get_local_cert():
    if(os.path.isfile(constants.LOCAL_SSL_CERT)):
        file = open(constants.LOCAL_SSL_CERT, "r")
        content = file.read()
        if content is not None and content != '':
            return content

    return None

def get_my_ip_from_cloud(remote_id):
    try:
        url = "http://cloud.homevee.de/server-api.php?action=getlocalip&remoteid="+remote_id
        contents = urllib.request.urlopen(url).read()

        data = json.loads(contents)

        if 'ip' in data and data['ip'] is not None:
            return data['ip']
        else:
            return None
    except:
        traceback.print_exc()
        return None

def get_my_cert_from_cloud(remote_id):
    try:
        url = "http://cloud.homevee.de/server-api.php?action=getcert&remoteid="+remote_id
        contents = urllib.request.urlopen(url).read()

        data = json.loads(contents)

        if 'cert' in data and data['cert'] is not None:
            return data['cert']
        else:
            return None
    except:
        #traceback.print_exc()
        return None
