{-# LANGUAGE DeriveGeneric #-}

module Pending ( findPending ) where

import Ast

import Text.PrettyPrint

data PendingTree
  = Node String Int Bool [PendingTree]
  deriving (Eq, Show)

data FormatTree
  = TEmpty  FormatTree
  | TSpace  FormatTree
  | TBranch FormatTree
  | TNode
  | TLeaf
  |TQuest
  deriving (Eq, Show)

showTree :: FormatTree -> Doc
showTree (TEmpty t)  = showTree t
showTree (TBranch t) = text " | " <> showTree t
showTree (TSpace t)  = text "   " <> showTree t
showTree TNode       = text " |- "
showTree TLeaf       = text " |> "
showTree TQuest      = text " |? "

linebreak :: Doc
linebreak = text "\n"

isLeaf :: PendingTree -> Bool
isLeaf (Node _ _ _ [])    = True
isLeaf (Node _ _ _ (_:_)) = False

formatTree :: PendingTree -> Doc
formatTree t = formatSubTree (TEmpty) t

formatSubTrees :: (FormatTree -> FormatTree) -> [PendingTree] -> Doc
formatSubTrees _ [] = empty
formatSubTrees ft [t] | isLeaf t =
  linebreak <> showTree (ft TLeaf) <> formatSubTree (ft . TSpace) t
formatSubTrees ft [t] =
  linebreak <> showTree (ft TNode) <> formatSubTree (ft . TSpace) t
formatSubTrees ft (t:ts) | isLeaf t =
  linebreak <> showTree (ft TLeaf) <> formatSubTree (ft . TBranch) t <>
  formatSubTrees ft ts
formatSubTrees ft (t:ts) =
  linebreak <> showTree (ft TNode) <> formatSubTree (ft . TBranch) t <>
  formatSubTrees ft ts

formatSubTree :: (FormatTree -> FormatTree) -> PendingTree -> Doc
formatSubTree ft (Node s cs _ ts) = text s <> formatTreeComments ft cs <> formatSubTrees ft ts

formatTreeComments :: (FormatTree -> FormatTree) -> Int -> Doc
formatTreeComments _ 0 = empty
formatTreeComments ft cs =
  linebreak <> showTree (ft TQuest) <> text (makePlural cs "impartial comment")

size :: PendingTree -> (Int, Int)
size (Node _ cs True []) = (1, cs)
size (Node _ cs False []) = (0, cs)
size (Node _ cs _ pt) = foldl tupAdd (0, cs) $ map size pt
  where tupAdd a b = (fst a + fst b, snd a + snd b)

limitPendingTree :: Int -> PendingTree -> PendingTree
limitPendingTree _    (Node s 0 b [])  = Node s 0 b []
limitPendingTree 0 (t@(Node s _ b _))  = Node (s ++ showTasks (size t)) 0 b []
limitPendingTree n    (Node s cs b ts) = Node s cs b (map (limitPendingTree (n-1)) ts)

showTasks :: (Int, Int) -> String
showTasks (n, 0) = " (" ++ makePlural n "task" ++ ")"
showTasks (0, m) = " (" ++ makePlural m "comment" ++ ")"
showTasks (n, m) = " (" ++ makePlural n "task" ++ " and " ++ makePlural m "comment" ++ ")"

makePlural :: Int -> String -> String
makePlural 0 s = "0 " ++ s ++ "s"
makePlural 1 s = "1 " ++ s
makePlural n s = show n ++ " " ++ s ++ "s"

pendingJudgement :: Judgement -> [PendingTree]
pendingJudgement (Bonus (_, _, cs)) =
  case countImpartials cs of
    0 -> []
    n -> [Node "Bonus" n False []]
pendingJudgement (Judgement (Header (t, p, _), _, cs, [])) | isInfinite p =
  [Node t (countImpartials cs) True []]
pendingJudgement (Judgement (Header (t, _, _), _, cs, subJs)) =
  case (concatMap pendingJudgement subJs) of
    [] ->
      case countImpartials cs of
        0 -> []
        n -> [Node t n False []]
    sub  -> [Node t (countImpartials cs) False sub]

countImpartials :: [Comment] -> Int
countImpartials = sum . (map countImpartial)

countImpartial :: Comment -> Int
countImpartial (Comment (Impartial, cps)) = 1 + (sum $ map countImpartialCP cps)
countImpartial (Comment (_, cps)) = sum $ map countImpartialCP cps

countImpartialCP :: CommentPart -> Int
countImpartialCP (CommentStr _) = 0
countImpartialCP (CommentCmt c) = countImpartial c

findPending :: Maybe Int -> [Judgement] -> Maybe(String)
findPending detailLevel js =
  case (concatMap pendingJudgement js) of
    [] -> Nothing
    t  ->
      case detailLevel of
        Nothing  -> Just $ render $ vcat $ map formatTree t
        (Just i) -> Just $ render $ vcat $ map (formatTree . (limitPendingTree i)) t