{-# LANGUAGE OverloadedStrings #-}

module Network.Protocol.MusicBrainz.Types (
    MBID(..)
  , Release(..)
  , TextRepresentation(..)
  , Medium(..)
  , Track(..)
  , Recording(..)
  , ArtistCredit(..)
  , Artist(..)
  , ReleaseGroup(..)
  , LabelInfo(..)
  , Label(..)
  , ReleaseEvent(..)
  , Area(..)
  , ISO3166Code(..)
  , CoverArtArchive(..)
) where

import Data.Text (Text)
import Data.Time.Calendar (Day)
import Data.Vector (Vector)

import Control.Applicative ((<$>), (<*>))
import Control.Monad (mzero)
import Data.Aeson (FromJSON(..), (.:), (.:?), Value(..))
import Data.Maybe (fromMaybe)
import qualified Data.Text as T
import Data.Time.Format (parseTimeM)
import Data.Time.Locale.Compat (defaultTimeLocale)

newtype MBID = MBID { unMBID :: Text }
    deriving (Eq, Show)

data Release = Release {
    _releaseId :: MBID
  , _releaseTitle :: Text
  , _releaseStatus :: Maybe Text
  , _releaseQuality :: Maybe Text
  , _releasePackaging :: Maybe Text
  , _releaseTextRepresentation :: Maybe TextRepresentation
  , _releaseArtistCredit :: [ArtistCredit]
  , _releaseDate :: Maybe Day
  , _releaseCountry :: Maybe Text
  , _releaseEvents :: [ReleaseEvent]
  , _releaseBarcode :: Maybe Text
  , _releaseASIN :: Maybe Text
  , _releaseCoverArtArchive :: Maybe CoverArtArchive
  , _releaseMedia :: Vector Medium
} deriving (Eq, Show)

instance FromJSON Release where
    parseJSON (Object v) = Release <$>
                                 (MBID <$> v .: "id") <*>
                                 v .: "title" <*>
                                 v .:? "status" <*>
                                 v .:? "quality" <*>
                                 v .:? "packaging" <*>
                                 v .:? "text-representation" <*>
                                 v .: "artist-credit" <*>
                                 ((parseTimeM True defaultTimeLocale "%Y-%m-%d" . T.unpack =<<) <$> v .:? "date") <*>
                                 v .:? "country" <*>
                                 v .: "release-events" <*>
                                 v .:? "barcode" <*>
                                 v .:? "asin" <*>
                                 v .:? "cover-art-archive" <*>
                                 v .: "media"
    parseJSON _          = mzero

data TextRepresentation = TextRepresentation {
    _textRepLanguage :: Maybe Text
  , _textRepScript :: Maybe Text
} deriving (Eq, Show)

instance FromJSON TextRepresentation where
    parseJSON (Object v) = TextRepresentation <$>
                                 v .:? "language" <*>
                                 v .:? "script"
    parseJSON _          = mzero

data Medium = Medium {
    _mediumTitle :: Maybe Text
  , _mediumPosition :: Maybe Integer
  , _mediumFormat :: Maybe Text
  , _mediumTrackCount :: Integer
  , _mediumTrackOffset :: Maybe Integer
  , _mediumTrackList :: Maybe [Track]
} deriving (Eq, Show)

instance FromJSON Medium where
    parseJSON (Object v) = Medium <$>
                                 v .:? "title" <*>
                                 v .:? "position" <*>
                                 v .:? "format" <*>
                                 v .:  "track-count" <*>
                                 v .:? "track-offset" <*>
                                 v .:? "tracks"
    parseJSON _          = mzero

data Track = Track {
    _trackId :: MBID
  , _trackArtistCredit :: [ArtistCredit]
  , _trackPosition :: Maybe Integer
  , _trackNumber :: Maybe Text
  , _trackLength :: Maybe Integer
  , _trackRecording :: Recording
} deriving (Eq, Show)

instance FromJSON Track where
    parseJSON (Object v) = Track <$>
                                 (MBID <$> v .: "id") <*>
                                 v .: "artist-credit" <*>
                                 v .:? "position" <*>
                                 v .:? "number" <*>
                                 v .:? "length" <*>
                                 v .: "recording"
    parseJSON _          = mzero

data Recording = Recording {
    _recordingId :: MBID
  , _recordingTitle :: Maybe Text
  , _recordingLength :: Maybe Integer
  , _recordingArtistCredit :: [ArtistCredit]
} deriving (Eq, Show)

instance FromJSON Recording where
    parseJSON (Object v) = Recording <$>
                                 (MBID <$> v .: "id") <*>
                                 v .:? "title" <*>
                                 v .:? "length" <*>
                                 v .: "artist-credit"
    parseJSON _          = mzero

data ArtistCredit = ArtistCredit {
    _artistCreditArtist :: Artist
  , _artistCreditJoinPhrase :: Maybe Text
  , _artistCreditName :: Maybe Text
} deriving (Eq, Show)

instance FromJSON ArtistCredit where
    parseJSON (Object v) = ArtistCredit <$>
                                 v .: "artist" <*>
                                 v .:? "joinphrase" <*>
                                 v .:? "name"
    parseJSON _          = mzero

data Artist = Artist {
    _artistId :: MBID
  , _artistName :: Maybe Text
  , _artistSortName :: Maybe Text
  , _artistDisambiguation :: Maybe Text
} deriving (Eq, Show)

instance FromJSON Artist where
    parseJSON (Object v) = Artist <$>
                                 (MBID <$> v .: "id") <*>
                                 v .:? "name" <*>
                                 v .:? "sort-name" <*>
                                 v .:? "disambiguation"
    parseJSON _          = mzero

data ReleaseGroup = ReleaseGroup {
    _releaseGroupId :: MBID
  , _releaseGroupType :: Text
  , _releaseGroupTitle :: Maybe Text
  , _releaseGroupFirstReleaseDate :: Maybe Text
  , _releaseGroupPrimaryType :: Maybe Text
  , _releaseGroupArtistCredit :: [ArtistCredit]
} deriving (Eq, Show)

data LabelInfo = LabelInfo {
    _labelInfoCatalogNumber :: Maybe Text
  , _labelInfoLabel :: Label
} deriving (Eq, Show)

data Label = Label {
    _labelId :: MBID
  , _labelName :: Maybe Text
  , _labelSortName :: Maybe Text
  , _labelLabelCode :: Maybe Text
} deriving (Eq, Show)

data ReleaseEvent = ReleaseEvent {
    _releaseEventDate :: Maybe Day
  , _releaseEventArea :: Maybe Area
} deriving (Eq, Show)

instance FromJSON ReleaseEvent where
    parseJSON (Object v) = ReleaseEvent <$>
                                 ((parseTimeM True defaultTimeLocale "%Y-%m-%d" . T.unpack =<<) <$> v .:? "date") <*>
                                 v .:? "area"
    parseJSON _          = mzero

data Area = Area {
    _areaId :: MBID
  , _areaName :: Maybe Text
  , _areaSortName :: Maybe Text
  , _areaISO3166_1Codes :: [ISO3166Code]
  , _areaISO3166_2Codes :: [ISO3166Code]
  , _areaISO3166_3Codes :: [ISO3166Code]
} deriving (Eq, Show)

instance FromJSON Area where
    parseJSON (Object v) = Area <$>
                                 (MBID <$> v .: "id") <*>
                                 v .:? "name" <*>
                                 v .:? "sort-name" <*>
                                 (fromMaybe [] <$> v .:? "iso_3166_1_codes") <*>
                                 (fromMaybe [] <$> v .:? "iso_3166_2_codes") <*>
                                 (fromMaybe [] <$> v .:? "iso_3166_3_codes")
    parseJSON _          = mzero

data CoverArtArchive = CoverArtArchive {
    _coverArtArchiveArtwork :: Maybe Bool
  , _coverArtArchiveCount :: Maybe Integer
  , _coverArtArchiveFront :: Maybe Bool
  , _coverArtArchiveBack :: Maybe Bool
} deriving (Eq, Show)

instance FromJSON CoverArtArchive where
    parseJSON (Object v) = CoverArtArchive <$>
                                 v .:? "artwork" <*>
                                 v .:? "count" <*>
                                 v .:? "front" <*>
                                 v .:? "back"
    parseJSON _          = mzero

newtype ISO3166Code = ISO3166Code { unISO3166Code :: Text }
    deriving (Eq, Show)

instance FromJSON ISO3166Code where
    parseJSON t = ISO3166Code <$> parseJSON t