import Control.Lens
import Data.List.Split
import Linear.V2

type Sensor = (V2 Int, V2 Int)

main :: IO ()
main = do
  input <- map (splitOneOf "=,:") . lines <$> readFile "input"
  let sensors = map parse input
  print $ "Cave from to: " ++ show (caveMinMaxX sensors)
  print $ part1 (caveMinMaxX sensors) sensors
  print $ part2 sensors

getBorder :: [Sensor] -> [V2 Int]
getBorder (s:ss) = map (+ fst s) xs ++ getBorder ss
  where
    xs =
      concat $ take 4 $ iterate (map perp) [V2 x (d - x) | x <- [0 .. pred d]]
    d = succ $ dist s
getBorder [] = []

caveMinMaxX :: [Sensor] -> (Int, Int)
caveMinMaxX sensors = (minimum k, maximum l)
  where
    d = map dist sensors
    s = map (view _y . fst) sensors
    k = zipWith (-) s d
    l = zipWith (+) s d

part1 :: (Int, Int) -> [Sensor] -> Int
part1 (a, b) sensors = length $ filter (== '#') $ map (checkPoint sensors) xs
  where
    xs = [V2 x 2000000 | x <- [a .. b]] --y=10 for testinput

part2 :: [Sensor] -> Int
part2 sensors =
  f $
  head $
  filter (\x -> checkPoint sensors x == ' ') $
  filter (\(V2 x y) -> x >= 0 && x <= 4000000 && y >= 0 && y <= 4000000) $
  getBorder sensors -- <=20 for testinput
  where
    f = (+) <$> view _y <*> ((*) 4000000 . view _x)

parse :: [String] -> Sensor
parse [_, a, _, b, _, c, _, d] = (V2 (read a) (read b), V2 (read c) (read d))

checkPoint :: [Sensor] -> V2 Int -> Char
checkPoint sensors point
  | point `elem` map fst sensors = 'S'
  | point `elem` map snd sensors = 'B'
  | otherwise = checkPoint' sensors point
  where
    checkPoint' (s:ss) p =
      if dist (fst s, p) <= dist s
        then '#'
        else checkPoint' ss p
    checkPoint' [] _ = ' '

dist :: Sensor -> Int
dist (a, b) = sum $ abs $ a - b
