{-# LANGUAGE CPP, OverloadedStrings #-}
module Sound.Freesound.Search.Filter (
  Filters
, id
, username
, created
, originalFilename
, description
, tag
, license
, isRemix
, wasRemixed
, pack
, isGeotagged
, fileType
, duration
, bitdepth
, bitrate
, samplerate
, filesize
, channels
, md5
, numDownloads
, avgRating
, numRatings
, comment
, comments
) where

import           Data.Default (Default(..))
import           Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS
import           Data.Maybe (mapMaybe)
import           Data.Text (Text)
import           Network.HTTP.Types.QueryLike (QueryValueLike(..))
import           Prelude hiding (id)
import           Sound.Freesound.Search.Numerical (Numerical)
import           Sound.Freesound.Sound.Type (FileType, SoundId)

#if __GLASGOW_HASKELL__ < 710
import           Data.Monoid (Monoid(..))
#endif

data License = Attribution | AttributionNoncommercial | CreativeCommons0 deriving (Eq, Show)

instance QueryValueLike License where
  toQueryValue Attribution              = toQueryValue $ BS.pack "Attribution"
  toQueryValue AttributionNoncommercial = toQueryValue $ BS.pack "Attribution Noncommercial"
  toQueryValue CreativeCommons0         = toQueryValue $ BS.pack "Creative Commons 0"

-- | Newtype wrapper for avoiding orphan instance.
newtype F_Bool = F_Bool Bool deriving (Eq, Show)

instance QueryValueLike F_Bool where
  toQueryValue (F_Bool True)  = toQueryValue $ BS.pack "true"
  toQueryValue (F_Bool False) = toQueryValue $ BS.pack "false"

data Filter =
    F_id (Numerical SoundId)  -- integer, sound id on freesound
  | F_username Text  -- string, not tokenized
  | F_created Text  -- date
  | F_original_filename Text --  string, tokenized
  | F_description Text -- string, tokenized
  | F_tag Text
  | F_license License
  | F_is_remix F_Bool
  | F_was_remixed F_Bool
  | F_pack Text
  --pack_tokenized:  string, tokenized
  | F_is_geotagged F_Bool
  | F_type FileType
  | F_duration (Numerical Double) -- duration of sound in seconds
  | F_bitdepth (Numerical Integer) -- WARNING is not to be trusted right now
  | F_bitrate (Numerical Integer) -- WARNING is not to be trusted right now
  | F_samplerate (Numerical Integer)
  | F_filesize (Numerical Integer) -- file size in bytes
  | F_channels (Numerical Integer) -- number of channels in sound, mostly 1 or 2, sometimes more
  | F_md5 Text -- 32-byte md5 hash of file
  | F_num_downloads (Numerical Integer) -- all zero right now (not imported data)
  | F_avg_rating (Numerical Double) -- average rating, from 0 to 5
  | F_num_ratings (Numerical Integer) -- number of ratings
  | F_comment Text -- tokenized
  | F_comments (Numerical Integer) -- number of comments
  deriving (Eq, Show)

mkF :: QueryValueLike a => ByteString -> a -> Maybe ByteString
mkF t = fmap (BS.append (BS.append t ":")) . toQueryValue

instance QueryValueLike Filter where
  toQueryValue filterSpec =
    case filterSpec of
      F_id x -> mkF "id" x
      F_username x -> mkF "username" x
      F_created x -> mkF "created" x
      F_original_filename x -> mkF "original_filename" x
      F_description x -> mkF "description" x
      F_tag x -> mkF "tag" x
      F_license x -> mkF "license" x
      F_is_remix x -> mkF "is_remix" x
      F_was_remixed x -> mkF "was_remixed" x
      F_pack x -> mkF "pack" x
      --F_pack_tokenized: string, tokenized
      F_is_geotagged x -> mkF "is_geotagged" x
      F_type x -> mkF "type" x
      F_duration x -> mkF "duration" x
      F_bitdepth x -> mkF "bitdepth" x
      F_bitrate x -> mkF "bitrate" x
      F_samplerate x -> mkF "samplerate" x
      F_filesize x -> mkF "filesize" x
      F_channels x -> mkF "channels" x
      F_md5 x -> mkF "md5" x
      F_num_downloads x -> mkF "num_downloads" x
      F_avg_rating x -> mkF "avg_rating" x
      F_num_ratings x -> mkF "num_ratings" x
      F_comment x -> mkF "comment" x
      F_comments x -> mkF "comments" x

newtype Filters = Filters [Filter] deriving (Show)

instance Monoid Filters where
  mempty = Filters []
  mappend (Filters a) (Filters b) = Filters (a++b)

instance Default Filters where
  def = mempty

instance QueryValueLike Filters where
  toQueryValue (Filters []) = Nothing
  toQueryValue (Filters fs) = Just $ BS.unwords $ mapMaybe toQueryValue fs

fromFilter :: Filter -> Filters
fromFilter = Filters . (:[])

-- | Sound id on Freesound.
id :: Numerical SoundId -> Filters
id = fromFilter . F_id

username :: Text -> Filters
username = fromFilter . F_username

created :: Text -> Filters
created = fromFilter . F_created

originalFilename :: Text -> Filters
originalFilename = fromFilter . F_original_filename

description :: Text -> Filters
description = fromFilter . F_description

tag :: Text -> Filters
tag = fromFilter . F_tag

license :: License -> Filters
license = fromFilter . F_license

isRemix :: Bool -> Filters
isRemix = fromFilter . F_is_remix . F_Bool

wasRemixed :: Bool -> Filters
wasRemixed = fromFilter . F_was_remixed . F_Bool

pack :: Text -> Filters
pack = fromFilter . F_pack

isGeotagged :: Bool -> Filters
isGeotagged = fromFilter . F_is_geotagged . F_Bool

fileType :: FileType -> Filters
fileType = fromFilter . F_type

-- | Duration of sound in seconds.
duration :: Numerical Double -> Filters
duration = fromFilter . F_duration

-- | WARNING is not to be trusted right now.
bitdepth :: Numerical Integer -> Filters
bitdepth = fromFilter . F_bitdepth

-- | WARNING is not to be trusted right now.
bitrate :: Numerical Integer -> Filters
bitrate = fromFilter . F_bitrate

samplerate :: Numerical Integer -> Filters
samplerate = fromFilter . F_samplerate

-- | File size in bytes.
filesize :: Numerical Integer -> Filters
filesize = fromFilter . F_filesize

-- | Number of channels in sound, mostly 1 or 2, sometimes more.
channels :: Numerical Integer -> Filters
channels = fromFilter . F_channels

-- | 32-byte md5 hash of file.
md5 :: Text -> Filters
md5 = fromFilter . F_md5

-- | All zero right now (not imported data).
numDownloads :: Numerical Integer -> Filters
numDownloads = fromFilter . F_num_downloads

-- | Average rating, from 0 to 5.
avgRating :: Numerical Double -> Filters
avgRating = fromFilter . F_avg_rating

-- | Number of ratings.
numRatings :: Numerical Integer -> Filters
numRatings = fromFilter . F_num_ratings

comment :: Text -> Filters
comment = fromFilter . F_comment

-- | Number of comments.
comments :: Numerical Integer -> Filters
comments = fromFilter . F_comments