-- | Add a device
deviceAdd :: DeviceManager -> DevicePath -> Maybe KernelEvent -> Sys ()
deviceAdd dm path mev = do

   let rpath = "devices" ++ Text.unpack path  -- relative path in sysfs

   (msubsystem,mdev) <- case mev of
      Nothing -> sysfsReadDev (dmSysFS dm) rpath
      -- device id may be read from the event properties
      Just ev -> do
         let 
            detail k    = Map.lookup (Text.pack k) (kernelEventDetails ev)
            detailNum k = fmap (read . Text.unpack) (detail k)
         case (detailNum "MAJOR", detailNum "MINOR") of
            (Just ma, Just mi) -> do
               sub <- sysfsReadSubsystem (dmSysFS dm) rpath
               return (sub, (`sysfsMakeDev` DeviceID ma mi) <$> sub)
            _                  -> sysfsReadDev (dmSysFS dm) rpath

   node <- deviceTreeCreate msubsystem mdev Map.empty

   atomically $ do
      -- update the tree
      tree  <- readTVar (dmDevices dm)
      tree' <- deviceTreeInsert path node tree
      writeTVar (dmDevices dm) tree'

      case msubsystem of
         Nothing        -> return ()
         Just subsystem -> do
            -- Add device into subsystem index
            subs <- readTVar (dmSubsystems dm)
            subs' <- case Map.lookup subsystem subs of

               Nothing    -> do
                  -- create new index
                  index <- SubsystemIndex (Set.singleton path)
                              <$> newBroadcastTChan
                              <*> newBroadcastTChan
                  -- signal the new subsystem
                  writeTChan (dmOnSubsystemAdd dm) subsystem
                  -- return the new index
                  return (Map.insert subsystem index subs)

               Just index -> do
                  let
                     devs   = subsystemDevices index
                     devs'  = Set.insert path devs
                     index' = index { subsystemDevices = devs' }
                  -- signal the addition
                  writeTChan (subsystemOnAdd index) path
                  -- return the new index
                  return (Map.insert subsystem index' subs)

            writeTVar (dmSubsystems dm) subs'

-- | Remove a device
deviceAdd :: DeviceManager -> DevicePath -> Maybe KernelEvent -> Sys ()
deviceAdd dm path mev = do

   let rpath = "devices" ++ Text.unpack path  -- relative path in sysfs

   (msubsystem,mdev) <- case mev of
      Nothing -> sysfsReadDev (dmSysFS dm) rpath
      -- device id may be read from the event properties
      Just ev -> do
         let 
            detail k    = Map.lookup (Text.pack k) (kernelEventDetails ev)
            detailNum k = fmap (read . Text.unpack) (detail k)
         case (detailNum "MAJOR", detailNum "MINOR") of
            (Just ma, Just mi) -> do
               sub <- sysfsReadSubsystem (dmSysFS dm) rpath
               return (sub, (`sysfsMakeDev` DeviceID ma mi) <$> sub)
            _                  -> sysfsReadDev (dmSysFS dm) rpath

   node <- deviceTreeCreate msubsystem mdev Map.empty

   atomically $ do
      -- update the tree
      tree  <- readTVar (dmDevices dm)
      tree' <- deviceTreeInsert path node tree
      writeTVar (dmDevices dm) tree'

      case msubsystem of
         Nothing        -> return ()
         Just subsystem -> do
            -- Add device into subsystem index
            subs <- readTVar (dmSubsystems dm)
            subs' <- case Map.lookup subsystem subs of

               Nothing    -> do
                  -- create new index
                  index <- SubsystemIndex (Set.singleton path)
                              <$> newBroadcastTChan
                              <*> newBroadcastTChan
                  -- signal the new subsystem
                  writeTChan (dmOnSubsystemAdd dm) subsystem
                  -- return the new index
                  return (Map.insert subsystem index subs)

               Just index -> do
                  let
                     devs   = subsystemDevices index
                     devs'  = Set.insert path devs
                     index' = index { subsystemDevices = devs' }
                  -- signal the addition
                  writeTChan (subsystemOnAdd index) path
                  -- return the new index
                  return (Map.insert subsystem index' subs)

            writeTVar (dmSubsystems dm) subs'

-- | Remove a device
deviceAdd dm path mev = do

   let rpath = "devices" ++ Text.unpack path  -- relative path in sysfs

   (msubsystem,mdev) <- case mev of
      Nothing -> sysfsReadDev (dmSysFS dm) rpath
      -- device id may be read from the event properties
      Just ev -> do
         let 
            detail k    = Map.lookup (Text.pack k) (kernelEventDetails ev)
            detailNum k = fmap (read . Text.unpack) (detail k)
         case (detailNum "MAJOR", detailNum "MINOR") of
            (Just ma, Just mi) -> do
               sub <- sysfsReadSubsystem (dmSysFS dm) rpath
               return (sub, (`sysfsMakeDev` DeviceID ma mi) <$> sub)
            _                  -> sysfsReadDev (dmSysFS dm) rpath

   node <- deviceTreeCreate msubsystem mdev Map.empty

   atomically $ do
      -- update the tree
      tree  <- readTVar (dmDevices dm)
      tree' <- deviceTreeInsert path node tree
      writeTVar (dmDevices dm) tree'

      case msubsystem of
         Nothing        -> return ()
         Just subsystem -> do
            -- Add device into subsystem index
            subs <- readTVar (dmSubsystems dm)
            subs' <- case Map.lookup subsystem subs of

               Nothing    -> do
                  -- create new index
                  index <- SubsystemIndex (Set.singleton path)
                              <$> newBroadcastTChan
                              <*> newBroadcastTChan
                  -- signal the new subsystem
                  writeTChan (dmOnSubsystemAdd dm) subsystem
                  -- return the new index
                  return (Map.insert subsystem index subs)

               Just index -> do
                  let
                     devs   = subsystemDevices index
                     devs'  = Set.insert path devs
                     index' = index { subsystemDevices = devs' }
                  -- signal the addition
                  writeTChan (subsystemOnAdd index) path
                  -- return the new index
                  return (Map.insert subsystem index' subs)

            writeTVar (dmSubsystems dm) subs'

-- | Remove a device