module Graphics.X11.XInput.Devices
  (Device (..),
   DevicesMap (..),
   buildDevicesMap,
   showDevicesMap,
   isMasterDevice,
   deviceById, masterDeviceById,
   deviceByName
  ) where

import qualified Data.Map as M
import Text.Printf

import Graphics.X11.XInput.Types

data Device = Device {
  masterDevice :: DeviceInfo,
  slaveDevices :: [DeviceInfo] }
  deriving (Eq, Show)

data DevicesMap = DevicesMap {
    masterDevices :: M.Map DeviceID Device
  , allDevices    :: M.Map DeviceID DeviceInfo
  , byName        :: M.Map String DeviceInfo }
  deriving (Eq, Show)

buildDevicesMap :: [DeviceInfo] -> DevicesMap
buildDevicesMap list = DevicesMap {
                           masterDevices = M.fromList $ build [] list
                         , allDevices    = M.fromList [(diID   di, di) | di <- list]
                         , byName        = M.fromList [(diName di, di) | di <- list]
                         }
  where
    build :: [(DeviceID, Device)] -> [DeviceInfo] -> [(DeviceID, Device)]
    build done [] = done
    build done (d:ds) = 
      case diUse d of
        XIMasterPointer  -> build (simply d: done) ds
        XIMasterKeyboard -> build (simply d: done) ds
        XISlavePointer   -> build (up d done) ds
        XISlaveKeyboard  -> build (up d done) ds
        XIFloatingSlave  -> build (up d done) ds

    simply d = (diID d, Device d [])

    up :: DeviceInfo -> [(DeviceID, Device)] -> [(DeviceID, Device)]
    up d ds = map (update d) ds

    update :: DeviceInfo -> (DeviceID, Device) -> (DeviceID, Device)
    update slave pair@(masterID, master)
      | diAttachment slave == masterID = (masterID, add master slave)
      | otherwise = pair

    add master slave =
        master {slaveDevices = slave: slaveDevices master}

showDevicesMap :: DevicesMap -> String
showDevicesMap m = unlines $ map go $ M.assocs $ masterDevices m
  where
    go (devID, dev) =
           (printf "#%s: %s\n" (show devID) (show $ masterDevice dev))
        ++ (unlines $ map one $ slaveDevices dev)

    one dev = printf " +-- #%s: %s"
                     (show $ diID dev)
                     (show dev)

isMasterDevice :: DeviceID -> DevicesMap -> Bool
isMasterDevice i dmap = i `M.member` masterDevices dmap

deviceById :: DeviceID -> DevicesMap -> Maybe DeviceInfo
deviceById i dmap = M.lookup i (allDevices dmap)

masterDeviceById :: DeviceID -> DevicesMap -> Maybe Device
masterDeviceById i dmap = M.lookup i (masterDevices dmap)

deviceByName :: String -> DevicesMap -> Maybe DeviceInfo
deviceByName name dmap = M.lookup name (byName dmap)