{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Request construction
module Network.Lastfm.Request
  ( -- * Request
    Request, R, Ready, Sign, Format(..)
    -- * Request major parameters
  , api, post, get, json, xml, APIKey, apiKey, SessionKey, sessionKey
    -- * Request minor parameters
  , Token, token, Callback, callback
  , Artist, artist, artists, Album, album, MBID, mbid
  , Country, country, Autocorrect, autocorrect
  , Event, event, Status(..), status
  , From, from, To, to, Group, group
  , Language, language, Distance, distance
  , Longitude, longitude, Latitude, latitude, Location, location
  , Start, start, End, end, Festivals, festivalsonly
  , StartTimestamp, startTimestamp, EndTimestamp, endTimestamp
  , Metro, metro, Tag, tags, tag, Track, track, Timestamp , timestamp, Fingerprint, fingerprint
  , AlbumArtist, albumArtist, Duration, duration, TrackNumber, trackNumber
  , Playlist, playlist, Title, title, Description, description
  , ChosenByUser, chosenByUser, Context, context, StreamId, streamId
  , RecentTracks, recentTracks
  , Recipient, recipient, Username, username, User, user, Password, password
  , Public, public, Message, message, Page, page, Limit, limit
  , TaggingType, taggingType, UseRecs, useRecs, Venue, venue, VenueName, venueName
  , Discovery, discovery, RTP, rtp, BuyLinks, buyLinks, Multiplier(..), multiplier
  , Bitrate(..), bitrate, Name, name, Station, station
  , Targeted, comparison, Scrobble, LibraryAlbum, LibraryArtist
  ) where

import Control.Applicative
import Data.Int (Int64)
import Data.Monoid ((<>), mempty)

import qualified Data.Map.Strict as M
import           Data.Text (Text)
import qualified Data.Text as T

import Network.Lastfm.Internal


class Argument a where
  add :: Text -> a -> Request f b
  add k v = wrap $ \r@R { _query = q } -> r { _query = M.insert k (toText v) q }
  {-# INLINE add #-}

  toText :: a -> Text

instance Argument Text where
  toText = id
  {-# INLINE toText #-}

instance Argument Bool where
  toText b = if b then "1" else "0"
  {-# INLINE toText #-}

instance Argument Int64 where
  toText = T.pack . show
  {-# INLINE toText #-}

instance Argument a => Argument [a] where
  toText = T.intercalate "," . map toText
  {-# INLINE toText #-}


data APIKey
data SessionKey
data Token
data Callback


-- | Change request API method
--
-- Primarily used in API call wrappers, not intended for usage by library user
api :: Text -> Request f a
api = add "method"
{-# INLINE api #-}

-- | Change html _method to GET
--
-- Primarily used in API call wrappers, not intended for usage by library user
get :: Request f a
get = wrap $ \r -> r { _method = "GET" }
{-# INLINE get #-}

-- | Change html _method to POST
--
-- Primarily used in API call wrappers, not intended for usage by library user
post :: Request f a
post = wrap $ \r -> r { _method = "POST" }
{-# INLINE post #-}

-- | Change API response format to JSON
--
-- This is a little helper. It's actually enough
-- to specialize Format manually
json :: Request JSON a
json = wrap id
{-# INLINE json #-}

-- | Change API response format to XML
--
-- This is a little helper. It's actually enough
-- to specialize Format manually
xml :: Request XML a
xml = wrap id
{-# INLINE xml #-}

-- | Change request API key
apiKey :: Text -> Request f APIKey
apiKey = add "api_key"
{-# INLINE apiKey #-}

-- | Change request session key
sessionKey :: Text -> Request f SessionKey
sessionKey = add "sk"
{-# INLINE sessionKey #-}

-- | Add token parameter
token :: Text -> Request f Token
token = add "token"
{-# INLINE token #-}

-- | Add callback link parameter
callback :: Text -> Request f Callback
callback = add "cb"
{-# INLINE callback #-}


data Artist
data Album
data MBID
data Country
data Language
data Tag
data Autocorrect
data Page
data Limit
data Message
data Public
data Recipient
data Username
data User
data Password
data Status = Attending | Maybe | NotAttending
data Event
data Festivals
data Longitude
data Latitude
data Location
data Distance
data Metro
data Start
data End
data StartTimestamp
data EndTimestamp
data From
data To
data Playlist
data Title
data Description
data Track
data Timestamp
data Fingerprint
data AlbumArtist
data Context
data StreamId
data Duration
data TrackNumber
data ChosenByUser
data TaggingType
data RecentTracks
data UseRecs
data Scrobble
data Group
data Venue
data VenueName
data Multiplier = M1 | M2
data Bitrate = B64 | B128
data Name
data Station
data Discovery
data RTP
data BuyLinks
data LibraryAlbum
data LibraryArtist


-- | Add artist parameter
artist :: Text -> Request f Artist
artist = add "artist"
{-# INLINE artist #-}

-- | Add artists parameter
artists :: [Text] -> Request f [Artist]
artists = add "artists"
{-# INLINE artists #-}

-- | Add album parameter
album :: Text -> Request f Album
album = add "album"
{-# INLINE album #-}

-- | Add MBID parameter
mbid :: Text -> Request f MBID
mbid = add "mbid"
{-# INLINE mbid #-}

-- | Add country parameter
country :: Text -> Request f Country
country = add "country"
{-# INLINE country #-}

-- | Add language parameter
language :: Text -> Request f Language
language = add "lang"
{-# INLINE language #-}

-- | Add tags parameter
tags :: [Text] -> Request f [Tag]
tags = add "tags"
{-# INLINE tags #-}

-- | Add tag parameter
tag :: Text -> Request f Tag
tag = add "tag"
{-# INLINE tag #-}

-- | Add autocorrect parameter
autocorrect :: Bool -> Request f Autocorrect
autocorrect = add "tags"
{-# INLINE autocorrect #-}

-- | Add page parameter
page :: Int64 -> Request f Page
page = add "page"
{-# INLINE page #-}

-- | Add limit parameter
limit :: Int64 -> Request f Limit
limit = add "limit"
{-# INLINE limit #-}

-- | Add message parameter
message :: Text -> Request f Message
message = add "message"
{-# INLINE message #-}

-- | Add public parameter
public :: Bool -> Request f Public
public = add "public"
{-# INLINE public #-}

-- | Add recipient parameter
recipient :: Text -> Request f Recipient
recipient = add "recipient"
{-# INLINE recipient #-}

-- | Add username parameter
username :: Text -> Request f Username
username = add "username"
{-# INLINE username #-}

-- | Add user parameter
user :: Text -> Request f User
user = add "user"
{-# INLINE user #-}

-- | Add password parameter
password :: Text -> Request f Password
password = add "password"
{-# INLINE password #-}

-- | Add status parameter
status :: Status -> Request f Status
status = add "status" . T.pack . \s -> case s of
  Attending -> "0"
  Maybe     -> "1"
  _         -> "2"
{-# INLINE status #-}

-- | Add event parameter
event :: Int64 -> Request f Event
event = add "event"
{-# INLINE event #-}

-- | Add festivalsonly parameter
festivalsonly :: Bool -> Request f Festivals
festivalsonly = add "festivalsonly"
{-# INLINE festivalsonly #-}

-- | Add longitude parameter
longitude :: Text -> Request f Longitude
longitude = add "longitude"
{-# INLINE longitude #-}

-- | Add latitude parameter
latitude :: Text -> Request f Latitude
latitude = add "latitude"
{-# INLINE latitude #-}

-- | Add location parameter
location :: Text -> Request f Location
location = add "location"
{-# INLINE location #-}

-- | Add distance parameter
distance :: Int64 -> Request f Distance
distance = add "distance"
{-# INLINE distance #-}

-- | Add venue parameter
venue :: Int64 -> Request f Venue
venue = add "venue"
{-# INLINE venue #-}

-- | Add venue parameter
venueName :: Text -> Request f VenueName
venueName = add "venue"
{-# INLINE venueName #-}

-- | Add metro parameter
metro :: Text -> Request f Metro
metro = add "metro"
{-# INLINE metro #-}

-- | Add start parameter
start :: Int64 -> Request f Start
start = add "start"
{-# INLINE start #-}

-- | Add end parameter
end :: Int64 -> Request f End
end = add "end"
{-# INLINE end #-}

-- | Add startTimestamp parameter
startTimestamp :: Int64 -> Request f StartTimestamp
startTimestamp = add "startTimestamp"
{-# INLINE startTimestamp #-}

-- | Add endTimestamp parameter
endTimestamp :: Int64 -> Request f EndTimestamp
endTimestamp = add "endTimestamp"
{-# INLINE endTimestamp #-}

-- | Add from parameter
from :: Int64 -> Request f From
from = add "from"
{-# INLINE from #-}

-- | Add to parameter
to :: Int64 -> Request f To
to = add "to"
{-# INLINE to #-}

-- | Add track parameter
track :: Text -> Request f Track
track = add "track"
{-# INLINE track #-}

-- | Add timestamp parameter
timestamp :: Int64 -> Request f Timestamp
timestamp = add "timestamp"
{-# INLINE timestamp #-}

-- | Add playlistID parameter
playlist :: Int64 -> Request f Playlist
playlist = add "playlistID"
{-# INLINE playlist #-}

-- | Add title parameter
title :: Text -> Request f Title
title = add "title"
{-# INLINE title #-}

-- | Add description parameter
description :: Text -> Request f Description
description = add "description"
{-# INLINE description #-}

-- | Add fingerprint parameter
fingerprint :: Int64 -> Request f Fingerprint
fingerprint = add "fingerprintid"
{-# INLINE fingerprint #-}

-- | Add albumArtist parameter
albumArtist :: Text -> Request f AlbumArtist
albumArtist = add "albumArtist"
{-# INLINE albumArtist #-}

-- | Add context parameter
context :: Text -> Request f Context
context = add "context"
{-# INLINE context #-}

-- | Add streamId parameter
streamId :: Int64 -> Request f StreamId
streamId = add "streamId"
{-# INLINE streamId #-}

-- | Add duration parameter
duration :: Int64 -> Request f Duration
duration = add "duration"
{-# INLINE duration #-}

-- | Add trackNumber parameter
trackNumber :: Int64 -> Request f TrackNumber
trackNumber = add "trackNumber"
{-# INLINE trackNumber #-}

-- | Add chosenByUser parameter
chosenByUser :: Bool -> Request f ChosenByUser
chosenByUser = add "chosenByUser"
{-# INLINE chosenByUser #-}

-- | Add taggingType parameter
taggingType :: Text -> Request f TaggingType
taggingType = add "taggingtype"
{-# INLINE taggingType #-}

-- | Add recentTracks parameter
recentTracks :: Bool -> Request f RecentTracks
recentTracks = add "recentTracks"
{-# INLINE recentTracks #-}

-- | Add useRecs parameter
useRecs :: Bool -> Request f UseRecs
useRecs = add "useRecs"
{-# INLINE useRecs #-}

-- | Add group parameter
group :: Text -> Request f Group
group = add "group"
{-# INLINE group #-}

-- | Add multiplier parameter
multiplier :: Multiplier -> Request f Multiplier
multiplier m = case m of
  M1 -> add "speed_multiplier" (T.pack "1.0")
  M2 -> add "speed_multiplier" (T.pack "2.0")
{-# INLINE multiplier #-}

-- | Add bitrate parameter
bitrate :: Bitrate -> Request f Bitrate
bitrate b = case b of
  B64  -> add "bitrate" (64 :: Int64)
  B128 -> add "bitrate" (128 :: Int64)
{-# INLINE bitrate #-}

-- | Add name parameter
name :: Text -> Request f Name
name = add "name"
{-# INLINE name #-}

-- | Add station parameter
station :: Text -> Request f Station
station = add "station"
{-# INLINE station #-}

-- | Add group parameter
discovery :: Bool -> Request f Discovery
discovery = add "discovery"
{-# INLINE discovery #-}

-- | Add rtp parameter
rtp :: Bool -> Request f RTP
rtp = add "rtp"
{-# INLINE rtp #-}

-- | Add buyLinks parameter
buyLinks :: Bool -> Request f BuyLinks
buyLinks = add "buyLinks"
{-# INLINE buyLinks #-}


class Targeted a where
  target :: Request f a -> Text

instance Targeted [Artist] where
  target _ = "artists"
  {-# INLINE target #-}

instance Targeted User where
  target _ = "user"
  {-# INLINE target #-}

-- | Add comparison parameter
comparison :: Targeted a => Int64 -> Request f a -> Request f a
comparison n t = let z = target t in
  add ("type" <> toText n) z <*> add ("value" <> toText n) (_query (unwrap t (R mempty mempty mempty)) M.! z)
{-# INLINE comparison #-}