package day05

import (

	_ "regexp"
	"strings"

	"fmt"

	"adventofcode2023/utils"

)

type Map struct {
	dest_range_start	int
	src_range_start		int
	range_len			int
}

type Almanac struct {
	seed2soil 	[]Map
	soil2fert 	[]Map
	fert2water 	[]Map
	water2light []Map
	light2temp 	[]Map
	temp2humid 	[]Map
	humid2loc 	[]Map
}

type SeedMap struct {
	seed int
	range_len int
}

func Part1(input string) int {
	seeds, almanac, _ := parseInput1(input)
	fmt.Println(seeds)
	fmt.Println(almanac)

	minLoc := int(utils.MaxInt)
	for _, seed := range seeds {
		soil := lookup_dest(seed, almanac.seed2soil)
		fert := lookup_dest(soil, almanac.soil2fert)
		water := lookup_dest(fert, almanac.fert2water)
		light := lookup_dest(water, almanac.water2light)
		temp := lookup_dest(light, almanac.light2temp)
		humid := lookup_dest(temp, almanac.temp2humid)
		loc := lookup_dest(humid, almanac.humid2loc)
		if loc < minLoc {
			minLoc = loc
		}
		fmt.Println(loc)
	}

	return minLoc
}

func Part2(input string) int {
	seedsmap, almanac, _ := parseInput2(input)

	for i:=0;i<utils.MaxInt;i++{
		humid := lookup_src(int(i), almanac.humid2loc)
		temp := lookup_src(humid, almanac.temp2humid)
		light := lookup_src(temp, almanac.light2temp)
		water := lookup_src(light, almanac.water2light)
		fert := lookup_src(water, almanac.fert2water)
		soil := lookup_src(fert, almanac.soil2fert)
		seed := lookup_src(soil, almanac.seed2soil)

		for _, seedmap := range seedsmap {
			if seed >= seedmap.seed && seed < seedmap.seed+seedmap.range_len {
				return i
			}
		}
	}
	return -1
}

func lookup_dest(entity int, data []Map) int {
	for _, dataMap := range data {
		if entity >= dataMap.src_range_start && entity <= dataMap.src_range_start + dataMap.range_len {
			return dataMap.dest_range_start + entity - dataMap.src_range_start
		}
	}
	return entity
}

func lookup_src(entity int, data []Map) int {
	for _, dataMap := range data {
		if entity >= dataMap.dest_range_start && entity <= dataMap.dest_range_start + dataMap.range_len {
			return dataMap.src_range_start + entity - dataMap.dest_range_start
		}
	}
	return entity
}

func parseInput1(input string) ([]int, Almanac, error) {
	
	var almanac Almanac
	var seeds []int

	lines := strings.Split(input, "\n")
	seedLine := string(lines[0])
	tokens := strings.Fields(seedLine)
	for _, token := range tokens[1:] {
			seeds = append(seeds, int(utils.MustAtoi(token)))
	}

	blocks := strings.Split(input, "\n\n")
	for i, block := range blocks {
		lines := strings.Split(block, "\n")
		switch i {
		case 0:
			continue
		case 1:
			almanac.seed2soil = getMap(lines[1:])
		case 2:
			almanac.soil2fert = getMap(lines[1:])
		case 3:
			almanac.fert2water = getMap(lines[1:])
		case 4:
			almanac.water2light = getMap(lines[1:])
		case 5:
			almanac.light2temp = getMap(lines[1:])
		case 6:
			almanac.temp2humid = getMap(lines[1:])
		case 7:
			almanac.humid2loc = getMap(lines[1:])
		}	
	}
	return seeds, almanac, nil
}

func parseInput2(input string) ([]SeedMap, Almanac, error) {
	
	var almanac Almanac
	var seeds []SeedMap

	lines := strings.Split(input, "\n")
	seedLine := string(lines[0])
	tokens := strings.Fields(seedLine)
	for i:=1;i<len(tokens);i=i+2 {
		seedStart := int(utils.MustAtoi(tokens[i]))
		seedRange := int(utils.MustAtoi(tokens[i+1]))
			seeds = append(seeds, SeedMap{seedStart, seedRange})
	}

	blocks := strings.Split(input, "\n\n")
	for i, block := range blocks {
		lines := strings.Split(block, "\n")
		switch i {
		case 0:
			continue
		case 1:
			almanac.seed2soil = getMap(lines[1:])
		case 2:
			almanac.soil2fert = getMap(lines[1:])
		case 3:
			almanac.fert2water = getMap(lines[1:])
		case 4:
			almanac.water2light = getMap(lines[1:])
		case 5:
			almanac.light2temp = getMap(lines[1:])
		case 6:
			almanac.temp2humid = getMap(lines[1:])
		case 7:
			almanac.humid2loc = getMap(lines[1:])
		}	
	}
	return seeds, almanac, nil
}

func getMap(input []string) []Map {
	var out []Map
	for _, line := range input {
		tokens := strings.Fields(line)
		out = append(out, Map{int(utils.MustAtoi(tokens[0])), int(utils.MustAtoi(tokens[1])),int(utils.MustAtoi(tokens[2]))})
	}
	return out
}