import Data.List
import Data.List.Split
import Linear.V3

main :: IO ()
main = do
  moons <- parseContent <$> readFile "input"
  let velocity = replicate 4 [0, 0, 0]
  print $ part1 (toV3 moons, toV3 velocity)
  print $ part2 (moons, velocity)

parseContent :: String -> [[Int]]
parseContent = map (map read . splitOn ",") . lines . removeJunk
  where
    removeJunk xs = [x | x <- xs, x `notElem` " <>xyz="]

--I’m sure there is a better way to do this
toV3 :: [[Int]] -> [V3 Int]
toV3 = map (\[x, y, z] -> V3 x y z)

part1 :: ([V3 Int], [V3 Int]) -> Int
part1 input = energy $ iterate step input !! 1000
  where
    energy (x, y) = sum $ zipWith (*) (geten x) (geten y)
    geten = map (sum . abs)

step :: (Num a, Eq a) => ([a], [a]) -> ([a], [a])
step (moons, vel) = (zipWith (+) moons newVel, newVel)
  where
    dVel = gravity moons
    newVel = zipWith (+) vel dVel
    gravity xs = [sum [signum $ x - y | x <- xs, y /= x] | y <- xs]

part2 :: (Num a, Eq a) => ([[a]], [[a]]) -> Int
part2 (moons, vel) = foldr1 lcm periods
  where
    m = transpose moons
    v = transpose vel
    periods = zipWith (curry findPeriod') m v
    findPeriod' x = findPeriod x x 1

findPeriod :: (Num a, Eq a) => ([a], [a]) -> ([a], [a]) -> Int -> Int
findPeriod x a n =
  if x' == a
    then n
    else findPeriod x' a (n + 1)
  where
    x' = step x
