#!/usr/bin/env python3
from bottle import route, run, request, HTTPResponse, response
import hashlib
import json
import pdb
import os

updated = True
filename = "firmware.bin"

@route('/update')
def updateRequest():
    #for header in request.headers.keys():
    #    print("{}: {}".format(header, request.headers.get(header, "")))

    if not "X-Esp8266-Sta-Mac" in request.headers:
        return "Hello fellow friend! Looking for updates?"

    readConfig()
    global config
    requestor_mac = request.headers.get("X-Esp8266-Sta-Mac", "")
    running_fw_md5 = request.headers.get("X-Esp8266-Sketch-Md5", "")
    role, version, fw_filename, fw_hash = getProperties(config, requestor_mac)
    if role == "" and version == "" and fw_filename == "" and fw_hash == "":
        print("No configuration for {} found. Serving 304 - No Firmware".format(requestor_mac))
        return nofirmware()

    print("Update request from {} with role {} (running fw hash: {}).".format(requestor_mac, role, running_fw_md5))
    fw_path = os.path.join("firmwares", role, version, fw_filename)
    try:
        latest_fw_md5 = md5(fw_path)
    except FileNotFoundError:
        print("Configured firmware not found at {}".format(fw_path))
        return nofirmware()

    if latest_fw_md5 != fw_hash:
        print("Configured hash ({}) does not match calculeted one ({})".format(fw_hash, latest_fw_md5))
        return nofirmware()

    print("Latest firmware for role \"{}\" is \"{}\" ({}) - ".format(role, fw_filename, latest_fw_md5), end="")
    if latest_fw_md5 == running_fw_md5:
        print("Same, skipping…")
        return nofirmware()

    print("Differs, updating…")
    response.set_header('Content-Type', 'application/octet-stream')
    response.set_header('Content-Disposition', ' attachment; filename={}'.format(filename))
    with open(fw_path, "rb") as fw:
        firmware = fw.read()
        response.set_header('Content-Length', '{}'.format(len(firmware)))
    response.set_header('X-MD5', latest_fw_md5)
    print("Serving {} bytes to {} with role {}".format(len(firmware), requestor_mac, role))
    return firmware

def getProperties(config, mac):
    configured_devices = config.get("devices", {}).keys()
    devices = config.get("devices", {})
    firmwares = config.get("firmwares", {})
    match = devices.get(mac, {"role":"", "version":""})
    role = match.get("role", "")
    version = match.get("version", "")
    role_firmwares = firmwares.get(role, {})
    firmware = role_firmwares.get(version, {})
    firmware_filename = firmware.get("filename", "")
    firmware_hash = firmware.get("hash", "")
    return role, version, firmware_filename, firmware_hash

def nofirmware():
    return HTTPResponse(status=304, body="")

def md5(fname):
    hash_md5 = hashlib.md5()
    with open(fname, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

def readConfig(config_name="config.json"):
    global config
    config = ""
    with open(config_name, "r") as conf:
        config = json.loads(conf.read())

readConfig()
run(host='localhost', port=8080, debug=True)