    {-# LANGUAGE GeneralizedNewtypeDeriving #-}
    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE TypeFamilies #-}
    module Hpack.Syntax.Dependencies (
      Dependencies(..)
    , DependencyInfo(..)
    , parseDependency
    ) where

    import           Data.Text (Text)
    import qualified Data.Text as T
    import           Data.Semigroup (Semigroup(..))
    import qualified Distribution.Package as D
    import           Data.Map.Lazy (Map)
    import qualified Data.Map.Lazy as Map
    import           GHC.Exts

    import           Data.Aeson.Config.FromValue
    import           Data.Aeson.Config.Types

    import           Hpack.Syntax.DependencyVersion
    import           Hpack.Syntax.ParseDependencies

    newtype Dependencies = Dependencies {
      unDependencies :: Map String DependencyInfo
    } deriving (Eq, Show, Semigroup, Monoid)

    instance IsList Dependencies where
      type Item Dependencies = (String, DependencyInfo)
      fromList = Dependencies . Map.fromList
      toList = Map.toList . unDependencies

    instance FromValue Dependencies where
      fromValue = fmap (Dependencies . Map.fromList) . parseDependencies parse
        where
          parse :: Parse String DependencyInfo
          parse = Parse {
            parseString = \ input -> do
              (name, version) <- parseDependency "dependency" input
              return (name, DependencyInfo [] version)
          , parseListItem = objectDependencyInfo
          , parseDictItem = dependencyInfo
          , parseName = T.unpack
          }

    data DependencyInfo = DependencyInfo {
      dependencyInfoMixins :: [String]
    , dependencyInfoVersion :: DependencyVersion
    } deriving (Eq, Show)

    addMixins :: Object -> DependencyVersion -> Parser DependencyInfo
    addMixins o version = do
      mixinsMay <- o .:? "mixin"
      return $ DependencyInfo (fromMaybeList mixinsMay) version

    objectDependencyInfo :: Object -> Parser DependencyInfo
    objectDependencyInfo o = objectDependency o >>= addMixins o

    dependencyInfo :: Value -> Parser DependencyInfo
    dependencyInfo = withDependencyVersion (DependencyInfo []) addMixins

    parseDependency :: Monad m => String -> Text -> m (String, DependencyVersion)
    parseDependency subject = fmap fromCabal . cabalParse subject . T.unpack
      where
        fromCabal :: D.Dependency -> (String, DependencyVersion)
        fromCabal d = (D.unPackageName $ D.depPkgName d, DependencyVersion Nothing . versionConstraintFromCabal $ D.depVerRange d)