{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric      #-}
module Distribution.Types.PackageId
  ( PackageIdentifier(..)
  , PackageId
  ) where

import Distribution.Compat.Prelude
import Prelude ()

import Distribution.Parsec            (Parsec (..), simpleParsec)
import Distribution.Pretty
import Distribution.Types.PackageName
import Distribution.Version           (Version, nullVersion)

import qualified Data.List.NonEmpty              as NE
import qualified Distribution.Compat.CharParsing as P
import qualified Text.PrettyPrint                as Disp

-- | Type alias so we can use the shorter name PackageId.
type PackageId = PackageIdentifier

-- | The name and version of a package.
data PackageIdentifier
    = PackageIdentifier {
        pkgName    :: PackageName, -- ^The name of this package, eg. foo
        pkgVersion :: Version -- ^the version of this package, eg 1.2
     }
     deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)

instance Binary PackageIdentifier
instance Structured PackageIdentifier

instance Pretty PackageIdentifier where
  pretty (PackageIdentifier n v)
    | v == nullVersion = pretty n -- if no version, don't show version.
    | otherwise        = pretty n <<>> Disp.char '-' <<>> pretty v

-- |
--
-- >>> simpleParsec "foo-bar-0" :: Maybe PackageIdentifier
-- Just (PackageIdentifier {pkgName = PackageName "foo-bar", pkgVersion = mkVersion [0]})
--
-- >>> simpleParsec "foo-bar" :: Maybe PackageIdentifier
-- Just (PackageIdentifier {pkgName = PackageName "foo-bar", pkgVersion = mkVersion []})
--
-- /Note:/ Stricter than 'Text' instance
--
-- >>> simpleParsec "foo-bar-0-0" :: Maybe PackageIdentifier
-- Nothing
--
-- >>> simpleParsec "foo-bar.0" :: Maybe PackageIdentifier
-- Nothing
--
-- >>> simpleParsec "foo-bar.4-2" :: Maybe PackageIdentifier
-- Nothing
--
-- >>> simpleParsec "1.2.3" :: Maybe PackageIdentifier
-- Nothing
--
instance Parsec PackageIdentifier where
  parsec = do
      xs' <- P.sepByNonEmpty component (P.char '-')
      (v, xs) <- case simpleParsec (NE.last xs') of
          Nothing -> return (nullVersion, toList xs') -- all components are version
          Just v  -> return (v, NE.init xs')
      if not (null xs) && all (\c ->  all (/= '.') c && not (all isDigit c)) xs
      then return $ PackageIdentifier (mkPackageName (intercalate  "-" xs)) v
      else fail "all digits or a dot in a portion of package name"
    where
      component = P.munch1 (\c ->  isAlphaNum c || c == '.')

instance NFData PackageIdentifier where
    rnf (PackageIdentifier name version) = rnf name `seq` rnf version
