module Animation
  ( animateSolution
  ) where

import Control.Arrow
import Control.Monad

import Graphics.Gloss(Color, Display(InWindow), Picture)
import qualified Graphics.Gloss as Gloss

import Node(Node(..))
import qualified Node

-- | Animates the 8-Puzzle based on a solution node.
animateSolution::Node -- ^ The solution node.
               -> IO()
animateSolution node =
  Gloss.simulate window background 2 nodeList (picturize . head) stepList
  where nodeList = Node.toList node
        stepList _ _ [] = []
        stepList _ _ [node'] = [node']
        stepList _ _ (_:ns) = ns

window::Display
window = InWindow "8 Puzzle Solver" (tileWidth * 3, tileWidth * 3) (100, 100)

-- | Converts a node into a Gloss.Picture, based on the node's state.
picturize::Node -> Picture
picturize EmptyNode = Gloss.blank
picturize node = Gloss.pictures $ uncurry makeTile <$> indexedBoard
  where indexedBoard = zip [0..] ((concat . boardState) node)

tileWidth::Int
tileWidth = 100

background::Color
background = Gloss.black

numberColor::Color
numberColor = Gloss.black

tileColor::Color
tileColor = Gloss.white

-- | Makes a tile picture based on the given tile number and index.
makeTile::Int -- ^ The 1D index of the tile.
        -> Int -- ^ The number of the tile, or 0 for the empty tile.
        -> Picture
makeTile _ 0 = Gloss.blank
makeTile i n = Gloss.translate dx dy $ Gloss.pictures [tile, text]
  where (r, c) = (quot i 3, rem i 3)
        (dx, dy) = join (***) (fromIntegral . (* tileWidth)) (c - 1, 1 - r)
        tileRadius = fromIntegral tileWidth / 2
        text = Gloss.scale 0.2 0.2 $ Gloss.color numberColor $ Gloss.text $ show n
        tile = Gloss.color tileColor $ Gloss.circleSolid tileRadius
