content: {-# LANGUAGE TypeFamilies, ScopedTypeVariables, TypeSynonymInstances, FlexibleInstances, FlexibleContexts, RecordWildCards #-}
  -- | Translation from the private to the public types
  module IdeSession.Types.Translation (
      XShared
    , ExplicitSharing(..)
    , IntroduceSharing(..)
    , showNormalized
    , dereferenceFilePathPtr
    ) where
  
  import Prelude hiding (mod, span)
  import Data.Proxy
  import qualified Data.ByteString.Char8 as BSSC
  import qualified Data.Text as Text
  import Data.Binary (Binary)
  
  import IdeSession.Strict.Container
  import qualified IdeSession.Types.Public  as Public
  import qualified IdeSession.Types.Private as Private
  import qualified IdeSession.Strict.IntMap as StrictIntMap
  import qualified IdeSession.Strict.Maybe  as StrictMaybe
  
  -- | The associated type with explicit sharing
  type family XShared a
  
  -- | The inverse of MShared, only for decidability of type checking
  type family MShared a
  
  type instance XShared Public.IdProp          = Private.IdProp
  type instance XShared Public.IdInfo          = Private.IdInfo
  type instance XShared Public.IdScope         = Private.IdScope
  type instance XShared Public.SourceSpan      = Private.SourceSpan
  type instance XShared Public.EitherSpan      = Private.EitherSpan
  type instance XShared Public.SourceError     = Private.SourceError
  -- type instance XShared Public.IdMap           = Private.IdMap
  -- type instance XShared Public.LoadedModules   = Private.LoadedModules
  type instance XShared Public.ModuleId        = Private.ModuleId
  type instance XShared Public.PackageId       = Private.PackageId
  type instance XShared Public.ImportEntities  = Private.ImportEntities
  type instance XShared Public.Import          = Private.Import
  type instance XShared Public.SpanInfo        = Private.SpanInfo
  type instance XShared Public.RunResult       = Private.RunResult
  type instance XShared Public.BreakInfo       = Private.BreakInfo
  
  type instance MShared Private.IdProp         = Public.IdProp
  type instance MShared Private.IdInfo         = Public.IdInfo
  type instance MShared Private.IdScope        = Public.IdScope
  type instance MShared Private.SourceSpan     = Public.SourceSpan
  type instance MShared Private.EitherSpan     = Public.EitherSpan
  type instance MShared Private.SourceError    = Public.SourceError
  -- type instance MShared Private.IdMap          = Public.IdMap
  -- type instance MShared Private.LoadedModules  = Public.LoadedModules
  type instance MShared Private.ModuleId       = Public.ModuleId
  type instance MShared Private.PackageId      = Public.PackageId
  type instance MShared Private.ImportEntities = Public.ImportEntities
  type instance MShared Private.Import         = Public.Import
  type instance MShared Private.SpanInfo       = Public.SpanInfo
  type instance MShared Private.RunResult      = Public.RunResult
  type instance MShared Private.BreakInfo      = Public.BreakInfo
  
  {------------------------------------------------------------------------------
    Removing explicit sharing
  ------------------------------------------------------------------------------}
  
  -- | Many of the public data types that we export in "IdeSession" have a
  -- corresponding private @XShared@ version. For instance, we have @IdProp@ and
  -- @XShared IdProp@, @SourceError@ and @XShared SourceError@, etc. These
  -- @XShared@ types are abstract; what's important is only that they can be
  -- serialized (support @FromJSON@ and @ToJSON@). The main difference between
  -- the public and the private data types is that the private data types use
  -- explicit sharing. This is important for serialization, because there is
  -- quite a bit of sharing in the type information that we collect and losing
  -- this would be a significant performance hit. (The other difference is that
  -- the private data types use specialized types that guarantee strictness.)
  --
  -- The @MShared (XShared a) ~ a@ condition on the @ExplicitSharing@ type class
  -- is there for technical reasons only (it convinces GHC that the @XShared@
  -- type family is a bijection).
  class (MShared (XShared a) ~ a, Binary (XShared a)) => ExplicitSharing a where
    removeExplicitSharing :: Proxy a -> Private.ExplicitSharingCache -> XShared a -> a
  
  showNormalized :: forall a. (Show a, ExplicitSharing a, MShared (XShared a) ~ a)
                 => Proxy a -> Private.ExplicitSharingCache -> XShared a -> String
  showNormalized _ cache x = show (removeExplicitSharing Proxy cache x :: a)
  
  instance ExplicitSharing Public.IdProp where
    removeExplicitSharing _ cache Private.IdProp{..} = Public.IdProp {
        Public.idName       = idName
      , Public.idSpace      = idSpace
      , Public.idType       = toLazyMaybe idType
      , Public.idDefSpan    = removeExplicitSharing Proxy cache idDefSpan
      , Public.idDefinedIn  = removeExplicitSharing Proxy cache idDefinedIn
      , Public.idHomeModule = StrictMaybe.maybe
                                Nothing
                                (Just . removeExplicitSharing Proxy cache)
                                idHomeModule
      }
  
  instance ExplicitSharing Public.IdInfo where
    removeExplicitSharing _ cache Private.IdInfo{..} = Public.IdInfo {
        Public.idProp  = case StrictIntMap.lookup (Private.idPropPtr idProp)
                                                  (Private.idPropCache cache)
                           of Just idProp' -> removeExplicitSharing Proxy cache idProp'
                              Nothing      -> unknownProp
      , Public.idScope = removeExplicitSharing Proxy cache idScope
      }
      where
        unknownProp = Public.IdProp {
            idName        = Text.pack "<<unknown id>>"
          , idSpace       = Public.VarName
          , idType        = Nothing
          , idDefinedIn   = unknownModule
          , idDefSpan     = Public.TextSpan (Text.pack "<<unknown span>>")
          , idHomeModule  = Nothing
          }
  
        unknownModule = Public.ModuleId {
            moduleName    = Text.pack "<<unknown module>>"
          , modulePackage = unknownPackage
          }
  
        unknownPackage = Public.PackageId {
           packageName    = Text.pack "<<unknown package>>"
         , packageVersion = Nothing
         , packageKey     = Text.pack "<<unknown package>>"
         }
  
  instance ExplicitSharing Public.ModuleId where
    removeExplicitSharing _ cache Private.ModuleId{..} = Public.ModuleId {
        Public.moduleName    = moduleName
      , Public.modulePackage = removeExplicitSharing Proxy cache modulePackage
      }
  
  instance ExplicitSharing Public.PackageId where
    removeExplicitSharing _ _cache Private.PackageId{..} = Public.PackageId {
        Public.packageName    = packageName
      , Public.packageVersion = toLazyMaybe packageVersion
      , Public.packageKey     = packageKey
      }
  
  instance ExplicitSharing Public.IdScope where
    removeExplicitSharing _ cache idScope = case idScope of
      Private.Binder -> Public.Binder
      Private.Local  -> Public.Local
      Private.Imported {..} -> Public.Imported {
          Public.idImportedFrom = removeExplicitSharing Proxy cache idImportedFrom
        , Public.idImportSpan   = removeExplicitSharing Proxy cache idImportSpan
        , Public.idImportQual   = idImportQual
        }
      Private.WiredIn -> Public.WiredIn
  
  instance ExplicitSharing Public.SourceSpan where
    removeExplicitSharing _ cache Private.SourceSpan{..} = Public.SourceSpan {
        Public.spanFilePath   = dereferenceFilePathPtr cache spanFilePath
      , Public.spanFromLine   = spanFromLine
      , Public.spanFromColumn = spanFromColumn
      , Public.spanToLine     = spanToLine
      , Public.spanToColumn   = spanToColumn
      }
  
  instance ExplicitSharing Public.EitherSpan where
    removeExplicitSharing _ cache eitherSpan = case eitherSpan of
      Private.ProperSpan sourceSpan ->
        Public.ProperSpan (removeExplicitSharing Proxy cache sourceSpan)
      Private.TextSpan str ->
        Public.TextSpan str
  
  instance ExplicitSharing Public.SourceError where
    removeExplicitSharing _ cache Private.SourceError{..} = Public.SourceError {
        Public.errorKind = errorKind
      , Public.errorSpan = removeExplicitSharing Proxy cache errorSpan
      , Public.errorMsg  = errorMsg
      }
  
  {-
  instance ExplicitSharing Public.IdMap where
    removeExplicitSharing cache = Public.IdMap
                                . toLazyMap
                                . StrictMap.map (removeExplicitSharing cache)
                                . StrictMap.mapKeys (removeExplicitSharing cache)
                                . Private.idMapToMap
  -}
  
  {-
  instance ExplicitSharing Public.LoadedModules where
    removeExplicitSharing cache = Map.map (removeExplicitSharing cache)
                                . toLazyMap
  -}
  
  instance ExplicitSharing Public.ImportEntities where
    removeExplicitSharing _ _cache entities = case entities of
      Private.ImportAll          -> Public.ImportAll
      Private.ImportHiding names -> Public.ImportHiding (toLazyList names)
      Private.ImportOnly   names -> Public.ImportOnly (toLazyList names)
  
  instance ExplicitSharing Public.Import where
    removeExplicitSharing _ cache Private.Import{..} = Public.Import {
        Public.importModule     = removeExplicitSharing Proxy cache $ importModule
      , Public.importPackage    = toLazyMaybe importPackage
      , Public.importQualified  = importQualified
      , Public.importImplicit   = importImplicit
      , Public.importAs         = toLazyMaybe importAs
      , Public.importEntities   = removeExplicitSharing Proxy cache $ importEntities
      }
  
  instance ExplicitSharing Public.SpanInfo where
    removeExplicitSharing _ cache spanInfo = case spanInfo of
      Private.SpanId       idInfo -> Public.SpanId (removeExplicitSharing Proxy cache idInfo)
      Private.SpanQQ       idInfo -> Public.SpanQQ (removeExplicitSharing Proxy cache idInfo)
      Private.SpanInSplice idInfo -> Public.SpanId (removeExplicitSharing Proxy cache idInfo)
  
  instance ExplicitSharing Public.BreakInfo where
    removeExplicitSharing _ cache Private.BreakInfo{..} = Public.BreakInfo {
        Public.breakInfoModule      = breakInfoModule
      , Public.breakInfoSpan        = removeExplicitSharing Proxy cache breakInfoSpan
      , Public.breakInfoResultType  = breakInfoResultType
      , Public.breakInfoVariableEnv = breakInfoVariableEnv
      }
  
  {------------------------------------------------------------------------------
    Low-level API
  ------------------------------------------------------------------------------}
  
  dereferenceFilePathPtr :: Private.ExplicitSharingCache
                         -> Private.FilePathPtr -> FilePath
  dereferenceFilePathPtr cache ptr = BSSC.unpack $
      StrictIntMap.findWithDefault
        unknownFilePath
        (Private.filePathPtr ptr)
        (Private.filePathCache cache)
    where
      unknownFilePath = BSSC.pack "<<unknown filepath>>"
  
  {------------------------------------------------------------------------------
    Introducing explicit sharing
  ------------------------------------------------------------------------------}
  
  -- | Introduce explicit sharing
  --
  -- This provides the opposite translation to removeExplicitSharing. Note however
  -- that this is a partial function -- we never extend the cache, so if a
  -- required value is missing from the cache we return @Nothing@.
  class IntroduceSharing a where
    introduceExplicitSharing :: Private.ExplicitSharingCache -> a -> Maybe (XShared a)
  
  instance IntroduceSharing Public.SourceSpan where
    introduceExplicitSharing cache Public.SourceSpan{..} = do
      ptr <- StrictIntMap.reverseLookup (Private.filePathCache cache)
                                        (BSSC.pack spanFilePath)
      return Private.SourceSpan {
          Private.spanFilePath   = Private.FilePathPtr ptr
        , Private.spanFromLine   = spanFromLine
        , Private.spanFromColumn = spanFromColumn
        , Private.spanToLine     = spanToLine
        , Private.spanToColumn   = spanToColumn
        }
  