{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}

module Text.XML.HXT.RelaxNG.PatternToString
  ( patternToStringTree
  , patternToFormatedString
  , xmlTreeToPatternStringTree
  , xmlTreeToPatternFormatedString
  , xmlTreeToPatternString
  , nameClassToString
  )
where

import Control.Arrow.ListArrows

import Data.Tree.Class                  ( formatTree )
import Data.Tree.NTree.TypeDefs

import Text.XML.HXT.DOM.Interface

import Text.XML.HXT.RelaxNG.DataTypes
import Text.XML.HXT.RelaxNG.CreatePattern
import Text.XML.HXT.RelaxNG.Utils

-- ------------------------------------------------------------

type PatternTree = NTree String


{- |
Returns a string representation of the pattern structure.
(see also: 'createPatternFromXmlTree')

Example:

> Element {}foo (Choice (Choice (Value ("","token") "abc"
> ("foo","www.bar.baz")]))(Data ("http://www.mysql.com","VARCHAR")
> [("length","2"),("maxLength","5")])) (Element {}bar (Group (Element {}baz

The function can @not@ be used to display circular ref-pattern structures.
-}

xmlTreeToPatternString :: LA XmlTree String
xmlTreeToPatternString
    = createPatternFromXmlTree
      >>^
      show


-- | Returns a string representation of a nameclass.

nameClassToString :: NameClass -> String
nameClassToString AnyName
    = "AnyName"

nameClassToString (AnyNameExcept nc)
    = "AnyNameExcept " ++ nameClassToString nc

nameClassToString (Name uri local)
    = "{" ++ uri ++ "}" ++ local

nameClassToString (NsName uri)
    = "{" ++ uri ++ "}"

nameClassToString (NsNameExcept uri nc)
    = uri ++ "except (NsName) " ++ nameClassToString nc

nameClassToString (NameClassChoice nc1 nc2)
    = nameClassToString nc1 ++ " " ++ nameClassToString nc2

nameClassToString (NCError e)
    = "NameClass Error: " ++ e


-- ------------------------------------------------------------

{- |
Returns a tree representation of the pattern structure.
The hard work is done by 'formatTree'.

Example:

> +---element {}bar
>     |
>     +---group
>         |
>         +---oneOrMore
>         |   |
>         |   +---attribute AnyName
>         |       |
>         |       +---text
>         |
>         +---text

The function can be used to display circular ref-pattern structures.

Example:

> <define name="baz">
>   <element name="baz">
>     ... <ref name="baz"/> ...
>   </element>
> </define>

-}

patternToStringTree :: LA Pattern String
patternToStringTree
    = fromSLA [] pattern2PatternTree
      >>^
      (\p -> formatTree id p ++ "\n")


-- | Returns a tree representation of the pattern structure.
-- (see also: 'createPatternFromXmlTree' and 'patternToStringTree')

xmlTreeToPatternStringTree :: LA XmlTree String
xmlTreeToPatternStringTree
    = createPatternFromXmlTree
      >>>
      patternToStringTree


pattern2PatternTree :: SLA [NameClass] Pattern PatternTree
pattern2PatternTree
    = choiceA
      [ isA isRelaxEmpty      :-> (constA $ NTree "empty" [])
      , isA isRelaxNotAllowed :-> notAllowed2PatternTree
      , isA isRelaxText       :-> (constA $ NTree "text" [])
      , isA isRelaxChoice     :-> choice2PatternTree
      , isA isRelaxInterleave :-> children2PatternTree "interleave"
      , isA isRelaxGroup      :-> children2PatternTree "group"
      , isA isRelaxOneOrMore  :-> children2PatternTree "oneOrMore"
      , isA isRelaxList       :-> children2PatternTree "list"
      , isA isRelaxData       :-> data2PatternTree
      , isA isRelaxDataExcept :-> dataExcept2PatternTree
      , isA isRelaxValue      :-> value2PatternTree
      , isA isRelaxAttribute  :-> createPatternTreeFromElement "attribute"
      , isA isRelaxElement    :-> element2PatternTree
      , isA isRelaxAfter      :-> children2PatternTree "after"
      ]

notAllowed2PatternTree :: SLA [NameClass] Pattern PatternTree
notAllowed2PatternTree
    = arr $ \(NotAllowed (ErrMsg _l sl)) -> NTree "notAllowed" $ map (\ s -> NTree s []) sl


data2PatternTree :: SLA [NameClass] Pattern PatternTree
data2PatternTree
    = arr $ \ (Data d p) -> NTree "data" [ datatype2PatternTree d
                                         , mapping2PatternTree "parameter" p
                                         ]

dataExcept2PatternTree :: SLA [NameClass] Pattern PatternTree
dataExcept2PatternTree
    = this &&& (listA $ arrL getChildrenPattern >>> pattern2PatternTree)
      >>>
      arr2 ( \ (DataExcept d param _) pattern ->
             NTree "dataExcept" ([ datatype2PatternTree d
                                 , mapping2PatternTree "parameter" param
                                 ] ++ pattern)
           )

value2PatternTree :: SLA [NameClass] Pattern PatternTree
value2PatternTree
    = arr $ \ (Value d v c) -> NTree ("value = " ++ v) [ datatype2PatternTree d
                                                       , context2PatternTree c
                                                       ]


createPatternTreeFromElement :: String -> SLA [NameClass] Pattern PatternTree
createPatternTreeFromElement name
    = ( arr getNameClassFromPattern
        &&&
        listA (arrL getChildrenPattern >>> pattern2PatternTree)
      )
      >>>
      arr2 (\nc rl -> NTree (name ++ " " ++ show nc) rl)

children2PatternTree :: String -> SLA [NameClass] Pattern PatternTree
children2PatternTree name
    = listA (arrL getChildrenPattern >>> pattern2PatternTree)
      >>^
      (NTree name)

choice2PatternTree :: SLA [NameClass] Pattern PatternTree
choice2PatternTree
    = ifA ( -- wenn das zweite kind ein noch nicht ausgegebenes element ist,
            -- muss dieses anders behandelt werden
            -- nur fuer bessere formatierung des outputs
            arr (last . getChildrenPattern) >>> isA (isRelaxElement) >>>
            (arr getNameClassFromPattern &&& getState) >>>
            isA(\ (nc, liste) -> not $ elem nc liste)
          )
      ( -- element in status aufnehmen, wird dann nicht mehr vom erste kind ausgegeben
        arr getChildrenPattern
        >>>
        changeState (\s p -> (getNameClassFromPattern (last p)) : s)
        >>>
        ( ( head ^>> pattern2PatternTree )              -- erstes kind normal verarbeiten
          &&&                                           -- zweites kind, das element, verarbeiten
          ( last ^>> createPatternTreeFromElement "element" )
        )
        >>>
        arr2 ( \ l1 l2 -> NTree "choice" [l1, l2] )
      )
      ( children2PatternTree "choice" )


element2PatternTree :: SLA [NameClass] Pattern PatternTree
element2PatternTree
    = ifA ( (arr getNameClassFromPattern &&& getState)
            >>>
            isA (\ (nc, liste) -> elem nc liste)
          )
      ( arr getNameClassFromPattern
        >>>
        arr (\nc -> NTree ("reference to element " ++ show nc) [])
      )
      ( changeState (\ s p -> (getNameClassFromPattern p) : s)
        >>>
        createPatternTreeFromElement "element"
      )


mapping2PatternTree :: String -> [(Prefix, Uri)] -> PatternTree
mapping2PatternTree name mapping
    = NTree name (map (\(a, b) -> NTree (a ++ " = " ++ b) []) mapping)


datatype2PatternTree :: Datatype -> PatternTree
datatype2PatternTree dt
    = NTree (datatype2String dt) []


context2PatternTree :: Context -> PatternTree
context2PatternTree (base, mapping)
    = NTree "context" [ NTree ("base-uri = " ++ base) []
                      , mapping2PatternTree "namespace environment" mapping
                      ]

-- ------------------------------------------------------------

-- | Returns a formated string representation of the pattern structure.
-- (see also: 'createPatternFromXmlTree' and 'patternToFormatedString')
xmlTreeToPatternFormatedString :: LA XmlTree String
xmlTreeToPatternFormatedString
    = createPatternFromXmlTree
      >>>
      fromSLA [] patternToFormatedString


{- |
Returns a formated string representation of the pattern structure.

Example:

> Element {}foo (Choice (Choice ( Value = abc,
> datatypelibrary = http://relaxng.org/ns/structure/1.0, type = token,
> context (base-uri =file://test.rng,
> parameter: xml = http://www.w3.org/XML/1998/namespaces, foo = www.bar.baz),

The function can be used to display circular ref-pattern structures.
-}

patternToFormatedString :: SLA [NameClass] Pattern String
patternToFormatedString
    = choiceA
      [ isA isRelaxEmpty      :-> (constA " empty ")
      , isA isRelaxNotAllowed :-> (arr $ \ (NotAllowed errorEnv) -> show errorEnv)
      , isA isRelaxText       :-> (constA " text ")
      , isA isRelaxChoice     :-> children2FormatedString "choice"
      , isA isRelaxInterleave :-> children2FormatedString "interleave"
      , isA isRelaxGroup      :-> children2FormatedString "group"
      , isA isRelaxOneOrMore  :-> children2FormatedString "oneOrMore"
      , isA isRelaxList       :-> children2FormatedString "list"
      , isA isRelaxData       :-> data2FormatedString
      , isA isRelaxDataExcept :-> dataExcept2FormatedString
      , isA isRelaxValue      :-> value2FormatedString
      , isA isRelaxAttribute  :-> createFormatedStringFromElement "attribute"
      , isA isRelaxElement    :-> element2FormatedString
      , isA isRelaxAfter      :-> children2FormatedString "after"
      ]

children2FormatedString :: String -> SLA [NameClass] Pattern String
children2FormatedString name
    = listA (arrL getChildrenPattern >>> patternToFormatedString)
      >>^
      (\ l -> name ++ " (" ++ formatStringListPatt l ++ ") " )

data2FormatedString :: SLA [NameClass] Pattern String
data2FormatedString
    = arr ( \ (Data datatype paramList) ->
            "Data " ++ datatype2String datatype ++ "\n " ++
            mapping2String "parameter" paramList ++ "\n"
          )

dataExcept2FormatedString :: SLA [NameClass] Pattern String
dataExcept2FormatedString
 = arr ( \ (DataExcept datatype paramList _) ->
         "DataExcept " ++ show datatype ++ "\n " ++
         mapping2String "parameter" paramList ++ "\n "
       )
   &&&
   ( arr (\ (DataExcept _ _ p) -> p) >>> patternToFormatedString )
   >>>
   arr2 (++)


value2FormatedString :: SLA [NameClass] Pattern String
value2FormatedString
 = arr $ \(Value datatype val context) ->
           "Value = " ++ val ++ ", " ++ datatype2String datatype ++
           "\n " ++ context2String context ++ "\n"


element2FormatedString :: SLA [NameClass] Pattern String
element2FormatedString
    = ifA ( (arr getNameClassFromPattern &&& getState)
            >>>
            isA (\ (nc, liste) -> elem nc liste)
          )
      ( arr getNameClassFromPattern
        >>^
        ( \nc -> "reference to element " ++ nameClassToString nc ++ " " )
      )
      ( changeState (\ s p -> (getNameClassFromPattern p) : s)
        >>>
        createFormatedStringFromElement "element"
      )


createFormatedStringFromElement :: String -> SLA [NameClass] Pattern String
createFormatedStringFromElement name
    = ( arr getNameClassFromPattern
        &&&
        ( listA (arrL getChildrenPattern >>> patternToFormatedString)
          >>^
          formatStringListId
        )
      )
      >>>
      arr2 (\ nc rl -> name ++ " " ++ nameClassToString nc ++ " (" ++ rl ++ ")")


-- ------------------------------------------------------------

mapping2String :: String -> [(Prefix, Uri)] -> String
mapping2String name mapping
    = name ++ ": " ++
      formatStringList id ", " (map (\(a, b) -> a ++ " = " ++ b) mapping)

datatype2String :: Datatype -> String
datatype2String (lib, localName)
    = "datatypelibrary = " ++ getLib ++ ", type = " ++ localName
    where
    getLib = if lib == "" then relaxNamespace else lib

context2String :: Context -> String
context2String (base, mapping)
    = "context (base-uri = " ++ base ++ ", " ++
      mapping2String "namespace environment" mapping ++ ")"

-- ------------------------------------------------------------
