-- |
-- Module      :  Cryptol.TypeCheck.PP
-- Copyright   :  (c) 2013-2016 Galois, Inc.
-- License     :  BSD3
-- Maintainer  :  cryptol@galois.com
-- Stability   :  provisional
-- Portability :  portable

{-# LANGUAGE Safe #-}
{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
module Cryptol.TypeCheck.PP
  ( NameMap, WithNames(..)
  , emptyNameMap
  , ppWithNamesPrec, ppWithNames
  , nameList
  , dump
  , module Cryptol.Utils.PP
  ) where

import           Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap
import           Data.List(transpose)
import           Cryptol.Utils.PP


type NameMap = IntMap String

emptyNameMap :: NameMap
emptyNameMap = IntMap.empty

-- | This packages together a type with some names to be used to display
-- the variables.  It is used for pretty printing types.
data WithNames a = WithNames a NameMap

ppWithNamesPrec :: PP (WithNames a) => NameMap -> Int -> a -> Doc
ppWithNamesPrec names prec t = ppPrec prec (WithNames t names)

ppWithNames :: PP (WithNames a) => NameMap -> a -> Doc
ppWithNames names t = ppWithNamesPrec names 0 t

dump :: PP (WithNames a) => a -> String
dump x = show (ppWithNames IntMap.empty x)

-- | Compute the n-th variant of a name (e.g., @a5@).
nameVariant :: Int -> String -> String
nameVariant 0 x   = x
nameVariant n x   = x ++ show n

-- | Compute all variants of a name: @a, a1, a2, a3, ...@
nameVariants :: String -> [String]
nameVariants x = map (`nameVariant` x) [ 0 .. ]

-- | Expand a list of base names into an infinite list of variations.
nameList :: [String] -> [String]
nameList names = concat $ transpose $ map nameVariants baseNames
  where
  baseNames | null names = map (:[]) [ 'a' .. 'z' ]
            | otherwise  = names