module Days.Day05 where

import AOCUtil
import Data.Char
import Data.List
import GHC.Utils.Misc

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

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

solve :: String -> String
solve = show . map head . uncurry (flip processAll) . readInput

solveB :: String -> String
solveB = show . map head . uncurry (flip processAllB) . readInput

type Stack = [Char] -- prop/push front

type Instruction = (Int, Int, Int) -- from, to, amount

readInput :: String -> ([Stack], [Instruction])
readInput s = (readStacks a, readInstructions b)
  where
    (a, b) = first2 . map unlines . splitOn "" . lines $ s

readStacks :: String -> [Stack]
readStacks = map (reverse . filter (/= ' ') . head . tail) . chunkList 4 . map reverse . transpose . init . lines

readInstructions :: String -> [Instruction]
readInstructions = map readI . lines

readI :: String -> Instruction
readI = first3 . map read . filter (isDigit . head) . words

processAll :: [Instruction] -> [Stack] -> [Stack]
processAll [] = id
processAll (i : is) = processAll is . process i

processAllB :: [Instruction] -> [Stack] -> [Stack]
processAllB [] = id
processAllB (i : is) = processAllB is . processB i

process :: Instruction -> [Stack] -> [Stack]
process (a, b, c) stack = map update . zip [1 ..] $ stack
  where
    update (i, s)
      | i == b = drop a s
      | i == c = (reverse . take a . (!!) stack $ (b - 1)) ++ s
      | otherwise = s

processB :: Instruction -> [Stack] -> [Stack]
processB (a, b, c) stack = map update . zip [1 ..] $ stack
  where
    update (i, s)
      | i == b = drop a s
      | i == c = (take a . (!!) stack $ (b - 1)) ++ s
      | otherwise = s
