import sys
from dataclasses import dataclass, field
from multiprocessing import *
from itertools import chain

DEBUG = True
debug_padding = 24

def debug_print(printname, *args):
    if DEBUG:
        print(f"{'DEBUG::'+printname:<{debug_padding}}", *args)

@dataclass
class RangeContainer:
    dest_start: int = 0
    src_start: int = 0
    range: int = 0

@dataclass
class SeedRangeContainer:
    start: int = 0
    length: int = 0

@dataclass
class Seed:
    number: int = 0
    location: int = 0


seeds = []
seed_ranges = []
transforms = []

def map_range(num, map_ranges):
    for range_cont in map_ranges:
        if num >= range_cont.src_start and num < range_cont.src_start+range_cont.range:
            return (num-range_cont.src_start) + range_cont.dest_start
    return num

#debug_print("the_range out", list(range(79,79+14)))
#min_map(79, 14, RangeContainer(50, 98, 2))

#debug_print("the_range out", list(range(90,90+10)))
#min_map(90, 10, RangeContainer(50, 98, 2))

#exit()

print("Parsing Input:")
print("")
print("===================================\n");

FILENAME = "/home/icaema/Public/advent-of-code/day-05/data/p02data.txt"
f = open(FILENAME, "r")

parse_map_no = 0    #the index of the maps that we are processing
                    # 1 is seed-to-soil
                    # 2 is soil-to-fert
                    # and so on

#for line in sys.stdin:
for line in f.readlines():
    line = line.strip()
    debug_print("input",  line, "no", parse_map_no)
    if line == "":
        continue
    if line[:7] == "seeds: ":
        #seeds = [Seed(int(num)) for num in line[7:].split()]
        seed_split = line[7:].split()
        for i in range(0, len(seed_split), 2):
            seed_ranges.append(SeedRangeContainer(int(seed_split[i]), int(seed_split[i+1])))
        debug_print("seeds", [s for s in seed_ranges])
        continue
    if "map" in line:
        parse_map_no += 1
        transforms.append([])
        continue
    
    pr = [int(x) for x in line.split()]
    transforms[-1].append(RangeContainer(pr[0], pr[1], pr[2]))
    debug_print("transforms", transforms[-1])




print("")
print(" Proccessing:")
print("===================================\n");
# Insert Processing Code

def find_lowest_of_range(srange, transforms, result, index):
    lowest_location = float('inf')
    for i in range(srange.length):
        #debug_print(f"thread: {index}  still working", f"Progress: {(i/srange.length):.0%}. {i}/{srange.length}")

        if i%500_000 == 0:
            debug_print(f"thread: {index}  still working", f"Progress: {(i/srange.length):.0%}. {i}/{srange.length}")
        tmp = srange.start+i
        #debug_print(f"processing {index}", f"checking seed loc: {tmp}")
        for tomap in transforms:
            tmp = map_range(tmp, tomap)
        if tmp < lowest_location:
            print(f"thread: {index}  new lower! ", tmp)
            lowest_location = tmp
    result[index] = lowest_location
    print(f"thread: {index}  all set", lowest_location)



lowest_location = float('inf')

threads = [None] * (len(seed_ranges)*4)

manager =Manager()
return_dict = manager.dict()

debug_print("seed_ranges length", len(seed_ranges))
debug_print("threads length", len(seed_ranges)*4+1)

for i, srange in enumerate(seed_ranges):
    rangelen = -(-srange.length // 4)

    range1_start = srange.start
    range2_start = srange.start+rangelen
    range3_start = srange.start+rangelen*2
    range4_start = srange.start+rangelen*3


    threads[i] = Process(target=find_lowest_of_range, args=(SeedRangeContainer(range1_start, rangelen), transforms, return_dict, i))
    threads[i+10] = Process(target=find_lowest_of_range, args=(SeedRangeContainer(range2_start, rangelen), transforms, return_dict, i+10))
    threads[i+20] = Process(target=find_lowest_of_range, args=(SeedRangeContainer(range3_start, rangelen), transforms, return_dict, i+20))
    threads[i+30] = Process(target=find_lowest_of_range, args=(SeedRangeContainer(range4_start, rangelen), transforms, return_dict, i+30))
    debug_print("thread", f"started thread nos.", i,i+10,i+20,i+30)
    debug_print("thread", len(threads))
    threads[i].start()
    threads[i+10].start()
    threads[i+20].start()
    threads[i+30].start()

debug_print("threads length", len(threads))



for i in range(len(threads)):
    threads[i].join()
    returned = return_dict.values()
    if returned[i] < lowest_location:
        lowest_location = returned[i]

debug_print("results",return_dict.values())
    
    #for i, attribute in  enumerate(attributes):
    #    setattr(seed, attribute, map_range(seed, transforms[i-1]))
    #    debug_print("get", attribute, getattr(seed, attribute))
    #debug_print(f"seed {seed.number}, soil {seed.soil}, fertilizer {seed.fertilizer}")






print("lowest", lowest_location)
#for seed in seeds:
#    debug_print("location", seed.location)



print("")
print(" Calculate Answer:")
print("===================================\n");


#min_location = min([s.location for s in seeds])




print("")
print(" Answer:")
print("===================================\n");
print(f"lowest_location: \n{lowest_location}");