{-# LANGUAGE DeriveDataTypeable #-}

-- | Scrobbling data types.

module Scrobble.Types where

import Control.Exception
import Data.Data
import Data.Time
import Network
import Network.URI (URI)

-- | Server configuration.
data Config = Config
  { cfgPort   :: PortNumber      -- ^ Port to listen on.
  , cfgHost   :: String          -- ^ Host name used for server (probably just localhost).
  , cfgExpire :: NominalDiffTime -- ^ Number of seconds of inactivity before a session expires.
  } deriving (Show)

-- | Event handlers.
data Handlers = Handlers
  { handleHandshake   :: Session -> IO ()                   -- ^ Initial connection hand-shake.
  , handleExpire      :: Session -> IO ()                   -- ^ Session expiry.
  , handleNowPlaying  :: Session -> NowPlaying -> IO ()     -- ^ Now-playing notification.
  , handleSubmissions :: Session -> [Submission] -> IO Bool -- ^ Played tracks submission.
  }

-- | A scrobbling session.
data Session = Session
  { sesHandshake :: Bool    -- ^ Does the session require handshake?
  , sesVersion   :: String  -- ^ Version of the protocol.
  , sesClientId  :: String  -- ^ Client (music player's) id.
  , sesClientVer :: String  -- ^ Client version.
  , sesUser      :: String  -- ^ Username.
  , sesTimestamp :: UTCTime -- ^ Timestamp of connection.
  , sesToken     :: String  -- ^ Session token.
  } deriving (Show)

-- | A now playing track.
data NowPlaying = NowPlaying
  { npArtist      :: String        -- ^ Artist name.
  , npTrack       :: String        -- ^ Track title.
  , npAlbum       :: Maybe String  -- ^ Album name (if any).
  , npLength      :: Maybe Integer -- ^ Track length in seconds (if known).
  , npPosition    :: Maybe Integer -- ^ Track position (if known).
  , npMusicBrainz :: Maybe String  -- ^ MusicBrainz track id (if known).
  } deriving (Show)

-- | A track submission.
data Submission = Submission
  { subArtist      :: String        -- ^ Artist name.
  , subTrack       :: String        -- ^ Track title.
  , subTimestamp   :: UTCTime       -- ^ Track timestamp.
  , subSource      :: Source        -- ^ Source of track.
  , subRating      :: Maybe Rating  -- ^ Rating (if any).
  , subLength      :: Maybe Integer -- ^ Track length (if any).
  , subAlbum       :: Maybe String  -- ^ Album (if any).
  , subPosition    :: Maybe Integer -- ^ Track position in album (if any).
  , subMusicBrainz :: Maybe String  -- ^ MusicBrainz track id (if any).
  } deriving (Show)

-- | A rating of a track.
--
-- Note: Currently, a web-service must also be called to set love/ban
-- status. We anticipate that this will be phased out soon, and the
-- submission service will handle the whole process.
data Rating
  = Love -- ^ Love (on any mode if the user has manually loved the
         -- track). This implies a listen.

  | Ban -- ^ Ban (only if source=L). This implies a skip, and the
        -- client should skip to the next track when a ban happens.

  | Skip -- ^ Skip (only if source=L).

  deriving (Enum,Eq,Show,Read)

-- | The source of the track. Required, must be one of the following
-- codes:
--
-- Please note, for the time being, sources other than P and L are not
-- supported.
data Source
  = UserChosen -- ^ Chosen by the user

  | NonPersonlizedBroadcast -- ^ Non-personalised broadcast
                            -- (e.g. Shoutcast, BBC Radio 1)

  | Personalized -- ^ Personalised recommendation except Last.fm
                 -- (e.g. Pandora, Launchcast)

  | LastFm -- ^ Last.fm (any mode). In this case, the 5-digit Last.fm
           -- recommendation key must be appended to this source ID to
           -- prove the validity of the submission (for example,
           -- "o[0]=L1b48a").

  | Unknown -- ^ Source unknown.

  deriving (Show,Enum,Eq,Read)

-- | Server response.
data Response = OK | BANNED | BADAUTH | FAILED String | BADSESSION
  deriving Show

-- | A scrobbling client.
data Client = Client
  { cliToken      :: String -- ^ Session token.
  , cliNowPlaying :: URI    -- ^ Now playing URL to submit to.
  , cliSubmit     :: URI    -- ^ URL to submit listened tracks to.
  } deriving (Show)

-- | Details for creating a scrobbling client.
data Details = Details
  { detPassword :: String
  , detUsername :: String
  , detClient :: String -- ^ E.g. “qlb”.
  , detVersion :: String -- ^ E.g. “0.9.2”.
  , detServer :: URI -- ^ See defaultServer in "Scrobble.Client".
  } deriving (Show)

-- | Scrobble exception.
data ScrobblerError
  = ScrobblerBanned
  | ScrobblerBadAuth
  | ScrobblerBadTime
  | ScrobblerFailed String
  | ScrobblerHardFail
  | ScrobblerSubmitFail String
  | ScrobblerNowPlayingFail String
  deriving (Show,Typeable,Data)
instance Exception ScrobblerError