import io
import ecdsa
import hashlib
import yaml
import base64
import sys

class ErrorHandlingDict(dict):
    def __init__(self, filename, path):
        self._filename = filename
        self._path = path
        dict.__init__({})
    def __missing__(self, key):
        if self._path:
            path = ", ".join(self._path)
        else:
            path = "the top level"
        print >>sys.stderr, "error: could not find configuration key '%s' at %s in %s" % (key, path, self._filename)
        sys.exit(1)

def errorhandlify(term, filename, path=[]):
    if isinstance(term, basestring):
        return term
    elif isinstance(term, int):
        return term
    elif isinstance(term, dict):
        result = ErrorHandlingDict(filename, path)
        for k, v in term.items():
            result[k] = errorhandlify(v, filename, path + [k])
        return result
    elif isinstance(term, list):
        return [errorhandlify(e, filename, path + ["item %d" % i]) for i, e in enumerate(term, start=1)]
    else:
        print "unknown type", type(term)
        sys.exit(1)

def verify_config(rawconfig, signature, publickey_base64, filename):
    publickey = base64.decodestring(publickey_base64)

    try:
        vk = ecdsa.VerifyingKey.from_der(publickey)
        vk.verify(signature, rawconfig, hashfunc=hashlib.sha256,
                sigdecode=ecdsa.util.sigdecode_der)
    except ecdsa.keys.BadSignatureError:
        print >>sys.stderr, "error: configuration file %s did not have a correct signature" % (filename,)
        sys.exit(1)

    return errorhandlify(yaml.load(io.BytesIO(rawconfig), yaml.SafeLoader), filename)

def verify_and_read_config(filename, publickey_base64):
    rawconfig = open(filename).read()
    signature = open(filename + ".sig").read()

    verify_config(rawconfig, signature, publickey_base64, filename)

    return errorhandlify(yaml.load(io.BytesIO(rawconfig), yaml.SafeLoader), filename)

def read_config(filename):
    return errorhandlify(yaml.load(open(filename), yaml.SafeLoader), filename)