module Lib.TopContext

import Data.IORef
import Data.SortedMap
import Data.String
import Prelude
import Lib.Common
import Lib.Types

-- I want unique ids, to be able to lookup, update, and a Ref so
-- I don't need good Context discipline. (I seem to have made mistakes already.)


lookup : QName -> TopContext -> Maybe TopEntry
lookup qn@(QN ns nm) top =
  case lookupMap' qn top.defs of
    Just entry => Just entry
    Nothing => case lookupMap' ns top.modules of
      Just mod => lookupMap' qn mod.modDefs
      Nothing => Nothing

lookupImported : String → TopContext -> List TopEntry
lookupImported raw top =
  mapMaybe (flip lookup top) $ (QN top.ns raw) :: map (flip QN raw) top.imported

-- For repl / out of scope errors
lookupAll : String → TopContext -> List TopEntry
lookupAll raw top =
  mapMaybe (flip lookup top) $ (QN top.ns raw) :: map (flip QN raw) (map fst $ toList top.modules)

lookupRaw : String -> TopContext -> Maybe TopEntry
lookupRaw raw top =
  case lookupMap' (QN top.ns raw) top.defs of
    Just entry => Just entry
    Nothing => go top.imported
  where
    go : List (List String) → Maybe TopEntry
    go Nil = Nothing
    go (ns :: nss) = case lookupMap' ns top.modules of
      Nothing => go nss
      Just mod => case lookupMap' (QN ns raw) mod.modDefs of
        Just entry => Just entry
        Nothing => go nss


instance Show TopContext where
  show top = "\nContext:\n [\{ joinBy "\n" $ map (show ∘ snd) $ toList top.defs}]"

-- TODO need to get class dependencies working
emptyTop : ∀ io. {{Monad io}} {{HasIO io}} -> io TopContext
emptyTop = do
  let mcctx = MC emptyMap Nil 0 CheckAll
  pure $ MkTop emptyMap Nil emptyMap Nil emptyMap mcctx 0 Nil emptyMap


setFlag : QName → FC → EFlag → M Unit
setFlag name fc flag = do
  top <- getTop
  let (Just (MkEntry fc nm ty def flags)) = lookupMap' name top.defs
    | Nothing => error fc "\{show name} not declared"
  modifyTop $ \ top => [ defs := (updateMap name (MkEntry fc name ty def (flag :: flags)) top.defs) ] top

setDef : QName -> FC -> Tm -> Def → List EFlag -> M Unit
setDef name fc ty def flags = do
  top <- getTop
  let (Nothing) = lookupMap' name top.defs
    | Just (MkEntry fc' nm' ty' def' _) => error fc "\{show name} is already defined at \{show fc'}"
  modifyTop $ \top =>
    [ defs := (updateMap name (MkEntry fc name ty def flags) top.defs)] top

updateDef : QName -> FC -> Tm -> Def -> M Unit
updateDef name fc ty def = do
  top <- getTop
  let (Just (MkEntry fc' nm' ty' def' flags)) = lookupMap' name top.defs
    | Nothing => error fc "\{show name} not declared"
  putTop $ [ defs := updateMap name (MkEntry fc' name ty def flags) top.defs ] top

typeName : Tm → Maybe QName
typeName (Pi fc nm Explicit rig t u) = Nothing
typeName (Pi fc name icit rig t u) = typeName u
typeName (App fc t u) = typeName t
typeName (Ref _ nm) = Just nm
typeName _ = Nothing

addHint : QName → M Unit
addHint qn = do
  top <- getTop
  case lookup qn top of
    Just entry => do
      let (Just tyname) = typeName entry.type
        | Nothing => error entry.fc "can't find tcon name for \{show qn}"
      let xs = fromMaybe Nil $ lookupMap' tyname top.hints
      let hints = updateMap tyname ((qn, entry.type) :: xs) top.hints
      putTop $ [ hints := hints ] top
    Nothing => pure MkUnit

addError : Error -> M Unit
addError err = do
  top <- getTop
  modifyTop [ errors $= _::_ err ]
