packages feed

vision-0.0.2.2: src/Index.hs

-- -*-haskell-*-
--  Vision (for the Voice): an XMMS2 client.
--
--  Author:  Oleg Belozeorov
--  Created: 20 Jul. 2010
--
--  Copyright (C) 2010 Oleg Belozeorov
--
--  This program is free software; you can redistribute it and/or
--  modify it under the terms of the GNU General Public License as
--  published by the Free Software Foundation; either version 3 of
--  the License, or (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
--  General Public License for more details.
--

{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}

module Index
  ( Index
  , makeIndex
  , getInfo
  , addToIndex
  , clearIndex
  ) where

import Control.Concurrent.MVar
import Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap

import Graphics.UI.Gtk hiding (add)

import XMMS2.Client

import Medialib hiding (getInfo)
import Handler


data IndexEntry i
  = IENone
  | IERetrieving
  | IEReady Stamp MediaInfo i

data Index i
  = Index { iTable :: MVar (IntMap (IndexEntry i, [TreeRowReference]))
          , iStore :: ListStore MediaId
          , iConv  :: MediaInfo -> IO i
          }

makeIndex store conv = do
  table <- newMVar IntMap.empty
  let index = Index { iTable = table
                    , iStore = store
                    , iConv  = conv
                    }

  onMediaInfo . add . ever $ handleInfo index

  return index

handleInfo index (id, stamp, info) = do
  let id' = fromIntegral id
  modifyMVar_ (iTable index) $ \ix ->
    case IntMap.lookup id' ix of
      Just (entry, list) ->
        updateIndex index ix id' stamp info entry list
      Nothing            ->
        return ix

updateIndex index ix id stamp info old list =
  if upd
  then do
    ti <- (iConv index) info
    mapM_ (touch index) list
    return $ IntMap.insert id (IEReady stamp info ti, list) ix
  else
    return ix
  where upd = case old of
          IEReady oldStamp _ _ -> stamp /= oldStamp
          _                    -> True

touch index ref = do
  path <- treeRowReferenceGetPath ref
  case path of
    [n] -> do
      Just iter <- treeModelGetIter (iStore index) [n]
      treeModelRowChanged (iStore index) [n] iter
    _   ->
      return ()

getInfo index id force = do
  let id' = fromIntegral id
  modifyMVar (iTable index) $ \ix ->
    case IntMap.lookup id' ix of
      Just (IEReady _ _ info, _) ->
        return (ix, Just info)
      Just (IENone, list) | force -> do
        requestInfo id
        return (IntMap.insert id'(IERetrieving, list) ix, Nothing)
      _ ->
        return (ix, Nothing)

addToIndex index id n =
  modifyMVar_ (iTable index) $ \ix -> do
    Just ref <- treeRowReferenceNew (iStore index) [n]
    return $ IntMap.insertWith iw (fromIntegral id) (IENone, [ref]) ix
  where iw (_, [new]) (entry, old) = (entry, new : old)

clearIndex index =
  modifyMVar_ (iTable index) . const $ return IntMap.empty