import datetime
import hashlib
import math
import os
import shutil
import subprocess
import sys
import time
from signal import signal, SIGINT

import numpy as np
from PIL import Image
from flask import Flask, render_template, request, send_file, Response, send_from_directory

from lsfr import LFSR
from Sniffer import Sniffer

date = datetime.datetime.now()

lastPacket = None
lastUsedPacket = None

poly = [255, 158, 124, 90, 78, 56, 52, 5]

lfsrSeed = np.random.randint(2, size=256)
lfsr = LFSR(lfsrSeed, poly)
app = Flask(__name__)
s = Sniffer()


def getLfsrSeed(s: Sniffer):
    hash = hashlib.sha256()
    randomDelay = s.packet_delay * 100000000000000 % 1000000000
    hash.update(bytearray(str(randomDelay * s.last_packet.dBm_AntSignal), 'utf-8'))
    bits = []
    digest = hash.digest()
    for i in range(32):
        byte = digest[i]
        for y in range(8):
            bits.append((byte & (1 << y)) >> y)
    return bits


def getRandom(s: Sniffer, number_of_bits: int):
    global lfsr
    global lastPacket
    hash = hashlib.sha256()
    if (lastPacket is None or s.last_packet != lastPacket) and s.last_packet is not None:
        lfsr.register = getLfsrSeed(s)
        lastPacket = s.last_packet

    hash.update(lfsr.rand(256))
    result = hash.digest()
    bits = number_of_bits
    bits -= 256
    while bits > 0:
        hash.update(lfsr.rand(256))
        result += hash.digest()
        bits -= 256
    return (int.from_bytes(result, byteorder="little") >> abs(bits)).to_bytes(math.ceil(number_of_bits / 8),
                                                                              byteorder="little")

def execute(cmd):
    popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True)
    for stdout_line in iter(popen.stdout.readline, ""):
        yield stdout_line
    popen.stdout.close()
    return_code = popen.wait()
    if return_code:
        raise subprocess.CalledProcessError(return_code, cmd)

@app.route("/getRandom", methods=["POST"])
def randomNumberRequest():
    random = getRandom(s, request.json["numberOfBits"])
    return str(int.from_bytes(random, byteorder="little"))


def numberGenerator(count, size):
    for i in range(count):
        yield str(int.from_bytes(getRandom(s, size), byteorder="little")) + "\n"


@app.route("/downloadRandomNumbers", methods=["GET"])
def downloadRandomNumbers():
    numberSize = int(request.args["numberSize"])
    numberCount = int(request.args["numberCount"])
    return Response(numberGenerator(numberCount, numberSize), mimetype="text/plain",
                    headers={"Content-Disposition": "attachment;filename=numbers.txt"})


@app.route("/")
def hello():
    return render_template("index.html")


@app.route("/getBitmap")
def getBitmap():
    pixels = []
    number = int.from_bytes(getRandom(s, 128 * 128), byteorder="little")
    for i in range(128 * 128):
        pixels.append(np.uint8(((number & 1 << i) >> i) * 255))
    pixels = np.array(pixels).reshape((128, 128))
    pi = Image.fromarray(pixels)
    pi.save("bitmap.png")
    return send_file("bitmap.png", mimetype='image/png')


@app.route("/runSts", methods=["POST"])
def runStsRequest():
    resultsFolder = "results"
    if os.path.exists(resultsFolder):
        shutil.rmtree(resultsFolder)
    bitCount = int(request.args["bitCount"])
    out, err = runSts(bitCount)
    shutil.copytree("sts-2.1.2/experiments/AlgorithmTesting/", resultsFolder)

    results = getResults()
    results["stdout"] = out
    results["stderr"] = err
    return results

@app.route('/resources/<path:path>')
def staticFiles(path):
    return send_from_directory('resources', path)

def getResults():
    if not os.path.isdir("results"):
        return None

    results = {}
    with open("results/finalAnalysisReport.txt") as f:
        results["finalAnalysisReport"] = f.read();
    for file in os.listdir("results"):
        if not os.path.isdir(f"results/{file}"):
            continue
        for result in os.listdir(f"results/{file}"):
            if result == "stats.txt":
                with open(f"results/{file}/{result}") as f:
                    results[file] = f.read()

    return results


def createStsInputFile(randomFilePath):
    file = open("sts.input", "w")
    file.write("0\n")  # input file
    file.write(os.path.abspath(randomFilePath) + "\n")  # file path
    file.write("1\n")  # statistical frequency tests
    file.write("0\n")  # parameter confirmation
    file.write("10\n")  # bitstreams
    file.write("1\n")  # binary file
    file.flush()
    file.close()


def generateRandomBinaryFile(fileName, bitCount):
    chunkSize = 1024
    file = open(fileName, "wb")

    while bitCount > chunkSize:
        bitCount -= chunkSize
        file.write(getRandom(s, chunkSize))
        file.flush()

    if bitCount != 0:
        file.write(getRandom(s, bitCount))
    file.flush()
    file.close()


def runSts(bitCount):
    stsFileName = "sts-input.bin"
    generateRandomBinaryFile(stsFileName, bitCount)

    cmd = ["./assess", str(math.floor((bitCount / 10)))]
    createStsInputFile(stsFileName)
    popen = subprocess.Popen(cmd, stdin=open("sts.input", "r"), stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                             universal_newlines=True, cwd="sts-2.1.2")
    out, err = popen.communicate()
    return out, err


s.stop()