import Data.List (sortBy, groupBy)

import Text.Printf (printf)

import Data.Ord (comparing)

import Data.Function (on)

type ID = Int

type DEP = String

type NAME = String

type SALARY = Double

data Employee = Employee
  { nr :: ID
  , dep :: DEP
  , name :: NAME
  , sal :: SALARY
  }

employees :: [Employee]
employees =
  fmap
    (\(i, d, n, s) -> Employee i d n s)
    [ (1001, "AB", "Janssen A.H.", 41000)
    , (101, "KA", "'t Woud B.", 45000)
    , (1013, "AB", "de Bont C.A.", 65000)
    , (1101, "CC", "Modaal A.M.J.", 30000)
    , (1203, "AB", "Anders H.", 50000)
    , (100, "KA", "Ezelbips P.J.", 52000)
    , (1102, "CC", "Zagt A.", 33000)
    , (1103, "CC", "Ternood T.R.", 21000)
    , (1104, "CC", "Lageln M.", 23000)
    , (1105, "CC", "Amperwat A.", 19000)
    , (1106, "CC", "Boon T.J.", 25000)
    , (1107, "CC", "Beloop L.O.", 31000)
    , (1009, "CD", "Janszoon A.", 38665)
    , (1026, "CD", "Janszen H.P.", 41000)
    , (1011, "CC", "de Goeij J.", 39000)
    , (106, "KA", "Pragtweik J.M.V.", 42300)
    , (111, "KA", "Bakeuro S.", 31000)
    , (105, "KA", "Clubdrager C.", 39800)
    , (104, "KA", "Karendijk F.", 23000)
    , (107, "KA", "Centjes R.M.", 34000)
    , (119, "KA", "Tegenstroom H.L.", 39000)
    , (1111, "CD", "Telmans R.M.", 55500)
    , (1093, "AB", "de Slegte S.", 46987)
    , (1199, "CC", "Uitlaat G.A.S.", 44500)
    ]

firstN :: Int
       -> (Employee -> DEP)
       -> (Employee -> SALARY)
       -> [Employee]
       -> [[Employee]]
firstN n o1 o2 x =
  fmap
    (take n . sortBy (comparingDown o2))
    (groupBy (groupingOn o1) (sortBy (comparing o1) x))

groupingOn
  :: Eq a1
  => (a -> a1) -> a -> a -> Bool
groupingOn = ((==) `on`)

comparingDown
  :: Ord a
  => (b -> a) -> b -> b -> Ordering
comparingDown = flip . comparing

main :: IO ()
main = do
  printf "%-16s%3s%10s\n" "NAME" "DEP" "TIP"
  putStrLn $ replicate 31 '='
  mapM_ (traverse ((printf "%-16s%3s%10.2g\n" . name) <*> dep <*> sal)) $
    firstN 3 dep sal employees