module Main
where

import Test.HUnit
import Chessboard
import Board
import Control.Monad
import Data.Tree

main :: IO ()
main = forever $ getLine >>= runTest

main' :: IO ()
main' = readFile "perftsuite.epd" >>= runAll >> return ()
    where runAll = runTestTT . TestList . map fenToTest . lines

runTest :: String -> IO String
runTest = liftM showCounts . runTestTT . fenToTest
    
fenToTest :: String -> Test
fenToTest fen = createTests (emptyBoard::Board) f a
    where (f,a) = parseEPD fen

parseEPD :: String -> (String, [Int])
parseEPD fen = (position, nums)
    where position = head fields
          fields = (map init . separateBy ';') fen
          nums = cleanup (drop 1 fields)
          cleanup = map (read . drop 3)

separateBy :: Eq a => a -> [a] -> [[a]]
separateBy ch string = case dropWhile (== ch) string of
                         [] -> []
                         s:ss -> (s:word) : separateBy ch rest
                             where (word,rest) = break (== ch) ss

createTests :: Chessboard c => c -> String -> [Int] -> Test
createTests c fen answers = TestLabel fen $ TestList $ map (\(d,a)->createTest c fen d a) (zip [1,2] answers)

createTest :: Chessboard c => c -> String -> Int -> Int -> Test
createTest c fen depth answer = TestLabel fen (TestCase (assertEqual (show depth) answer result))
    where result = perft fen depth c

perft :: (Chessboard c) => String -> Int -> c -> Int
perft fen depth c = leaves (prune depth (moveTree (asTypeOf (loadFEN fen) c)))

moveTree :: (Chessboard c) => c -> Tree c
moveTree = unfoldTree (ap (,) (ap (map . flip makeMove) legal_moves))

prune :: Int -> Tree a -> Tree a
prune 0 (Node a _) = Node a []
prune n (Node a x) = Node a (map (prune (n-1)) x)

leaves :: Tree a -> Int
leaves (Node x []) = 1
leaves (Node x xs) = sum (map leaves xs)