{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings  #-}
module Stackage.Metadata
    ( PackageInfo (..)
    , Deprecation (..)
    ) where

import           Control.Applicative           ((<$>), (<*>))
import           Data.Aeson                    (FromJSON (..), ToJSON (..),
                                                object, withObject, (.:), (.=))
import           Data.Map                      (Map)
import qualified Data.Map                      as Map
import           Data.Set                      (Set)
import qualified Data.Set                      as Set
import           Data.Text                     (Text)
import           Data.Typeable                 (Typeable)
import           Data.Version                  (Version)
import           Distribution.Package          (PackageName)
import           Distribution.Version          (VersionRange)
import           Prelude                       hiding (pi)
import           Stackage.PackageIndex.Conduit (parseDistText, renderDistText)

data PackageInfo = PackageInfo
    { piLatest          :: !Version
    , piHash            :: !Text
    , piAllVersions     :: !(Set Version)
    , piSynopsis        :: !Text
    , piDescription     :: !Text
    , piDescriptionType :: !Text
    , piChangeLog       :: !Text
    , piChangeLogType   :: !Text
    , piBasicDeps       :: !(Map PackageName VersionRange)
    , piTestBenchDeps   :: !(Map PackageName VersionRange)
    , piAuthor          :: !Text
    , piMaintainer      :: !Text
    , piHomepage        :: !Text
    , piLicenseName     :: !Text
    }
    deriving (Show, Eq, Typeable)
instance ToJSON PackageInfo where
    toJSON pi = object
        [ "latest" .= renderDistText (piLatest pi)
        , "hash" .= piHash pi
        , "all-versions" .= map renderDistText (Set.toList $ piAllVersions pi)
        , "synopsis" .= piSynopsis pi
        , "description" .= piDescription pi
        , "description-type" .= piDescriptionType pi
        , "changelog" .= piChangeLog pi
        , "changelog-type" .= piChangeLogType pi
        , "basic-deps" .= showM (piBasicDeps pi)
        , "test-bench-deps" .= showM (piTestBenchDeps pi)
        , "author" .= piAuthor pi
        , "maintainer" .= piMaintainer pi
        , "homepage" .= piHomepage pi
        , "license-name" .= piLicenseName pi
        ]
      where
        showM = Map.mapKeysWith const renderDistText . Map.map renderDistText
instance FromJSON PackageInfo where
    parseJSON = withObject "PackageInfo" $ \o -> PackageInfo
        <$> (o .: "latest" >>= parseDistText)
        <*> o .: "hash"
        <*> (o .: "all-versions" >>= fmap Set.fromList . mapM parseDistText)
        <*> o .: "synopsis"
        <*> o .: "description"
        <*> o .: "description-type"
        <*> o .: "changelog"
        <*> o .: "changelog-type"
        <*> (o .: "basic-deps" >>= parseM)
        <*> (o .: "test-bench-deps" >>= parseM)
        <*> o .: "author"
        <*> o .: "maintainer"
        <*> o .: "homepage"
        <*> o .: "license-name"
      where
        parseM = fmap Map.fromList . mapM go . Map.toList
        go (name, range) = do
            name' <- parseDistText name
            range' <- parseDistText range
            return (name', range')

data Deprecation = Deprecation
    { depPackage    :: !Text
    , depInFavourOf :: !(Set Text)
    }
instance ToJSON Deprecation where
    toJSON d = object
        [ "deprecated-package" .= depPackage d
        , "in-favour-of" .= depInFavourOf d
        ]
instance FromJSON Deprecation where
    parseJSON = withObject "Deprecation" $ \o -> Deprecation
        <$> o .: "deprecated-package"
        <*> o .: "in-favour-of"