import json
import math
import copy

with open("clean.json") as infile:
    data = json.load(infile)

def clean_item(item):
    if not "displayName" in item:
        item["displayName"] = item["name"];
    return item

items = data["items"]
item_map = {clean_item(item)["displayName"]: item for item in items}

# build_items_names = [
#     "Cumulonimbus",
#     "Soulflare",
#     "Leictreach Makani",
#     "Slayer",
#     "Intensity",
#     "Moon Pool Circlet",
#     "Diamond Static Bracelet",
#     "Royal Stormy Amulet"
# ]
# build_weapon_name = "Fatal"
# build_items_names = [
#     "Morph-Stardust",
#     "Morph-Steel",
#     "Morph-Iron",
#     "Morph-Gold",
#     "Morph-Topaz",
#     "Morph-Emerald",
#     "Morph-Amethyst",
#     "Morph-Ruby"
# ]
# build_weapon_name = "Cascade"
build_items_names = [
    "Blue Mask",
    "Sparkling Plate",
    "Gemini",
    "Slayer",
    "Draoi Fair",
    "Moon Pool Circlet",
    "Prowess",
    "Diamond Fusion Necklace"
]
build_weapon_name = "Praesidium"
build_items = [item_map[item] for item in build_items_names]
build_weapon = item_map[build_weapon_name]

for item in build_items:
    print(item)
    print("-------------------------------")

print(build_weapon)

# Consolidate skillpoint and req into arrays for ease of processing.
def setup(item):
    item["skillpoints"] = [item["str"], item["dex"], item["int"], item["def"], item["agi"]]
    item["has_negstat"] = any(x < 0 for x in item["skillpoints"])
    item["reqs"] = [item["strReq"], item["dexReq"], item["intReq"], item["defReq"], item["agiReq"]]

fixed = []
consider = []
noboost = []

for item in build_items:
    setup(item)
    if all(x == 0 for x in item["reqs"]):
        fixed.append(item)
    elif all(x == 0 for x in item["skillpoints"]) and item["set"] is None:
        noboost.append(item)
    else:
        consider.append(item)
setup(build_weapon)
fixed = tuple(fixed)
noboost = tuple(noboost)
consider = tuple(consider)

"""
The way this code expects this to work:
sets is a map: setName -> setObject {
    bonuses: array[bonusObject]
}

where each bonusObject is a mapping from id to boost value.
And the bonuses array describes the effect of equipping set items (0 index = 1 item).
"""
sets = dict()

# Apply the skillpoints an item gives to the build.
"""
skillPoints: current skillpoint totals.
item: Item in uestion.
activeSetCounts: Mapping from setname to number of items currently worn (not including this one).
"""
def apply_skillpoints(skillpoints, item, activeSetCounts):
    for i in range(5):
        skillpoints[i] += item["skillpoints"][i]

    if item["set"] is not None:
        setName = item["set"]
        old_bonus = dict()
        if setName in activeSetCounts:
            setCount = activeSetCounts[setName]
            old_bonus = sets[setName]["bonuses"][setCount-1]
            activeSetCounts[setName] = setCount + 1
        else:
            setCount = 0
            activeSetCounts[setName] = 1
        new_bonus = sets[setName]["bonuses"][setCount]
        skp_order = ["str","dex","int","def","agi"]
        for i, skp in enumerate(skp_order):
            delta = new_bonus[skp] - old_bonus[skp]
            skillpoints[i] += delta

# Figure out (naively) how many skillpoints need to be applied to get the current item to fit.
# Doesn't handle -skp.
def apply_to_fit(skillpoints, item, skillpoint_filter, activeSetCounts):
    applied = [0, 0, 0, 0, 0]
    total = 0
    for i, req, cur in zip(range(5), item["reqs"], skillpoints):
        if item["skillpoints"][i] < 0 and skillpoint_filter[i]:
            applied[i] -= item["skillpoints"][i]
            total -= item["skillpoints"][i]
        if (item["reqs"][i] == 0):
            continue
        skillpoint_filter[i] = True
        if req > cur:
            diff = req - cur
            applied[i] += diff
            total += diff

    if item["set"] is not None:
        setName = item["set"]
        old_bonus = dict()
        if setName in activeSetCounts:
            setCount = activeSetCounts[setName]
            old_bonus = sets[setName]["bonuses"][setCount-1]
            activeSetCounts[setName] = setCount + 1
        else:
            setCount = 0;
            activeSetCounts[setName] = 1
        new_bonus = sets[setName]["bonuses"][setCount]
        skp_order = ["str","dex","int","def","agi"]
        for i, skp in enumerate(skp_order):
            delta = new_bonus[skp] - old_bonus[skp]
            if delta < 0 and skillpoint_filter[i]:
                applied[i] -= delta
                total -= delta
    return applied, total

# Permutations in js reference (also cool algorithm):
# https://stackoverflow.com/a/41068709

static_skillpoints_base = [0, 0, 0, 0, 0]
static_activeSetCounts = dict()

# Separate out the no req items and add them to the static skillpoint base.
for item in fixed:
    apply_skillpoints(static_skillpoints_base, item, static_activeSetCounts)

best = consider + noboost;
final_skillpoints = static_skillpoints_base[:]
best_skillpoints = [0, 0, 0, 0, 0]
best_total = math.inf
best_activeSetCounts = dict()

allFalse = [False] * 5

if len(consider) or len(noboost):

    # Try every combination and pick the best one.
    import itertools
    for permutation in itertools.permutations(consider):
        activeSetCounts = dict(best_activeSetCounts)
        has_skillpoint = allFalse[:]

        permutation += noboost

        skillpoints_applied = [0, 0, 0, 0, 0]
        skillpoints = static_skillpoints_base[:]
        total_applied = 0
        for item in permutation:
            needed_skillpoints, total_diff = apply_to_fit(skillpoints, item, has_skillpoint, activeSetCounts)
            for i in range(5):
                skillpoints_applied[i] += needed_skillpoints[i]
                skillpoints[i] += needed_skillpoints[i]
            apply_skillpoints(skillpoints, item, activeSetCounts)
            total_applied += total_diff
            if total_applied >= best_total:
                break

        needed_skillpoints, total_diff = apply_to_fit(skillpoints, build_weapon, has_skillpoint, activeSetCounts)
        for i in range(5):
            skillpoints_applied[i] += needed_skillpoints[i]
            skillpoints[i] += needed_skillpoints[i]
        apply_skillpoints(skillpoints, build_weapon, activeSetCounts)
        total_applied += total_diff

        if total_applied < best_total:
            best = permutation
            final_skillpoints = skillpoints
            best_skillpoints = skillpoints_applied
            best_total = total_applied
            best_activeSetCounts = activeSetCounts
else:
    best_total = 0
    needed_skillpoints, total_diff = apply_to_fit(skillpoints, build_weapon, allFalse, best_activeSetCounts)
    for i in range(5):
        best_skillpoints[i] += needed_skillpoints[i]
        final_skillpoints[i] += needed_skillpoints[i]
    apply_skillpoints(skillpoints, build_weapon, best_activeSetCounts)
    best_total += total_diff

equip_order = fixed + best
results = [equip_order, best_skillpoints, final_skillpoints, best_total, best_activeSetCounts];

print([i["displayName"] for i in fixed + best])
print(best_skillpoints)
print(final_skillpoints)
print(best_total)

#def attempt(skillpoints, items_in_order):