{-# LANGUAGE TemplateHaskell #-}
{- |
Module      : Web.RBB.Crawler.MetaCombiner
Description :  Contract a list of meta data into a single value type
Copyright   :  (c) Sebastian Witte
License     :  BSD3

Maintainer  :  woozletoff@gmail.com
Stability   :  experimental

-}
module Web.RBB.Crawler.MetaCombiner
    where

import           Control.Lens               hiding (Context)
import           Control.Monad.State
import           Data.IxSet                 hiding (null)
import qualified Data.IxSet                 as IxSet
import           Data.Monoid
import           Data.Set                   (Set)
import qualified Data.Set                   as Set
import           Data.Text                  (Text)
import           Web.RBB.Crawler.MetaParser as M
import           Web.RBB.Types.Entry

data MetaDataContractionState = S
    { _context     :: Maybe FilePath
    , _metaDataMap :: IxSet Entry
    }
makeLenses ''MetaDataContractionState

-- | Add the given meta information to the giten 'IxSet'.
contract :: Maybe FilePath -- ^ Content relative path for an 'Entry'
         -> [Meta]         -- ^ Parsed meta information to add
         -> IxSet Entry    -- ^ Original entry 'IxSet'
         -> IxSet Entry
contract initialContext meta m =
    let initialState = S initialContext m
    in execState (mapM_ contract' meta) initialState ^. metaDataMap

contract' :: Meta -> State MetaDataContractionState ()
contract' (Context c) = context .= Just c
contract' None = return ()
contract' meta = maybe (return ()) (updateMetaData meta) =<< use context

updateMetaDataMap :: FilePath
                  -> (Entry -> Entry)
                  -> State MetaDataContractionState ()
updateMetaDataMap c f = do
    m <- use metaDataMap
    let ixC = RelativePath c
    case getOne $ m @= ixC of
        Nothing -> return ()
        Just e -> do
            metaDataMap %= IxSet.deleteIx ixC
            metaDataMap %= IxSet.insert (f e)

updateMetaData :: Meta -> FilePath -> State MetaDataContractionState ()
updateMetaData meta c = case meta of
    M.Tags ts    -> updateMetaDataMap c $ tags %~ updateTags ts
    ~(M.Title t) -> updateMetaDataMap c $ title .~ t

updateTags :: [(TagQuantifier, Text)] -> Set Text -> Set Text
updateTags ts tset
    | null reps = foldr update tset ts
    | otherwise = foldr update mempty ts
  where
    reps = filter ((== TagReplace) . fst) ts

    update (TagRemove, t) = Set.delete t
    update (_,         t) = Set.insert t