module Days.Day15 where

import AOCUtil
import Data.Bifunctor
import Data.Bool
import Data.Char
import Data.Either
import Data.IntSet qualified as Data.Map
import Data.Ix
import Data.List
import Data.Maybe
import Data.Set qualified
import Debug.Trace
import GHC.Arr
import GHC.Utils.Misc

runA :: IO ()
runA = interactF "data/day15.txt" solve

runB :: IO ()
runB = interactF "data/day15.txt" solveB

aLine :: Int
aLine = 2000000

solve :: String -> String
-- solve s = show . subtract (beaconsLine d) . length . nub . sort . filter ((== line) . snd) . concatMap coveredPos $ d
-- solve s = show . length . filter (isInRange d) $ [(x, line) | x <- [minXSens d - maxDistance d .. maxXSens d + maxDistance d]]
solve s = show . subtract (beaconsOnLine aLine d) . sum . map sLen . traceShowId . (allOnLine aLine) $ d
  where
    d = readData s

solveB :: String -> String
solveB s = show . filter (any (inRange (0, sMax)) . fst) . map (\i -> ((,i) . concatMap asList2 . allOnLine i $ d)) $ [0 .. sMax]
  where
    d = readData s

type Pos = (Int, Int)

type Dat = [(Pos, Pos)]

readData :: String -> Dat
readData = map (toData . map (read . takeWhile isDigit2) . tail . splitOn '=') . lines

isDigit2 :: Char -> Bool
isDigit2 c = isDigit c || c == '-'

toData :: [Int] -> (Pos, Pos)
toData [a, b, c, d] = ((a, b), (c, d))

onLine :: Int -> (Pos, Pos) -> Maybe (Int, Int) -- sensor, beacon -> startx starty
onLine line (s@(x, y), b)
  | manhattan s b < abs (y - line) = Nothing
  | otherwise = Just (x - expansion, x + expansion)
  where
    expansion = manhattan s b - abs (y - line)

allOnLine :: Int -> Dat -> [(Int, Int)]
allOnLine line = foldl collapseSections [] . sort . mapMaybe (onLine line)

beaconsOnLine :: Int -> Dat -> Int
beaconsOnLine line = length . nub . sort . filter ((== line) . snd) . map snd

collapseSections :: [(Int, Int)] -> (Int, Int) -> [(Int, Int)]
collapseSections [] a = [a]
collapseSections (l@(s, e) : ls) n@(ns, ne)
  | e < ns = n : l : ls
  | otherwise = (s, max ne e) : ls -- join

sLen :: (Int, Int) -> Int
sLen (a, b) = b - a + 1

sMax :: Int
sMax = 4000000

-- ----------------------------

-- maxDistance :: Dat -> Int
-- maxDistance = maximum . map (uncurry manhattan)

-- minXSens :: Dat -> Int
-- minXSens = minimum . map (fst . fst)

-- maxXSens :: Dat -> Int
-- maxXSens = maximum . map (fst . fst)

-- isInRange :: Dat -> Pos -> Bool
-- isInRange dat p = any (isInRange' p) dat

-- isInRange' :: Pos -> (Pos, Pos) -> Bool
-- isInRange' p (s, b) = manhattan s b >= manhattan s p

-- ----------------------------------------

-- coveredPos :: (Pos, Pos) -> [Pos]
-- coveredPos (p@(x, y), s@(sx, sy)) = filter ((<= lim) . manhattan p) [(x, y) | x <- [x - lim .. x + lim], y <- [y - lim .. y + lim]]
--   where
--     lim = manhattan p s

-- beaconsLine :: Int -> [(Pos, Pos)] -> Int
-- beaconsLine line = length . filter (== line) . map snd . nub . sort . map snd
