module Example.QueryMonad where
import Query
import Company
import Data.Function (on, )
import Data.List (nub, nubBy, sortBy, maximumBy, )
import Data.Ord (comparing, )
import Control.Monad (guard, )
employees :: [Emp]
employees = emp
clerks :: [Emp]
clerks = do
   e <- emp
   guard (job e == Clerk)
   return e
richClerks :: [Emp]
richClerks = do
   e <- emp
   guard (job e == Clerk && sal e >= 1000)
   return e
researchers :: [Emp]
researchers = do
   e <- emp
   d <- dept
   guard (deptno e == deptno d && dname d == "RESEARCH")
   return e
researchers0 :: [Emp]
researchers0 = do
   d <- dept
   guard (dname d == "RESEARCH")
   e <- emp
   guard (deptno e == deptno d)
   return e
managers :: [(String, String)]
managers = do
   e <- emp
   m <- emp
   guard (Just (empno m) == mgr e)
   return (ename e, ename m)
managers0 :: [(String, String)]
managers0 = do
   e <- emp
   mname <-
      case mgr e of
         Nothing ->
            return ""
         Just mgre -> do
            m <- emp
            guard (empno m == mgre)
            return (ename m)
   return (ename e, mname)
realManagers :: [String]
realManagers = nub $ do
   e <- emp
   m <- emp
   guard (Just (empno m) == mgr e)
   return (ename m)
realManagersFull :: [Emp]
realManagersFull =
   nubBy ((==) `on` ename) $ do
      e <- emp
      m <- emp
      guard (Just (empno m) == mgr e)
      return m
realManagersSortedFull :: [Emp]
realManagersSortedFull =
   sortBy (comparing ename) $
   nubBy ((==) `on` ename) $ do
      e <- emp
      m <- emp
      guard (Just (empno m) == mgr e)
      return m
maximumSalary :: Int
maximumSalary = maximum $ do
   e <- emp
   return (sal e)
richestEmployee :: Emp
richestEmployee =
   maximumBy (comparing sal) emp
teams :: [(String, [String])]
teams = do
   m <- emp
   let es = do
         e <- emp
         guard (Just (empno m) == mgr e)
         return (ename e)
   guard (not (null es))
   return (ename m, es)
teams0 :: [(String, [String])]
teams0 = do
   (mm,es) <- groupBy mgr emp
   m <- emp
   guard (Just (empno m) == mm)
   return (ename m, map ename es)
averageSalariesInDepartments :: [(String, Int)]
averageSalariesInDepartments = do
   (dm,es) <- groupBy deptno emp
   d <- dept
   guard (deptno d == dm)
   return (dname d, Query.averageInt (map sal es))
managerOfLargestTeam :: (String, Int)
managerOfLargestTeam = maximumBy (comparing snd) $ do
   (mm,es) <- groupBy mgr emp
   m <- emp
   guard (Just (empno m) == mm)
   return (ename m, length es)
teamSalaries0 :: [(String, Int)]
teamSalaries0 =
   let recurse mgrsal mgrno =
          (\(sals,emps) ->
             let s = mgrsal + sum sals
             in  (s, (mgrno, s) : concat emps)) $
          unzip $
          map (\e -> recurse (sal e) (Just (empno e))) $
          filter (\e -> mgr e == mgrno) emp
   in  do (mno,esal) <- snd (recurse 0 Nothing)
          e <- emp
          guard (Just (empno e) == mno)
          return (ename e, esal)
teamSalaries :: [(String, Int)]
teamSalaries =
   let recurse mgrno =
          (\(sals,emps) -> (sum sals, concat emps)) $
          unzip $
          map (\e ->
             let (teamSal, team) = recurse (Just (empno e))
                 totalSal = teamSal + sal e
             in  (totalSal, (ename e, totalSal) : team)) $
          filter (\e -> mgr e == mgrno) emp
   in  snd (recurse Nothing)