content: {-# LANGUAGE OverloadedLabels  #-}
  {-# LANGUAGE OverloadedLists   #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE RecordWildCards   #-}
  {-# LANGUAGE ScopedTypeVariables   #-}
  {-# LANGUAGE TemplateHaskell #-}
  {-# LANGUAGE RankNTypes #-}
  
  module Model
    ( module Model
    , module Namespace
    , module Common
    ) where
  
  import Data.Sequence as Seq
  import Lens.Micro.Platform
  import Data.Time
  import System.Directory
  import System.FilePath
  import Data.Text(Text, pack)
  
  import Brick.Widgets.List
  import IOTree
  import TextCursor
  
  import Namespace
  import Common
  import Lib
  
  
  initialAppState :: AppState
  initialAppState = AppState
    { _majorState = Setup
        { _setupKind = Socket
        , _knownDebuggees = list Setup_KnownDebuggeesList [] 1
        , _knownSnapshots = list Setup_KnownSnapshotsList [] 1
        }
    }
  
  data AppState = AppState
    { _majorState :: MajorState
    }
  
  mkSocketInfo :: FilePath -> IO SocketInfo
  mkSocketInfo fp = SocketInfo fp <$> getModificationTime fp
  
  socketName :: SocketInfo -> Text
  socketName = pack . takeFileName . _socketLocation
  
  renderSocketTime :: SocketInfo -> Text
  renderSocketTime = pack . formatTime defaultTimeLocale "%c" . _socketCreated
  
  data SocketInfo = SocketInfo
                      { _socketLocation :: FilePath -- ^ FilePath to socket, absolute path
                      , _socketCreated :: UTCTime  -- ^ Time of socket creation
                      } deriving Eq
  
  instance Ord SocketInfo where
    compare (SocketInfo s1 t1) (SocketInfo s2 t2) =
      -- Compare time first
      compare t1 t2 <> compare s1 s2
  
  type SnapshotInfo = SocketInfo
  
  data SetupKind = Socket | Snapshot
  
  toggleSetup :: SetupKind -> SetupKind
  toggleSetup Socket = Snapshot
  toggleSetup Snapshot = Socket
  
  data MajorState
    -- | We have not yet connected to a debuggee.
    = Setup
      { _setupKind :: SetupKind
      , _knownDebuggees :: GenericList Name Seq SocketInfo
      , _knownSnapshots :: GenericList Name Seq SnapshotInfo
      }
  
    -- | Connected to a debuggee
    | Connected
      { _debuggeeSocket :: SocketInfo
      , _debuggee :: Debuggee
      , _mode     :: ConnectedMode
      }
  
  data InfoInfo = InfoInfo
    {
      _labelInParent :: Text -- ^ A label describing the relationship to the parent
    -- Stuff  that requires IO to calculate
    , _pretty :: Text
    , _sourceLocation :: Maybe SourceInformation
    , _closureType :: Maybe Text
    , _constructor :: Maybe Text }
  
  data ClosureDetails pap s c = ClosureDetails
    { _closure :: DebugClosure pap ConstrDesc s c
    , _retainerSize :: Maybe RetainerSize
    , _excSize :: Size
    , _info :: InfoInfo
    }
    | InfoDetails { _info :: InfoInfo }
    | LabelNode { _label :: Text }
  
  data TreeMode = Dominator | SavedAndGCRoots | Reverse | Retainer (IOTree (ClosureDetails PayloadCont StackCont ClosurePtr) Name)
  
  data FooterMode = FooterInfo
                  | FooterMessage Text
                  | FooterInput FooterInputMode TextCursor
  
  data FooterInputMode = FSearch | FProfile | FRetainer | FRetainerExact | FSnapshot
  
  formatFooterMode :: FooterInputMode -> Text
  formatFooterMode FSearch = "search: "
  formatFooterMode FProfile = "filename: "
  formatFooterMode FRetainer = "constructor name: "
  formatFooterMode FRetainerExact = "closure name: "
  formatFooterMode FSnapshot = "snapshot name: "
  
  data ConnectedMode
    -- | Debuggee is running
    = RunningMode
    -- | Debuggee is paused and we're exploring the heap
    | PausedMode { _pausedMode :: OperationalState }
  
  data RootsOrigin = DefaultRoots [(Text, Ptr)]
                   | SearchedRoots [(Text, Ptr)]
  
  
  currentRoots :: RootsOrigin -> [(Text, Ptr)]
  currentRoots (DefaultRoots cp) = cp
  currentRoots (SearchedRoots cp) = cp
  
  data OperationalState = OperationalState
      { _treeMode :: TreeMode
      , _footerMode :: FooterMode
      , _rootsFrom  :: RootsOrigin
      , _treeDominator :: Maybe DominatorAnalysis
      -- ^ Tree corresponding to Dominator mode
      , _treeSavedAndGCRoots :: IOTree (ClosureDetails PayloadCont StackCont ClosurePtr) Name
      -- ^ Tree corresponding to SavedAndGCRoots mode
      , _treeReverse :: Maybe ReverseAnalysis
      -- ^ Tree corresponding to Dominator mode
      , _heapGraph :: Maybe (HeapGraph Size)
      -- ^ Raw heap graph
      }
  
  data DominatorAnalysis =
    DominatorAnalysis { _getDominatorAnalysis :: Analysis
                      , _getDominatorTree :: IOTree (ClosureDetails PayloadCont StackCont ClosurePtr) Name
                      }
  
  data ReverseAnalysis = ReverseAnalysis { _reverseIOTree :: IOTree (ClosureDetails PapHI StackHI (Maybe HeapGraphIndex)) Name
                                            , _convertPtr :: ClosurePtr -> Maybe (DebugClosure PapHI ConstrDesc StackHI (Maybe HeapGraphIndex)) }
  
  pauseModeTree :: (forall pap s c . IOTree (ClosureDetails pap s c) Name -> r) -> OperationalState -> r
  pauseModeTree k (OperationalState mode _ _footer dom roots reverseA _) = case mode of
    Dominator -> k $ maybe (error "DOMINATOR-DavidE is not ready") _getDominatorTree dom
    SavedAndGCRoots -> k roots
    Reverse -> k $ maybe (error "bop it, flip, reverse it, DavidE") _reverseIOTree reverseA
    Retainer r -> k r
  
  makeLenses ''AppState
  makeLenses ''MajorState
  makeLenses ''ClosureDetails
  makeLenses ''ConnectedMode
  makeLenses ''OperationalState
  makeLenses ''SocketInfo
  makeLenses ''DominatorAnalysis
  makeLenses ''ReverseAnalysis
  