from typing import List

# import data dari file data_contoh.py
from data_contoh import *

def _pm(i, iterasi):
    for col in S[i]:
        precedence[iterasi][col] = 1
        if col in S:
            _pm(col, iterasi)


# membuat precedence matrix
def generate_pm():
    for i in key:
        _pm(i, i)
    precedence[0][1:] = 1


def _calculate_ranked_position_method(durasi):
    a, b, c, d = durasi
    up = c**2 + d**2 - (a**2 + b**2) + (c * d) - (a * b)
    down = 3 * ((c + d) - (a + b))
    return round(up/down, 2)


# menghitung ranked position matrix
def _ranked_position_method(*durasi):
    to_count = [0, 0, 0, 0]
    for dur in durasi:
        for x in dur:
            to_count[0] += x[0]
            to_count[1] += x[1]
            to_count[2] += x[2]
            to_count[3] += x[3]
    return _calculate_ranked_position_method(to_count)


# menghitung weight
def count_weight(data):
    # generate precedence matrix
    generate_pm()

    weights: List[int] = []
    to_count = []
    for row in range(1, len(data)+1):
        tc = []
        counter = 0
        for col in range(1, len(data)+1):
            if precedence[row][col]:
                tc.append(data[counter])
            counter += 1
        tc.insert(0, data[row-1])
        to_count.append(tc)

    for i in to_count:
        ui = _ranked_position_method(i)
        weights.append(ui)
    weights.insert(0, weights[0])
    weights.append(0)
    return weights


def _knapsack(W: int, wt: List, val: List, n: int) -> List:
    K = [[0 for x in range(W + 1)] for x in range(n + 1)]

    for i in range(n + 1):
        for w in range(W + 1):
            if i == 0 or w == 0:
                K[i][w] = 0
            elif wt[i - 1] <= w:
                K[i][w] = max(val[i - 1] + K[i - 1][w - wt[i - 1]], K[i - 1][w])
            else:
                K[i][w] = K[i - 1][w]

    res = K[n][W]

    w = W
    Z = []
    for i in range(n, 0, -1):
        if res <= 0:
            break
        if res == K[i - 1][w]:
            continue
        else:

            # This item is included.
            Z.append(val[i - 1])

            res = res - val[i - 1]
            w = w - wt[i - 1]
    return Z


# knapsack
def solve_knapsack(R: List, E: List) -> List:
    val = []
    for e in E:
        val.append(u[e])

    wt = []

    for row in range(len(R)):
        temp = []
        for e in E:
            temp.append(kebutuhan[e][row])
        wt.append(temp)

    Z_list = []
    for w in wt:
        counter = 0
        z = _knapsack(R[0], w, val, len(val))
        Z_list.append(z)
        counter += 1

    ctr = {}
    for v in val:
        for Z in Z_list:
            if v in Z and v not in ctr:
                ctr[E[val.index(v)]] = 1
            elif v in Z and ctr[E[val.index(v)]]:
                ctr[E[val.index(v)]] =+ 1

    to_process = []
    for c in ctr:
        if ctr[c] == len(R):
            to_process.append(c)

    return to_process


u = count_weight(data_durasi)
u[n+1] = 1
schedule = {}


def rcpsp(A, F, P, s0, RSk):
    if F == A:
        return schedule

    U = list(set(A).difference(set(F)))
    ss = []
    for j in U:
        if j in S:
            for i in S[j]:
                ss.append(i)
    N = list(set(ss))
    E = list(set(U).difference(set(N).union(P)))

    to_process = solve_knapsack(RSk, E)
    for tp in to_process:
        P.append(tp)

    for i in to_process:
        if i not in schedule:
            si = list(s0)
            fi = []
            di = data_durasi[i-1]
            for s in range(len(si)):
                fi.append(si[s] + di[s])
            schedule[i] = [si, fi]

    sigma_r = [0 for x in range(len(RSk))]
    for i in to_process:
        for k in range(len(kebutuhan[i])):
            sigma_r[k] += kebutuhan[i][k]

    for index in range(len(RSk)):
        RSk[index] = RSk[index] - sigma_r[index]

    j = []
    j_index = []
    for i in P:
        j.append(schedule[i][1][-1])
        j_index.append(i)

    f4 = min(j)
    f4_index = j.index(f4)
    F = list(set(F).union(set([j_index[f4_index]])))
    Ft = j_index[f4_index]
    s0 = schedule[Ft][1]

    for index in range(len(RSk)):
        RSk[index] = RSk[index] + kebutuhan[Ft][index]

    P, F = set(P), set(F)
    pf = P.intersection(F)
    P = list(P.difference(pf))
    F = list(F)

    if F[-1] == n:
        schedule[F[-1] + 1] = schedule[F[-1]]
        F.append(F[-1] + 1)

    rcpsp(A, F, P, s0, RSk)


rcpsp(A, F, P, s0, RSk)
print(schedule)
D_s = [0, 0, 0, 0]
for i in range(len(b)):
    D_s[i] = schedule[A[-1]][1][i] - b[i]

print(D_s)
