module IdeSession.Types.Private (
    
    FilePathPtr(..)
  , IdPropPtr(..)
  , UseSites
    
  , Public.IdNameSpace(..)
  , IdInfo(..)
  , IdProp(..)
  , IdScope(..)
  , SourceSpan(..)
  , EitherSpan(..)
  , SourceError(..)
  , Public.SourceErrorKind(..)
  , Public.ModuleName
  , ModuleId(..)
  , PackageId(..)
  , IdList
  , IdMap(..)
  , ExpMap(..)
  , SpanInfo(..)
  , ImportEntities(..)
  , Import(..)
  , RunResult(..)
  , BreakInfo(..)
    
  , ExplicitSharingCache(..)
  , unionCache
    
  , mkIdMap
  , mkExpMap
  , dominators
  ) where
import Prelude hiding (span, mod)
import Data.Text (Text)
import Data.ByteString (ByteString)
import Control.Applicative ((<$>), (<*>))
import Control.Arrow (first)
import Data.Binary (Binary(..), getWord8, putWord8)
import Data.Typeable (Typeable)
import GHC.Generics (Generic)
import qualified IdeSession.Types.Public as Public
import IdeSession.Strict.Container
import IdeSession.Strict.IntervalMap (StrictIntervalMap, Interval(..))
import qualified IdeSession.Strict.IntervalMap as IntervalMap
import qualified IdeSession.Strict.IntMap      as IntMap
import IdeSession.Util.PrettyVal
newtype FilePathPtr = FilePathPtr { filePathPtr :: Int }
  deriving (Eq, Ord, Show, Generic)
newtype IdPropPtr = IdPropPtr { idPropPtr :: Int }
  deriving (Eq, Ord, Show, Generic)
data IdInfo = IdInfo {
    idProp  ::  !IdPropPtr
  , idScope :: !IdScope
  }
  deriving (Show, Typeable, Generic)
data IdProp = IdProp {
    idName       :: !Text
  , idSpace      :: !Public.IdNameSpace
  , idType       :: !(Strict Maybe Public.Type)
  , idDefinedIn  ::  !ModuleId
  , idDefSpan    :: !EitherSpan
  , idHomeModule :: !(Strict Maybe ModuleId)
  }
  deriving (Show, Generic)
data IdScope =
    
    Binder
    
  | Local
    
  | Imported {
        idImportedFrom ::  !ModuleId
      , idImportSpan   :: !EitherSpan
        
        
        
        
        
        
      , idImportQual   :: !Text
      }
    
  | WiredIn
  deriving (Show, Generic)
data SourceSpan = SourceSpan
  { spanFilePath   ::  !FilePathPtr
  , spanFromLine   ::  !Int
  , spanFromColumn ::  !Int
  , spanToLine     ::  !Int
  , spanToColumn   ::  !Int
  }
  deriving (Eq, Ord, Show, Generic)
data EitherSpan =
    ProperSpan  !SourceSpan
  | TextSpan !Text
  deriving (Show, Generic)
data SourceError = SourceError
  { errorKind :: !Public.SourceErrorKind
  , errorSpan :: !EitherSpan
  , errorMsg  :: !Text
  }
  deriving (Show, Generic)
data ModuleId = ModuleId
  { moduleName    :: !Public.ModuleName
  , modulePackage ::  !PackageId
  }
  deriving (Show, Eq, Generic)
data PackageId = PackageId
  { packageName    :: !Text
  , packageVersion :: !(Strict Maybe Text)
  , packageKey     :: !Text
  }
  deriving (Show, Eq, Ord, Generic)
type IdList = [(SourceSpan, SpanInfo)]
data SpanInfo =
   SpanId IdInfo
 | SpanQQ IdInfo
   
   
 | SpanInSplice IdInfo
 deriving (Show, Generic)
newtype IdMap = IdMap { idMapToMap :: StrictIntervalMap (FilePathPtr, Int, Int) SpanInfo }
  deriving (Show, Generic)
newtype ExpMap = ExpMap { expMapToMap :: StrictIntervalMap (FilePathPtr, Int, Int) Text }
  deriving (Show, Generic)
type UseSites = Strict (Map IdPropPtr) [SourceSpan]
data ImportEntities =
    ImportOnly   !(Strict [] Text)
  | ImportHiding !(Strict [] Text)
  | ImportAll
  deriving (Show, Eq, Generic)
data Import = Import {
    importModule    :: !ModuleId
  
  , importPackage   :: !(Strict Maybe Text)
  , importQualified :: !Bool
  , importImplicit  :: !Bool
  , importAs        :: !(Strict Maybe Public.ModuleName)
  , importEntities  :: !ImportEntities
  }
  deriving (Show, Eq, Generic)
data RunResult =
    
    RunOk
    
  | RunProgException String
    
  | RunGhcException String
    
  | RunBreak BreakInfo
  deriving (Typeable, Show, Generic)
data BreakInfo = BreakInfo {
    breakInfoModule      :: Public.ModuleName
  , breakInfoSpan        :: SourceSpan
  , breakInfoResultType  :: Public.Type
  , breakInfoVariableEnv :: Public.VariableEnv
  }
  deriving (Typeable, Show, Generic)
data ExplicitSharingCache = ExplicitSharingCache {
    filePathCache :: !(Strict IntMap ByteString)
  , idPropCache   :: !(Strict IntMap IdProp)
  }
  deriving (Show, Generic)
unionCache :: ExplicitSharingCache -> ExplicitSharingCache -> ExplicitSharingCache
unionCache a b = ExplicitSharingCache {
    filePathCache = IntMap.union (filePathCache a) (filePathCache b)
  , idPropCache   = IntMap.union (idPropCache   a) (idPropCache   b)
  }
instance Binary FilePathPtr where
  put = put . filePathPtr
  get = FilePathPtr <$> get
instance Binary SourceSpan where
  put SourceSpan{..} = do
    put spanFilePath
    put spanFromLine
    put spanFromColumn
    put spanToLine
    put spanToColumn
  get = SourceSpan <$> get <*> get <*> get <*> get <*> get
instance Binary EitherSpan where
  put (ProperSpan span) = putWord8 0 >> put span
  put (TextSpan text)   = putWord8 1 >> put text
  get = do
    header <- getWord8
    case header of
      0 -> ProperSpan <$> get
      1 -> TextSpan <$> get
      _ -> fail "EitherSpan.get: invalid header"
instance Binary SourceError where
  put SourceError{..} = do
    put errorKind
    put errorSpan
    put errorMsg
  get = SourceError <$> get <*> get <*> get
instance Binary IdInfo where
  put IdInfo{..} = put idProp >> put idScope
  get = IdInfo <$> get <*> get
instance Binary IdScope where
  put Binder       = putWord8 0
  put Local        = do putWord8 1
  put Imported{..} = do putWord8 2
                        put idImportedFrom
                        put idImportSpan
                        put idImportQual
  put WiredIn      = putWord8 3
  get = do
    header <- getWord8
    case header of
      0 -> return Binder
      1 -> return Local
      2 -> Imported <$> get <*> get <*> get
      3 -> return WiredIn
      _ -> fail "IdScope.get: invalid header"
instance Binary IdPropPtr where
  put = put . idPropPtr
  get = IdPropPtr <$> get
instance Binary ModuleId where
  put ModuleId{..} = put moduleName >> put modulePackage
  get = ModuleId <$> get <*> get
instance Binary PackageId where
  put PackageId{..} = do
    put packageName
    put packageVersion
    put packageKey
  get = PackageId <$> get <*> get <*> get
instance Binary IdProp where
  put IdProp{..} = do
    put idName
    put idSpace
    put idType
    put idDefinedIn
    put idDefSpan
    put idHomeModule
  get = IdProp <$> get <*> get <*> get <*> get <*> get <*> get
instance Binary ImportEntities where
  put (ImportOnly names)   = putWord8 0 >> put names
  put (ImportHiding names) = putWord8 1 >> put names
  put ImportAll            = putWord8 2
  get = do
    header <- getWord8
    case header of
      0 -> ImportOnly   <$> get
      1 -> ImportHiding <$> get
      2 -> return ImportAll
      _ -> fail "ImportEntities.get: invalid header"
instance Binary Import where
  put Import{..} = do
    put importModule
    put importPackage
    put importQualified
    put importImplicit
    put importAs
    put importEntities
  get = Import <$> get <*> get <*> get <*> get <*> get <*> get
instance Binary ExplicitSharingCache where
  put ExplicitSharingCache{..} = do
    put filePathCache
    put idPropCache
  get = ExplicitSharingCache <$> get <*> get
instance Binary SpanInfo where
  put (SpanId idInfo)       = putWord8 0 >> put idInfo
  put (SpanQQ idInfo)       = putWord8 1 >> put idInfo
  put (SpanInSplice idInfo) = putWord8 2 >> put idInfo
  get = do
    header <- getWord8
    case header of
      0 -> SpanId       <$> get
      1 -> SpanQQ       <$> get
      2 -> SpanInSplice <$> get
      _ -> fail "SpanInfo.get: invalid header"
instance Binary RunResult where
  put RunOk                  = putWord8 0
  put (RunProgException str) = putWord8 1 >> put str
  put (RunGhcException str)  = putWord8 2 >> put str
  put (RunBreak info)        = putWord8 3 >> put info
  get = do
    header <- getWord8
    case header of
      0 -> return RunOk
      1 -> RunProgException <$> get
      2 -> RunGhcException <$> get
      3 -> RunBreak <$> get
      _ -> fail "RunResult.get: invalid header"
instance Binary BreakInfo where
  put (BreakInfo{..}) = do
    put breakInfoModule
    put breakInfoSpan
    put breakInfoResultType
    put breakInfoVariableEnv
  get = BreakInfo <$> get <*> get <*> get <*> get
instance PrettyVal FilePathPtr
instance PrettyVal IdPropPtr
instance PrettyVal IdInfo
instance PrettyVal IdProp
instance PrettyVal IdScope
instance PrettyVal SourceSpan
instance PrettyVal EitherSpan
instance PrettyVal SourceError
instance PrettyVal ModuleId
instance PrettyVal PackageId
instance PrettyVal SpanInfo
instance PrettyVal IdMap
instance PrettyVal ExpMap
instance PrettyVal ImportEntities
instance PrettyVal Import
instance PrettyVal RunResult
instance PrettyVal BreakInfo
instance PrettyVal ExplicitSharingCache
mkIdMap :: IdList -> IdMap
mkIdMap = IdMap . IntervalMap.fromList . map (first spanToInterval)
mkExpMap :: [(SourceSpan, Text)] -> ExpMap
mkExpMap = ExpMap . IntervalMap.fromList . map (first spanToInterval)
dominators :: SourceSpan -> StrictIntervalMap (FilePathPtr, Int, Int) a -> [(SourceSpan, a)]
dominators span ivalmap =
    map (\(ival, idInfo) -> (intervalToSpan ival, idInfo))
        (IntervalMap.dominators (spanToInterval span) ivalmap)
spanToInterval :: SourceSpan -> Interval (FilePathPtr, Int, Int)
spanToInterval SourceSpan{..} =
  Interval (spanFilePath, spanFromLine, spanFromColumn)
           (spanFilePath, spanToLine, spanToColumn)
intervalToSpan :: Interval (FilePathPtr, Int, Int) -> SourceSpan
intervalToSpan (Interval (spanFilePath, spanFromLine, spanFromColumn)
                         (_,            spanToLine, spanToColumn)) =
  SourceSpan{..}