from typing import List

import Transaction
from DBConnection import postgresConnection
from Transaction import transaction
import random
import numpy as np
from matplotlib import pyplot as plt


def correctVals(vals: List[int]) -> List[int]:
    y = [[] for _ in range(len(vals))]  # truth labels to be estimated
    for i in range(len(vals)):
        if i == 0:
            if vals[i][1] == 0:
                y[i] = 1000 + vals[i][0] * (-1)
            else:
                y[i] = 1000 + vals[i][0]
        else:
            if vals[i][1] == 0:
                y[i] = y[i - 1] - vals[i][0]
            else:
                y[i] = y[i - 1] + vals[i][0]
    y.insert(0, 1000)  # initial bank value
    return y


def valueGenerator() -> List[List[int]]:
    randomlen = random.randrange(5, 20, 1)
    y = [[] for _ in range(randomlen)]
    for i in range(len(y)):
        r = random.randint(0, 1)
        randomval = random.randrange(5, 701, 5)
        y[i] = [randomval, r]
    return y


# def scheduleGeneratorBank1(values: List) -> List[List[transaction]]:
#     actionlist = [db.sub_val_bank1, db.add_val_bank1]
#     schedule = [[] for _ in range(len(values))]
#     for i in range(len(values)):
#         # r = random.randint(0, 1)
#         schedule[i] = transaction()
#         if i == 0:
#             schedule[0].check = True
#         schedule[i].setAction(actionlist[values[i][1]], values[i])
#     return schedule

def scheduleGeneratorBank1(values: List):
    actionlist = [db.sub_val_bank1, db.add_val_bank1]
    for i in range(len(values)):
        # r = random.randint(0, 1)
        Transaction.schedule.append(transaction(i))
        if i == 0:
            Transaction.schedule[i].check = True
        Transaction.schedule[i].setAction(actionlist[values[i][1]], values[i], Transaction.schedule[i].pos)


def scheduleGeneratorBank2(values: List) -> List[List[transaction]]:
    actionlist = [db.sub_val_bank2, db.add_val_bank2]
    schedule = [[] for _ in range(len(values))]
    for i in range(len(values)):
        # r = random.randint(0, 1)
        schedule[i] = transaction(i)
        schedule[i].setAction(actionlist[values[i][1]], values[i])
    return schedule



# Need to have the correct y-label for the corresponding x-value on the graph
def sortLabels(labels: np.ndarray, keys: np.ndarray):
    k = keys.copy()
    n = len(labels)
    temp = [0] * n

    for i in range(0, n):
        temp[k[i]] = labels[i]

    for i in range(0, n):
        labels[i] = temp[i]
        k[i] = i

# FIRST VERSION
# def runSchedule_withLocking(schedule: List[transaction]):
#     for t in schedule:
#         while not t.check:
#             if not db.locked:
#                 t.start()
#                 db.lock()
#             if not t.isAlive():
#                 t.check = True
#                 db.unlock()
#     for t in schedule:
#         t.join()

def runSchedule_withLocking(schedule: List[transaction]):
    for t in schedule:
        t.start()
    for t in schedule:
        t.join()

def runSchedule_withoutLocking(schedule: List[transaction]):
    for t in schedule:
        t.start()
    for t in schedule:
        t.join()



if __name__ == '__main__':

    ######## CREATE THE GRAPH FOR THE CORRECT STATES #############

    db = postgresConnection()
    db.reset_table()
    values = valueGenerator()
    scheduleGeneratorBank1(values)  # bank 1 consistent
    scheduleBank2 = scheduleGeneratorBank2(values)  # bank 2 inconsistent
    values_cleaned = np.asarray([item[0] for item in values])  # remove second element in every sublist
    truth_labels = np.asarray(correctVals(values))  # get correct values for each transaction step
    valkeys = np.argsort(values_cleaned)  # get index if sorted
    values_cleaned.sort()
    # sortLabels(truth_labels, valkeys)



    ######## START SCHEDULE WITH LOCKING #############

    runSchedule_withLocking(Transaction.schedule)

    ######## START SCHEDULE WITHOUT LOCKING #############

    runSchedule_withoutLocking(scheduleBank2)

    ##############         PLOT         ################

    # sortLabels(db.lockingVals, valkeys)
    # sortLabels(db.noLockingVals, valkeys)
    fig = plt.figure(1)
    xvals = list(range(len(truth_labels)))
    truthplot = plt.plot(xvals, truth_labels, linewidth=2, color='lightgreen', label='Correct expected state')
    lockplot = plt.plot(xvals, db.lockingVals, '--', dashes=(5, 10), color='blue', label='State with locking')
    lockplot = plt.plot(xvals, db.noLockingVals, color='red', label='State without locking')

    plt.xlabel('Step')
    plt.ylabel('Bank value state')
    plt.title('Transaction steps')
    plt.legend(loc='upper left')
    plt.show()

