{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Distribution.Types.GenericPackageDescription (
    GenericPackageDescription(..),
    emptyGenericPackageDescription,
) where

import Prelude ()
import Distribution.Compat.Prelude

-- lens
import Distribution.Compat.Lens                     as L
import qualified Distribution.Types.BuildInfo.Lens  as L

import Distribution.Types.PackageDescription

import Distribution.Types.Benchmark
import Distribution.Types.CondTree
import Distribution.Types.ConfVar
import Distribution.Types.Dependency
import Distribution.Types.Executable
import Distribution.Types.Flag
import Distribution.Types.ForeignLib
import Distribution.Types.Library
import Distribution.Types.TestSuite
import Distribution.Types.UnqualComponentName
import Distribution.Package
import Distribution.Version

-- ---------------------------------------------------------------------------
-- The 'GenericPackageDescription' type

data GenericPackageDescription =
  GenericPackageDescription
  { packageDescription :: PackageDescription
  , gpdScannedVersion  :: Maybe Version
    -- ^ This is a version as specified in source.
    --   We populate this field in index reading for dummy GPDs,
    --   only when GPD reading failed, but scanning haven't.
    --
    --   Cabal-the-library never produces GPDs with Just as gpdScannedVersion.
    --
    --   Perfectly, PackageIndex should have sum type, so we don't need to
    --   have dummy GPDs.
  , genPackageFlags    :: [PackageFlag]
  , condLibrary        :: Maybe (CondTree ConfVar [Dependency] Library)
  , condSubLibraries   :: [( UnqualComponentName
                           , CondTree ConfVar [Dependency] Library )]
  , condForeignLibs    :: [( UnqualComponentName
                           , CondTree ConfVar [Dependency] ForeignLib )]
  , condExecutables    :: [( UnqualComponentName
                           , CondTree ConfVar [Dependency] Executable )]
  , condTestSuites     :: [( UnqualComponentName
                           , CondTree ConfVar [Dependency] TestSuite )]
  , condBenchmarks     :: [( UnqualComponentName
                           , CondTree ConfVar [Dependency] Benchmark )]
  }
    deriving (Show, Eq, Typeable, Data, Generic)

instance Package GenericPackageDescription where
  packageId = packageId . packageDescription

instance Binary GenericPackageDescription
instance Structured GenericPackageDescription
instance NFData GenericPackageDescription where rnf = genericRnf

emptyGenericPackageDescription :: GenericPackageDescription
emptyGenericPackageDescription = GenericPackageDescription emptyPackageDescription Nothing [] Nothing [] [] [] [] []

-- -----------------------------------------------------------------------------
-- Traversal Instances

instance L.HasBuildInfos GenericPackageDescription where
    traverseBuildInfos f (GenericPackageDescription p v a1 x1 x2 x3 x4 x5 x6) =
        GenericPackageDescription
            <$> L.traverseBuildInfos f p
            <*> pure v
            <*> pure a1
            <*> (traverse . traverseCondTreeBuildInfo) f x1
            <*> (traverse . L._2 . traverseCondTreeBuildInfo) f x2
            <*> (traverse . L._2 . traverseCondTreeBuildInfo) f x3
            <*> (traverse . L._2 . traverseCondTreeBuildInfo) f x4
            <*> (traverse . L._2 . traverseCondTreeBuildInfo) f x5
            <*> (traverse . L._2 . traverseCondTreeBuildInfo) f x6
      where

-- We use this traversal to keep [Dependency] field in CondTree up to date.
traverseCondTreeBuildInfo
    :: forall f comp v. (Applicative f, L.HasBuildInfo comp)
    => LensLike' f (CondTree v [Dependency] comp) L.BuildInfo
traverseCondTreeBuildInfo g = node where
    mkCondNode :: comp -> [CondBranch v [Dependency] comp] -> CondTree v [Dependency] comp
    mkCondNode comp branches = CondNode comp (view L.targetBuildDepends comp) branches

    node (CondNode comp _ branches) = mkCondNode
        <$> L.buildInfo g comp
        <*> traverse branch branches

    branch (CondBranch v x y) = CondBranch v
        <$> node x
        <*> traverse node y