module XMLQuery.XMLTypes.Interpreter where

import XMLQuery.XMLTypes.Prelude
import Data.XML.Types
import qualified XMLQuery.AST as AST
import qualified XMLQuery.XMLTypes.Renderer as Renderer
import qualified Success.Pure as Success


type Result =
  Success Text

nodes :: Alt AST.Nodes a -> [Node] -> Result a
nodes (Alt alternatives) input =
  runAlternatives alternatives input
  where
    runAlternatives alternatives input =
      asum (map (\alternative -> runAlternative alternative input) alternatives)
    runAlternative alternative input =
      case alternative of
        Ap fa altfab ->
          case fa of
            AST.NodesNode query ->
              case input of
                head : tail ->
                  nodes altfab tail <*>
                  node query head
                _ ->
                  Success.failure "No more nodes"
        Pure a ->
          Success.success a

node :: Alt AST.Node a -> Node -> Result a
node alt node =
  runAlt interpreter alt
  where
    interpreter :: forall a. AST.Node a -> Result a
    interpreter =
      \case
        AST.NodeElement alt ->
          case node of
            NodeElement input ->
              element alt input
            _ ->
              Success.failure "Not an element node"
        AST.NodeText ast ->
          case node of
            NodeContent content ->
              text ast (Renderer.run Renderer.content content)
            _ ->
              Success.failure "Not a content node"

element :: Alt AST.Element a -> Element -> Result a
element alt input =
  runAlt interpreter alt
  where
    interpreter :: forall a. AST.Element a -> Result a
    interpreter =
      \case
        AST.ElementNameText textAST ->
          text textAST (Renderer.run Renderer.name (elementName input))
        AST.ElementAttr attrAST ->
          asum (map (attr attrAST) (elementAttributes input))
        AST.ElementNodes ast ->
          nodes ast (elementNodes input)

attr :: Alt AST.Attr a -> (Name, [Content]) -> Result a
attr alt input =
  runAlt interpreter alt
  where
    interpreter :: forall a. AST.Attr a -> Result a
    interpreter =
      \case
        AST.AttrNameText ast ->
          text ast (Renderer.run Renderer.name (fst input))
        AST.AttrValueText ast ->
          text ast (Renderer.run Renderer.contents (snd input))

text :: Alt AST.Text a -> Text -> Result a
text alt input =
  runAlt interpreter alt
  where
    interpreter :: forall a. AST.Text a -> Result a
    interpreter =
      \case
        AST.Text fn ->
          either Success.failure Success.success (fn input)
