package day13

import (
	"adventofcode2023/utils"
	"fmt"
	"strings"
)

type mirrorLoc struct {
	orient string
	index int
}
func Part1(input string) int {
	count := 0

	patterns := strings.Split(input, "\n\n")
	for p, pattern := range patterns {
		rows := strings.Split(pattern, "\n")
		if v, ok := findMirror(rows); ok {
			count = count + v * 100
		} else {
			cols := swap(rows)
			if v, ok := findMirror(cols); ok {
				count = count + v
			} else {
				utils.PanicOnErr(fmt.Errorf("p: %v\n %v\n", p, pattern))
			}
		}
	}
	return count
}

func Part2(input string) int {
	count := 0
	patterns := strings.Split(input, "\n\n")

	for p, pattern := range patterns {
		rows := strings.Split(pattern, "\n")
		loc, _ := findMirrorLoc(rows)
		found := false
		for i:=0;i<len(rows[0])*len(rows);i++{
			newRows := smudge(i, rows)
			if v, ok := findNewMirrorLoc(loc, newRows); ok {
				if v != loc {
					found = true
					if v.orient == "row" {
						count = count + v.index * 100
					} else {
						count = count + v.index
					}
					break
				}
			}
		}
		if ! found {
			utils.PanicOnErr(fmt.Errorf("p: %v\n %v\n", p, pattern))
		}
	}
	return count
}

func smudge(i int, rows []string) []string {
	out := make([]string, len(rows))
	copy(out, rows)
	col := i % len(rows[0])
	row := i / len(rows[0])
	if col >= 0 && col < len(rows[0]) {
		v := rows[row][col]
		if v == '#' {
			out[row] = out[row][:col] + "."  + out[row][col+1:]
		} else {
			out[row] = out[row][:col] + "#"  + out[row][col+1:]
		}
	}
	return out
}

func findNewMirrorLoc(loc mirrorLoc, rows []string) (mirrorLoc, bool) {
	if v, ok := findNewMirror(loc, "row", rows); ok {
		return mirrorLoc{"row", v}, true
	}
	cols := swap(rows)
	if v, ok := findNewMirror(loc, "col", cols); ok {
		return mirrorLoc{"col", v}, true
	}
	return mirrorLoc{}, false
}

func findMirrorLoc(rows []string) (mirrorLoc, bool) {
	if v, ok := findMirror(rows); ok {
		return mirrorLoc{"row", v}, true
	}
	cols := swap(rows)
	if v, ok := findMirror(cols); ok {
		return mirrorLoc{"col", v}, true
	}
	return mirrorLoc{}, false
}

func swap(in []string) []string {
	out := []string{}
	for j:=0;j<len(in[0]);j++ {
		o := ""
		for i:=0;i<len(in);i++ {
			o = o + string(in[i][j])
		}
		out = append(out, o)
	}
	return out
}

func findMirror(rows []string) (int, bool) {
	for i:=0;i<len(rows)-1;i++ {
		if rows[i] == rows[i+1] {
			if isMirror(i, 0, rows) {
				return i+1, true
			}
		}
	}
	return -1, false
}

func findNewMirror(loc mirrorLoc, orient string, rows []string) (int, bool) {
	for i:=0;i<len(rows)-1;i++ {
		if orient == loc.orient && i == loc.index - 1{ continue } 
		if rows[i] == rows[i+1] {
			if isMirror(i, 0, rows) {
				return i+1, true
			}
		}
	}
	return -1, false
}

func isMirror(i int, offset int, rows []string) bool {
	if i == 0 || i == len(rows) - 1 { return true }

	if i - offset < 0 || i+1+offset > len(rows) - 1 { return true }

	if rows[i+1+offset] == rows[i-offset] { 
		return isMirror(i, offset+1, rows)
	}
	return false
}