waldo-0: src/Waldo/Story.hs
{-# LANGUAGE OverloadedStrings #-}
module Waldo.Story (
selectStory
, knapsackSizer
, Story(..)
, StoryGuard, StoryOption
, isIn, browserIs, osIs, netSpeedIs, orgIs, orgMatch, ispIs
, refererDomainIs
, pdTestJustIs
, giveThem, allocate
) where
import Data.Maybe
import Data.List
import Control.Monad.Reader
import Data.ByteString.Char8 (ByteString)
import qualified Data.Text as T
import Network.URI
import Text.Regex.TDFA ((=~))
import Waldo.Stalk
import Waldo.Script
type StoryGuard = ReaderT PersonalData Maybe ()
type StoryOption = ReaderT PersonalData Maybe Story
data Story =
StoryGoto {
storyGoto :: T.Text
}
| Story {
storyAltText :: T.Text
, storyPanelSets :: [PanelSizes]
, storyPadX :: Int
, storyPadY :: Int
, storyName :: T.Text
}
deriving (Show)
selectStory :: ((Int, Int) -> Story -> Maybe Script) -> Script -> [StoryOption] -> PersonalData -> IO Script
selectStory sizer d storyGens pd = do
--print storyGens
-- generate stories
let stories = catMaybes $ map (flip runReaderT pd) storyGens
--print stories
-- size the selected scripts
let scripts = mapMaybe doSize stories
--print scripts
-- Get our script, either the default or a selected one.
return $ fromMaybe d $ listToMaybe scripts
where
doSize (s@Story {}) = sizer (pdScreen pd) s
doSize (StoryGoto t) = Just (ScriptTo t)
knapsackSizer :: Int -> (Int, Int) -> Story -> Maybe Script
knapsackSizer sitePad (w, h) s =
-- Get the first entry if there is one, the smallest if none of them fit.
listToMaybe $ (sortCorrectDir sizeLimited) ++ (take 1 areaSortedSized)
where
-- selected sort dir by what we know about the screen
sortCorrectDir =
if (h > 0) && (w > 0)
then reverse
else id
-- The fitting scripts
sizeLimited = fitWidth $ fitHeight $ areaSortedSized
-- sort by area
areaSortedSized = areaSort allScripts
-- Of all scripts
allScripts = do
combo <- mapM id $ storyPanelSets s
return $ mkScript (storyName s) (storyAltText s) combo
areaSort = sortBy (\a b -> compare (scriptArea a) (scriptArea b))
scriptArea scr = (sHeight scr) * (sWidth scr)
fitHeight scripts =
if h > 0
then filter (\scr -> h > (sHeight scr+storyPadY s+sitePad)) scripts
else scripts
fitWidth scripts =
if w > 0
then filter (\scr -> w > (sWidth scr+storyPadX s+sitePad)) scripts
else scripts
refererDomainIs :: String -> StoryGuard
refererDomainIs d =
asks pdRefURI >>= guard . fromMaybe False . fmap ((isSuffixOf d) . uriRegName) . join . fmap uriAuthority
--refererMatches ::
pdTestJustIs :: Eq a => (PersonalData -> Maybe a) -> a -> StoryGuard
pdTestJustIs g v = asks g >>= guard . maybe False (v==)
isIn :: ByteString -> StoryGuard
isIn locName = asks pdLocal >>= guard . (not . null . (filter (locName==)))
browserIs :: Browser -> StoryGuard
browserIs b = asks pdBrowser >>= guard . (maybe False (b==))
osIs :: OS -> StoryGuard
osIs os = asks pdOS >>= guard . (maybe False (os==))
netSpeedIs :: NetSpeed -> StoryGuard
netSpeedIs ns = asks pdNetSpeed >>= guard . (maybe False (ns==))
orgIs :: ByteString -> StoryGuard
orgIs o = asks pdOrg >>= guard . (maybe False (o==))
orgMatch :: ByteString -> StoryGuard
orgMatch o = asks pdOrg >>= guard . (maybe False (flip (=~) o))
ispIs :: ByteString -> StoryGuard
ispIs i = asks pdISP >>= guard . (maybe False (i==))
allocate :: MonadPlus m => m () -> a -> m a
allocate cnd r = cnd >> return r
giveThem :: MonadPlus m => m () -> m a -> m a
giveThem = (>>)