package main

import (
	"errors"
	"fmt"
	"strings"

	h "git.bullercodeworks.com/brian/adventofcode/helpers"
)

type scanner struct {
	number  int
	region  map[h.Coordinate]bool
	beacons []beacon
}

func NewScanner(b []beacon) scanner {
	return scanner{beacons: b}
}

func NewScannerFromInput(inp []string) (scanner, error) {
	s := scanner{}
	r := strings.NewReader(inp[0])
	_, err := fmt.Fscanf(r, "--- scanner %d ---", &s.number)
	if err != nil {
		return s, errors.New("No Scanner ID")
	}
	for _, v := range inp[1:] {
		r = strings.NewReader(v)
		b := beacon{}
		_, err := fmt.Fscanf(r, "%d,%d,%d", &b.x, &b.y, &b.z)
		if err == nil {
			s.beacons = append(s.beacons, b)
		}
	}
	return s, nil
}

func (s scanner) allRotations() []scanner {
	var ret []scanner
	var beacons [][]beacon
	for i := range s.beacons {
		beacons = append(beacons, s.beacons[i].allRotations())
	}
	for i := range beacons {
		ret = append(ret, NewScanner(beacons[i]))
	}
	return ret
}

func (s scanner) getTransformedIfOverlap(w scanner) (transformedScanner, error) {
	for _, otherReoriented := range w.allRotations() {
		for _, first := range s.beacons {
			for _, second := range otherReoriented.beacons {
				otherPosition := first.sub(second)
				var otherTransformed []beacon
				for _, it := range otherReoriented.beacons {
					otherTransformed = append(otherTransformed, otherPosition.add(it))
				}
				// Check if otherTransformed has at least 12 beacons in common with s
				var count int
				for _, chkI := range otherTransformed {
					for _, chkJ := range s.beacons {
						if chkI.equals(chkJ) {
							count++
							if count >= 12 {
								return transformedScanner{
									beacons:  otherTransformed,
									position: otherPosition,
								}, nil
							}
						}
					}
				}
			}
		}
	}
	return transformedScanner{}, errors.New("Scanners don't overlap")
}

func (s scanner) String() string {
	return fmt.Sprintf("-- Scanner %d (%d beacons) ---", s.number, len(s.beacons))
}

type beacon struct {
	x, y, z int
}

func (b beacon) sub(c beacon) beacon {
	return beacon{
		x: b.x - c.x,
		y: b.y - c.y,
		z: b.z - c.z,
	}
}

func (b beacon) add(c beacon) beacon {
	return beacon{
		x: b.x + c.x,
		y: b.y + c.y,
		z: b.z + c.z,
	}
}

func (b beacon) equals(c beacon) bool {
	return b.x == c.x && b.y == c.y && b.z == c.z
}

func (b beacon) allRotations() []beacon {
	return []beacon{
		{b.x, b.y, b.z}, {b.x, -b.z, b.y}, {b.x, -b.y, -b.z}, {b.x, b.z, -b.y}, {-b.x, -b.y, b.z},
		{-b.x, -b.z, -b.y}, {-b.x, b.y, -b.z}, {-b.x, b.z, b.y}, {-b.z, b.x, -b.y}, {b.y, b.x, -b.z},
		{b.z, b.x, b.y}, {-b.y, b.x, b.z}, {b.z, -b.x, -b.y}, {b.y, -b.x, b.z}, {-b.z, -b.x, b.y},
		{-b.y, -b.x, -b.z}, {-b.y, -b.z, b.x}, {b.z, -b.y, b.x}, {b.y, b.z, b.x}, {-b.z, b.y, b.x},
		{b.z, b.y, -b.x}, {-b.y, b.z, -b.x}, {-b.z, -b.y, -b.x}, {b.y, -b.z, -b.x},
	}
}

func (b beacon) String() string {
	return fmt.Sprintf("{%d,%d,%d}", b.x, b.y, b.z)
}

type transformedScanner struct {
	beacons  []beacon
	position beacon
}
