module Sound.Audacity.XML.Parser where

import qualified Text.HTML.Tagchup.Tag as Tag
import qualified Text.XML.Basic.Attribute as Attr
import qualified Text.XML.Basic.Name.MixedCase as Name

import Text.Printf (printf)

import qualified Control.Monad.Trans.State as MS
import qualified Control.Monad.Trans.Maybe as MM
import qualified Control.Monad.Trans.Class as MT
import qualified Control.Monad.Exception.Synchronous as ME
import Control.Monad (MonadPlus, void, when, guard, mzero)
import Control.Applicative (many)

import qualified Data.List.HT as ListHT
import Data.String.HT (trim)
import Data.Char (isSpace)


type T = MS.StateT [Tag.T Name.T String] (MM.MaybeT (ME.Exceptional Message))
type Message = String


tag :: T (Tag.T Name.T String)
tag = MS.StateT $ MM.MaybeT . return . ListHT.viewL

fromMaybeGen :: (MonadPlus m) => Maybe a -> m a
fromMaybeGen = maybe mzero return

fromMaybe :: Maybe a -> T a
fromMaybe = MT.lift . MM.MaybeT . return

tagOpen :: Tag.Name Name.T -> T [Attr.T Name.T String]
tagOpen name = do
   x <- tag
   (foundName, attrs) <- fromMaybe $ Tag.maybeOpen x
   guard $ foundName == name
   return attrs

tagClose :: Tag.Name Name.T -> T ()
tagClose name = do
   x <- tag
   foundName <- fromMaybe $ Tag.maybeClose x
   guard $ foundName == name

lookupAttr :: String -> [Attr.T Name.T String] -> T String
lookupAttr name attrs =
   MT.lift $ MT.lift $
      ME.fromMaybe
         (printf "did not find attribute %s in%s"
            name (Attr.formatListBlankHead attrs "")) $
      Attr.lookupLit name attrs

lookupAttrRead :: (Read a) => String -> [Attr.T Name.T String] -> T a
lookupAttrRead name attrs = do
   str <- lookupAttr name attrs
   case reads str of
      [(x, "")] -> return x
      _ ->
         MT.lift $ MT.lift $ ME.throw $
            "could not parse attribute value " ++ str

lookupAttrBool :: String -> [Attr.T Name.T String] -> T Bool
lookupAttrBool name attrs = do
   str <- lookupAttr name attrs
   case str of
      "0" -> return False
      "1" -> return True
      _ -> MT.lift $ MT.lift $ ME.throw $ "not a bool value: " ++ str

skipSpace :: T ()
skipSpace = do
   x <- tag
   text <- fromMaybe $ Tag.maybeText x
   when (not $ all isSpace text) $ MT.lift $ MT.lift $
      ME.throw $ "expected spaces, but found: " ++ show (trim text)

skipSpaces :: T ()
skipSpaces = void $ many skipSpace