{-# OPTIONS -fglasgow-exts #-}
module XML (tests) where
import Test.HUnit
import Control.Monad
import Data.Maybe
import Data.Generics
import CompanyDatatypes
data Element   = Elem Name [Attribute] [Content]
                 deriving (Show, Eq, Typeable, Data)
data Content   = CElem Element
               | CString Bool CharData
                        
               | CRef Reference
               | CMisc Misc
                 deriving (Show, Eq, Typeable, Data)
type CharData = String
type Attribute = ()
type Reference = ()
type Misc      = ()
data2content :: Data a => a -> [Content]
data2content =         element
               `ext1Q` list
               `extQ`  string 
               `extQ`  float
 where
  
  element x = [CElem (Elem (tyconUQname (dataTypeName (dataTypeOf x)))
                           [] 
                           (concat (gmapQ data2content x)))]
  
  list :: Data a => [a] -> [Content]
  list = concat . map data2content
  
  string :: String -> [Content]
  string x = [CString True x]
  
  float :: Float -> [Content]
  float x = [CString True (show x)]
content2data :: forall a. Data a => ReadX a
content2data = result
 where
 
  
  result =         element
           `ext1R` list
           `extR`  string
           `extR`  float
  
  myType = myTypeOf result
    where
      myTypeOf :: forall a. ReadX a -> a
      myTypeOf =  undefined
  
  element = do c <- readX
               case c of
                 (CElem (Elem x as cs))
                    |    as == [] 
                      && x  == (tyconUQname (dataTypeName (dataTypeOf myType)))
                   -> alts cs
                 _ -> mzero
  
  list :: forall a. Data a => ReadX [a]
  list =          ( do h <- content2data
                       t <- list
                       return (h:t) )
         `mplus`  return []
  
  alts cs = foldr (mplus . recurse cs) mzero shapes
  
  shapes = map fromConstr consOf
  
  consOf = dataTypeConstrs
         $ dataTypeOf 
         $ myType
  
  recurse cs x = maybe mzero
                       return
                       (runReadX (gmapM (const content2data) x) cs)
  
  string :: ReadX String
  string =  do c <- readX
               case c of
                 (CString _ x) -> return x
                 _             -> mzero
  
  float :: ReadX Float
  float =  do c <- readX
              case c of
                (CString _ x) -> return (read x)
                _             -> mzero
newtype ReadX a =
        ReadX { unReadX :: [Content]
                        -> Maybe ([Content], a) }
runReadX x y = case unReadX x y of 
                 Just ([],y) -> Just y
                 _           -> Nothing
readX :: ReadX Content
readX =  ReadX (\x -> if null x 
                        then Nothing
                        else Just (tail x, head x)
               )
instance Monad ReadX where
  return x = ReadX (\y -> Just (y,x))
  c >>= f  = ReadX (\x -> case unReadX c x of
                            Nothing -> Nothing
                            Just (x', a) -> unReadX (f a) x'
                   )
instance MonadPlus ReadX where
  mzero = ReadX (const Nothing)
  f `mplus` g = ReadX (\x -> case unReadX f x of
                               Nothing -> unReadX g x
                               y -> y
                      )
tests = (   genCom
        , ( data2content genCom
        , ( zigzag person1 :: Maybe Person
        , ( zigzag genCom  :: Maybe Company
        , ( zigzag genCom == Just genCom
        ))))) ~=? output
 where 
  
  zigzag :: Data a => a -> Maybe a
  zigzag = runReadX content2data . data2content
output = (C [D "Research" (E (P "Laemmel" "Amsterdam") (S 8000.0)) [PU (E (P "Joost" "Amsterdam") (S 1000.0)),PU (E (P "Marlow" "Cambridge") (S 2000.0))],D "Strategy" (E (P "Blair" "London") (S 100000.0)) []],([CElem (Elem "Company" [] [CElem (Elem "Dept" [] [CString True "Research",CElem (Elem "Employee" [] [CElem (Elem "Person" [] [CString True "Laemmel",CString True "Amsterdam"]),CElem (Elem "Salary" [] [CString True "8000.0"])]),CElem (Elem "Unit" [] [CElem (Elem "Employee" [] [CElem (Elem "Person" [] [CString True "Joost",CString True "Amsterdam"]),CElem (Elem "Salary" [] [CString True "1000.0"])])]),CElem (Elem "Unit" [] [CElem (Elem "Employee" [] [CElem (Elem "Person" [] [CString True "Marlow",CString True "Cambridge"]),CElem (Elem "Salary" [] [CString True "2000.0"])])])]),CElem (Elem "Dept" [] [CString True "Strategy",CElem (Elem "Employee" [] [CElem (Elem "Person" [] [CString True "Blair",CString True "London"]),CElem (Elem "Salary" [] [CString True "100000.0"])])])])],(Just (P "Lazy" "Home"),(Just (C [D "Research" (E (P "Laemmel" "Amsterdam") (S 8000.0)) [PU (E (P "Joost" "Amsterdam") (S 1000.0)),PU (E (P "Marlow" "Cambridge") (S 2000.0))],D "Strategy" (E (P "Blair" "London") (S 100000.0)) []]),True))))