{-# LANGUAGE DeriveGeneric #-}
-- The communication protocol with the server

module Spark.Core.Internal.Client where

import Spark.Core.StructuresInternal
import Spark.Core.Dataset(UntypedNode)
import Spark.Core.Internal.Utilities


import Data.Text(Text, pack)
import Data.Aeson
import Data.Aeson.Types(Parser)
import GHC.Generics


-- Imports for the client


data LocalSessionId = LocalSessionId {
  unLocalSession :: !Text
} deriving (Eq, Show)

data ComputationID = ComputationID {
  unComputationID :: !Text
} deriving (Eq, Show, Generic)

data Computation = Computation {
  cSessionId :: !LocalSessionId,
  cId :: !ComputationID,
  cNodes :: ![UntypedNode],
  -- Non-empty
  cTerminalNodes :: ![NodeName],
  -- The node at the top of the computation.
  -- Must be part of the terminal nodes.
  cCollectingNode :: NodeName
} deriving (Show, Generic)


data PossibleNodeStatus =
    NodeQueued
  | NodeRunning
  | NodeFinishedSuccess NodeComputationSuccess
  | NodeFinishedFailure NodeComputationFailure deriving (Show, Generic)

data NodeComputationSuccess = NodeComputationSuccess {
  -- Because Row requires additional information to be deserialized.
  ncsData :: Value
} deriving (Show, Generic)

data NodeComputationFailure = NodeComputationFailure {
  ncfMessage :: !Text
} deriving (Show, Generic)

-- data EndNodeStatus = EndNodeStatus {
--   ensPath :: !NodePath,
--   ensStatus :: !PossibleNodeStatus,
--   ensValue :: !(Maybe Cell)
-- } deriving (Show, Generic)

-- data AggregateProgressStatus = AggregateProgressStatus {
--   apsBundlePaths :: ![NodePath]
-- } deriving (Show, Generic)

-- data GetNodeStatusResponse = GetNodeStatusResponse {
--   gnsrEndNodes :: ![EndNodeStatus],
--   gnsrBundles :: ![AggregateProgressStatus]
-- } deriving (Show, Generic)

-- data GetNodeStatus = GetNodeStatus {
--   gnsSessionId :: !LocalSessionId,
--   gnsComputationId :: !ComputationID,
--   gnsIncludeResults :: !Bool,
--   gnsRequestedNodes :: ![NodePath]
-- } deriving (Show, Generic)

-- **** AESON INSTANCES ***

instance ToJSON Computation

instance ToJSON LocalSessionId where
  toJSON = toJSON . unLocalSession

-- instance FromJSON LocalSessionID where
--   parseJSON = LocalSessionID <$> parseJSON

instance ToJSON ComputationID where
  toJSON = toJSON . unComputationID

-- Because we get a row back, we need to supply a SQLType for deserialization.
instance FromJSON PossibleNodeStatus where
  parseJSON =
    let parseSuccess :: Object -> Parser PossibleNodeStatus
        parseSuccess o =
          (NodeFinishedSuccess . NodeComputationSuccess) <$> o .: pack "finalResult"
        parseFailure :: Object -> Parser PossibleNodeStatus
        parseFailure o =
          (NodeFinishedFailure . NodeComputationFailure) <$> o .: pack "finalError"
    in
      withObject "PossibleNodeStatus" $ \o -> do
      status <- o .: pack "status"
      case status of
        "running" -> return NodeRunning
        "finished_success" -> parseSuccess o
        "finished_failure" -> parseFailure o
        "scheduled" -> return NodeQueued
        _ -> failure $ pack ("FromJSON PossibleNodeStatus " ++ show status)

    -- Person <$> o .: "name" <*> o .: "age"

-- instance ToJSON PossibleNodeStatus where
--   toJSON NodeQueued = toJSON "queued"
--   toJSON NodeRunning = toJSON "running"
--   toJSON NodeFinished = toJSON "finished"