module Sound.Freesound.Properties (
    User(..),
    Description(..),
    Statistics(..),
    Tag,
    Properties(..),
    fromXML, listFromXML
) where

import Data.Maybe               (mapMaybe)
import Network.Curl             (URLString)
import Sound.Freesound.Util
import Text.XML.Light           (unqual)
import qualified Text.XML.Light as XML

-- | User of the Freesound database.
data User = User {
    userId   :: Int,
    userName :: String
} deriving (Eq, Show)

-- | Description of a 'Sample', containing user and text.
data Description = Description User String deriving (Eq, Show)

-- | Tag (a single word) for describing a 'Sound'
type Tag = String

-- | Statistics of a 'Sample' in the database.
data Statistics = Statistics {
    downloads :: Int,
    rating    :: (Int, Int)
} deriving (Eq, Show)

-- | Properties of a 'Sample' in the database.
data Properties = Properties {
    sampleId            :: Int,
    
    user                :: User,
    date                :: String,
    originalFileName    :: String,
    statistics          :: Statistics,
    
    image               :: URLString,
    preview             :: URLString,
    colors              :: URLString,

    -- descriptors ??
    -- parent ??
    -- geotag ??
    
    extension           :: String,
    sampleRate          :: Int,
    bitRate             :: Int,
    bitDepth            :: Int,
    channels            :: Int,
    duration            :: Double,
    fileSize            :: Int,
    
    descriptions        :: [Description],
    tags                :: [Tag]
    
    -- comments ??
} deriving (Eq, Show)

-- | Read a 'User' value from an 'XML.Element' in the 'Maybe' monad.
userFromXML :: XML.Element -> Maybe User
userFromXML e = do
    _userId   <- XML.findAttr (unqual "id") e >>= readMaybe
    _userName <- XML.findChild (unqual "name") e >>= return . XML.strContent
    return (User _userId _userName)

-- | Read a rating from an 'XML.Element' in the 'Maybe' monad.
ratingFromXML :: XML.Element -> Maybe (Int, Int)
ratingFromXML e = do
    count <- findAttr "count" e >>= readMaybe
    value <- strContent e >>= readMaybe
    return (count, value)
    
-- | Read a 'Statistics' value from an 'XML.Element' in the 'Maybe' monad.
statisticsFromXML :: XML.Element -> Maybe Statistics
statisticsFromXML e = do
    _downloads <- findChild "downloads" e >>= strContent >>= readMaybe
    _rating    <- findChild "rating" e >>= ratingFromXML
    return (Statistics _downloads _rating)

-- | Read a 'Description' value from an 'XML.Element' in the 'Maybe' monad.
descriptionFromXML :: XML.Element -> Maybe Description
descriptionFromXML e = do
    _user <- userFromXML e
    _text <- findChild "text" e >>= strContent
    return (Description _user _text)

-- | Read a list of 'Description's from an 'XML.Element' in the 'Maybe' monad.
descriptionsFromXML :: XML.Element -> Maybe [Description]
descriptionsFromXML = return . mapMaybe descriptionFromXML . findChildren "description"

-- | Read a list of 'Tag's from an 'XML.Element' in the 'Maybe' monad.
tagsFromXML :: XML.Element -> Maybe [Tag]
tagsFromXML = return . map XML.strContent . findChildren "tag"

-- | Read a 'Properties' value from an 'XML.Element' in the 'Maybe' monad.
fromXML :: XML.Element -> Maybe Properties
fromXML e = do
    _sampleId           <- findAttr "id" e >>= readMaybe
    _user               <- findChild "user" e >>= userFromXML
    _date               <- findChild "date" e >>= strContent
    _originalFileName   <- findChild "originalFilename" e >>= strContent
    _statistics         <- findChild "statistics" e >>= statisticsFromXML
    _image              <- findChild "image" e >>= strContent
    _preview            <- findChild "preview" e >>= strContent
    _colors             <- findChild "colors" e >>= strContent
    _extension          <- findChild "extension" e >>= strContent
    _sampleRate         <- findChild "samplerate" e >>= strContent >>= readMaybe
    _bitRate            <- findChild "bitrate" e  >>= strContent >>= readMaybe
    _bitDepth           <- findChild "bitdepth" e >>= strContent >>= readMaybe
    _channels           <- findChild "channels" e >>= strContent >>= readMaybe
    _duration           <- findChild "duration" e >>= strContent >>= readMaybe
    _fileSize           <- findChild "filesize" e >>= strContent >>= readMaybe
    _descriptions       <- findChild "descriptions" e >>= descriptionsFromXML
    _tags               <- findChild "tags" e >>= tagsFromXML
    return $ Properties {
        sampleId            = _sampleId,

        user                = _user,
        date                = _date,
        originalFileName    = _originalFileName,
        statistics          = _statistics,

        image               = _image,
        preview             = _preview,
        colors              = _colors,

        extension           = _extension,
        sampleRate          = _sampleRate,
        bitRate             = _bitRate,
        bitDepth            = _bitDepth,
        channels            = _channels,
        duration            = _duration,
        fileSize            = _fileSize,

        descriptions        = _descriptions,
        tags                = _tags
    }

-- | Read a list of 'Properties' from an 'XML.Element'.
listFromXML :: XML.Element -> [Properties]
listFromXML = mapMaybe fromXML . XML.findChildren (unqual "sample")