module DataFlow.JSONGraphFormat.Renderer (convertDiagram, renderJSONGraph) where

import qualified Data.Aeson               as A
import           Data.ByteString.Lazy     (ByteString)
import           Data.Text (pack, unpack)
import qualified Data.Map                 as M
import qualified Data.Vector              as V

import           DataFlow.Core
import qualified DataFlow.JSONGraphFormat as JG

getTitle :: JG.Metadata -> Maybe String
getTitle m =  do
  v <- M.lookup "title" m
  case v of
    (JG.Str s) -> Just s
    _ -> Nothing

convertValue :: Value -> JG.Val
convertValue (String s) = JG.Str s
convertValue (Array vs) = JG.Arr (map convertValue vs)

convertAttrs :: Attributes -> M.Map String JG.Val
convertAttrs = M.map convertValue

withLabelAndMetadataFrom :: (Maybe String -> JG.Metadata -> v) -> JG.Metadata -> v
f `withLabelAndMetadataFrom` metadata = f (getTitle metadata) (M.delete "title" metadata)

addType :: String -> JG.Metadata -> JG.Metadata
addType s = M.insert "type" (JG.Str s)

addBoundary :: Maybe String -> JG.Metadata -> JG.Metadata
addBoundary (Just b) m = M.insert "trust-boundary" (JG.Str b) m
addBoundary Nothing m = m

convertNode :: Maybe String -> Node -> JG.Node
convertNode b (InputOutput id attrs) =
  JG.Node id `withLabelAndMetadataFrom` addBoundary b (addType "io" $ convertAttrs attrs)
convertNode b (Function id attrs) =
  JG.Node id `withLabelAndMetadataFrom` addBoundary b (addType "function" $ convertAttrs attrs)
convertNode b (Database id attrs) =
  JG.Node id `withLabelAndMetadataFrom` addBoundary b (addType "database" $ convertAttrs attrs)

convertRootNode :: RootNode -> [JG.Node]
convertRootNode (Node node) = [convertNode Nothing node]
-- TODO: replace title attribute with mandatory ID for boundaries
convertRootNode (TrustBoundary id' attrs nodes) =
  map (convertNode (Just id')) nodes

convertFlow :: Flow -> JG.Edge
convertFlow (Flow source target attrs) =
  JG.Edge source target `withLabelAndMetadataFrom` convertAttrs attrs

convertDiagram :: Diagram -> JG.Document
convertDiagram (Diagram attrs rootNodes flows) =
  let nodes = concatMap convertRootNode rootNodes
      edges = map convertFlow flows
      graph = JG.Graph nodes edges `withLabelAndMetadataFrom` convertAttrs attrs
  in JG.SingleGraph graph

renderJSONGraph :: Diagram -> ByteString
renderJSONGraph = A.encode . convertDiagram