#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2014, NORDUnet A/S.
# See LICENSE for licensing information.

import json
import base64
import urllib
import urllib2
import sys
import time
from certtools import build_merkle_tree, create_sth_signature, check_sth_signature

ctbaseurl = "https://127.0.0.1:8080/"
frontendnodes = ["https://127.0.0.1:8082/"]
storagenodes = ["https://127.0.0.1:8081/"]

chainsdir = "../rel/mergedb/chains"
logorderfile = "../rel/mergedb/logorder"

def parselogrow(row):
    return base64.b16decode(row)

def get_logorder():
    f = open(logorderfile, "r")
    return [parselogrow(row.rstrip()) for row in f]

def write_chain(key, value):
    f = open(chainsdir + "/" + base64.b16encode(key), "w")
    f.write(value)
    f.close()

def read_chain(key):
    f = open(chainsdir + "/" + base64.b16encode(key), "r")
    value = f.read()
    f.close()
    return value

def add_to_logorder(key):
    f = open(logorderfile, "a")
    f.write(base64.b16encode(key) + "\n")
    f.close()

def get_new_entries(baseurl):
    try:
        result = urllib2.urlopen(baseurl + "ct/storage/fetchnewentries").read()
        parsed_result = json.loads(result)
        if parsed_result.get(u"result") == u"ok":
            return parsed_result[u"entries"]
        print "ERROR: fetchnewentries", parsed_result
        sys.exit(1)
    except urllib2.HTTPError, e:
        print "ERROR: fetchnewentries", e.read()
        sys.exit(1)

def get_curpos(baseurl):
    try:
        result = urllib2.urlopen(baseurl + "ct/frontend/currentposition").read()
        parsed_result = json.loads(result)
        if parsed_result.get(u"result") == u"ok":
            return parsed_result[u"position"]
        print "ERROR: currentposition", parsed_result
        sys.exit(1)
    except urllib2.HTTPError, e:
        print "ERROR: currentposition", e.read()
        sys.exit(1)

def sendlog(baseurl, submission):
    try:
        result = urllib2.urlopen(baseurl + "ct/frontend/sendlog",
            json.dumps(submission)).read()
        return json.loads(result)
    except urllib2.HTTPError, e:
        print "ERROR: sendlog", e.read()
        sys.exit(1)
    except ValueError, e:
        print "==== FAILED REQUEST ===="
        print submission
        print "======= RESPONSE ======="
        print result
        print "========================"
        raise e

def sendentry(baseurl, entry, hash):
    try:
        result = urllib2.urlopen(baseurl + "ct/frontend/sendentry",
            json.dumps({"entry":base64.b64encode(entry), "treeleafhash":base64.b64encode(hash)})).read()
        return json.loads(result)
    except urllib2.HTTPError, e:
        print "ERROR: sendentry", e.read()
        sys.exit(1)
    except ValueError, e:
        print "==== FAILED REQUEST ===="
        print hash
        print "======= RESPONSE ======="
        print result
        print "========================"
        raise e

def sendsth(baseurl, submission):
    try:
        result = urllib2.urlopen(baseurl + "ct/frontend/sendsth",
            json.dumps(submission)).read()
        return json.loads(result)
    except urllib2.HTTPError, e:
        print "ERROR: sendsth", e.read()
        sys.exit(1)
    except ValueError, e:
        print "==== FAILED REQUEST ===="
        print submission
        print "======= RESPONSE ======="
        print result
        print "========================"
        raise e

def get_missingentries(baseurl):
    try:
        result = urllib2.urlopen(baseurl + "ct/frontend/missingentries").read()
        parsed_result = json.loads(result)
        if parsed_result.get(u"result") == u"ok":
            return parsed_result[u"entries"]
        print "ERROR: missingentries", parsed_result
        sys.exit(1)
    except urllib2.HTTPError, e:
        print "ERROR: missingentries", e.read()
        sys.exit(1)


logorder = get_logorder()
certsinlog = set(logorder)

new_entries = [entry for storagenode in storagenodes for entry in get_new_entries(storagenode)]

print "adding entries"
added_entries = 0
for new_entry in new_entries:
    hash = base64.b64decode(new_entry["hash"])
    entry = base64.b64decode(new_entry["entry"])
    if hash not in certsinlog:
        write_chain(hash, entry)
        add_to_logorder(hash)
        logorder.append(hash)
        certsinlog.add(hash)
        added_entries += 1
print "added", added_entries, "entries"

tree = build_merkle_tree(logorder)
tree_size = len(logorder)
root_hash = tree[-1][0]
timestamp = int(time.time() * 1000)
privatekey = base64.decodestring(
    "MHcCAQEEIMM/FjZ4FSzfENTTwGpTve6CP+IVr"
    "Y7p8OKV634uJI/foAoGCCqGSM49AwEHoUQDQg"
    "AE4qWq6afhBUi0OdcWUYhyJLNXTkGqQ9PMS5l"
    "qoCgkV2h1ZvpNjBH2u8UbgcOQwqDo66z6BWQJ"
    "GolozZYmNHE2kQ==")

tree_head_signature = create_sth_signature(tree_size, timestamp,
                                           root_hash, privatekey)

sth = {"tree_size": tree_size, "timestamp": timestamp,
       "sha256_root_hash": base64.b64encode(root_hash),
       "tree_head_signature": base64.b64encode(tree_head_signature)}

check_sth_signature(ctbaseurl, sth)

print "root hash", base64.b16encode(root_hash)

for frontendnode in frontendnodes:
    print "distributing for node", frontendnode
    curpos = get_curpos(frontendnode)
    print "current position", curpos
    entries = [base64.b64encode(entry) for entry in logorder[curpos:]]
    sendlog(frontendnode, {"start": curpos, "hashes": entries})
    print "log sent"
    missingentries = get_missingentries(frontendnode)
    print "missing entries:", missingentries
    for missingentry in missingentries:
        hash = base64.b64decode(missingentry)
        sendentry(frontendnode, read_chain(hash), hash)
    sendsth(frontendnode, sth)