deriveXmlObject :: Options -> Name -> DecsQ
deriveXmlObject opts name = withType name $ \name tvbs cons -> do
  let ns = "Network.AuthorizeNet.TH.deriveXmlObject: Type - " ++ showName name ++ ": "
  let context = []
      ty = AppT (ConT ''SchemaType) (ConT name)
      con = case cons of
        [con] -> con
        _ -> error $ ns ++ "Expected exactly one constructor on type " ++ showName name
      conN = conName con
      xV = varE $ mkName "x"
      sV = varE $ mkName "s"
  let parseOneField :: VarStrictType -> Q Exp
      parseOneField (fieldNameRaw, _, ty) = do
        let fieldName = showName fieldNameRaw :: String
            xmlName = fieldLabelModifier opts fieldName
        let parseExpr = [| parseSchemaType $(litE $ stringL xmlName) |]
        case specialType ty of
              SMaybe -> [| optional $(parseExpr) |]
              SList  -> parseExpr
              SNone  -> parseExpr
      parseConstructor :: Con -> ExpQ
      parseConstructor (RecC name vsts) =
        let joinExpr = varE $ mkName "apply"
            exprs = [appE (varE $ mkName "return") (conE name) ] ++ map parseOneField vsts
        in joinExprs exprs joinExpr
      parseConstructor _ = error $ ns ++ "Unsupported constructor for type"
      decParseSchemaType :: DecsQ
      decParseSchemaType = do
        body <- [| do
                    (pos,e) <- posnElement [$(sV)]
                    commit $ interior e $ $(parseConstructor con)
                 |]
        return $ pure $ FunD (mkName "parseSchemaType") [Clause [VarP $ mkName "s"] (NormalB body) []]
      toXmlOneField :: VarStrictType -> ExpQ
      toXmlOneField (fieldName, _, ty) =
        let xmlName = fieldLabelModifier opts $ showName fieldName
            sttxE = [| schemaTypeToXML $(litE $ stringL xmlName) |]
        in case specialType ty of
          SMaybe -> [| maybe [] $(sttxE) $ $(appE (varE fieldName) xV) |]
          SList -> [| $(sttxE) $ $(appE (varE fieldName) xV) |]
          SNone -> [| $(sttxE) $ $(appE (varE fieldName) xV) |]
      decSchemaTypeToXml :: DecsQ
      decSchemaTypeToXml =
        let vsts = case con of
              NormalC{} -> error $ ns ++ "You must use record syntax when automatically deriving SchemaType instances"
              RecC _ vsts -> vsts
              _ -> error $ ns ++ "Unsupported constructor for type"
        in do
          let exps = map toXmlOneField vsts
          body <- [| toXMLElement $(varE $ mkName "s") [] $(listE exps) |]
          let clause = Clause [VarP $ mkName "s", AsP (mkName "x") $ RecP conN []] (NormalB body) []

          return $ pure $ FunD (mkName "schemaTypeToXML") [clause]
  decs <- concat <$> sequence [decParseSchemaType, decSchemaTypeToXml]
  -- If there is exactly one record and its a list, automatically derive IsLIst
  let decIsList :: DecsQ
      decIsList =
        case con of
          RecC _ [(fieldName, _, ty)] ->
            case specialType ty of
              SList -> deriveIsList name
              _ -> pure []
          _ -> pure []
  (++) <$> return [ InstanceD context ty decs ] <*> decIsList
deriveXmlObject :: Options -> Name -> DecsQ
deriveXmlObject opts name = withType name $ \name tvbs cons -> do
  let ns = "Network.AuthorizeNet.TH.deriveXmlObject: Type - " ++ showName name ++ ": "
  let context = []
      ty = AppT (ConT ''SchemaType) (ConT name)
      con = case cons of
        [con] -> con
        _ -> error $ ns ++ "Expected exactly one constructor on type " ++ showName name
      conN = conName con
      xV = varE $ mkName "x"
      sV = varE $ mkName "s"
  let parseOneField :: VarStrictType -> Q Exp
      parseOneField (fieldNameRaw, _, ty) = do
        let fieldName = showName fieldNameRaw :: String
            xmlName = fieldLabelModifier opts fieldName
        let parseExpr = [| parseSchemaType $(litE $ stringL xmlName) |]
        case specialType ty of
              SMaybe -> [| optional $(parseExpr) |]
              SList  -> parseExpr
              SNone  -> parseExpr
      parseConstructor :: Con -> ExpQ
      parseConstructor (RecC name vsts) =
        let joinExpr = varE $ mkName "apply"
            exprs = [appE (varE $ mkName "return") (conE name) ] ++ map parseOneField vsts
        in joinExprs exprs joinExpr
      parseConstructor _ = error $ ns ++ "Unsupported constructor for type"
      decParseSchemaType :: DecsQ
      decParseSchemaType = do
        body <- [| do
                    (pos,e) <- posnElement [$(sV)]
                    commit $ interior e $ $(parseConstructor con)
                 |]
        return $ pure $ FunD (mkName "parseSchemaType") [Clause [VarP $ mkName "s"] (NormalB body) []]
      toXmlOneField :: VarStrictType -> ExpQ
      toXmlOneField (fieldName, _, ty) =
        let xmlName = fieldLabelModifier opts $ showName fieldName
            sttxE = [| schemaTypeToXML $(litE $ stringL xmlName) |]
        in case specialType ty of
          SMaybe -> [| maybe [] $(sttxE) $ $(appE (varE fieldName) xV) |]
          SList -> [| $(sttxE) $ $(appE (varE fieldName) xV) |]
          SNone -> [| $(sttxE) $ $(appE (varE fieldName) xV) |]
      decSchemaTypeToXml :: DecsQ
      decSchemaTypeToXml =
        let vsts = case con of
              NormalC{} -> error $ ns ++ "You must use record syntax when automatically deriving SchemaType instances"
              RecC _ vsts -> vsts
              _ -> error $ ns ++ "Unsupported constructor for type"
        in do
          let exps = map toXmlOneField vsts
          body <- [| toXMLElement $(varE $ mkName "s") [] $(listE exps) |]
          let clause = Clause [VarP $ mkName "s", AsP (mkName "x") $ RecP conN []] (NormalB body) []

          return $ pure $ FunD (mkName "schemaTypeToXML") [clause]
  decs <- concat <$> sequence [decParseSchemaType, decSchemaTypeToXml]
  -- If there is exactly one record and its a list, automatically derive IsLIst
  let decIsList :: DecsQ
      decIsList =
        case con of
          RecC _ [(fieldName, _, ty)] ->
            case specialType ty of
              SList -> deriveIsList name
              _ -> pure []
          _ -> pure []
  (++) <$> return [ InstanceD context ty decs ] <*> decIsList
deriveXmlObject opts name = withType name $ \name tvbs cons -> do
  let ns = "Network.AuthorizeNet.TH.deriveXmlObject: Type - " ++ showName name ++ ": "
  let context = []
      ty = AppT (ConT ''SchemaType) (ConT name)
      con = case cons of
        [con] -> con
        _ -> error $ ns ++ "Expected exactly one constructor on type " ++ showName name
      conN = conName con
      xV = varE $ mkName "x"
      sV = varE $ mkName "s"
  let parseOneField :: VarStrictType -> Q Exp
      parseOneField (fieldNameRaw, _, ty) = do
        let fieldName = showName fieldNameRaw :: String
            xmlName = fieldLabelModifier opts fieldName
        let parseExpr = [| parseSchemaType $(litE $ stringL xmlName) |]
        case specialType ty of
              SMaybe -> [| optional $(parseExpr) |]
              SList  -> parseExpr
              SNone  -> parseExpr
      parseConstructor :: Con -> ExpQ
      parseConstructor (RecC name vsts) =
        let joinExpr = varE $ mkName "apply"
            exprs = [appE (varE $ mkName "return") (conE name) ] ++ map parseOneField vsts
        in joinExprs exprs joinExpr
      parseConstructor _ = error $ ns ++ "Unsupported constructor for type"
      decParseSchemaType :: DecsQ
      decParseSchemaType = do
        body <- [| do
                    (pos,e) <- posnElement [$(sV)]
                    commit $ interior e $ $(parseConstructor con)
                 |]
        return $ pure $ FunD (mkName "parseSchemaType") [Clause [VarP $ mkName "s"] (NormalB body) []]
      toXmlOneField :: VarStrictType -> ExpQ
      toXmlOneField (fieldName, _, ty) =
        let xmlName = fieldLabelModifier opts $ showName fieldName
            sttxE = [| schemaTypeToXML $(litE $ stringL xmlName) |]
        in case specialType ty of
          SMaybe -> [| maybe [] $(sttxE) $ $(appE (varE fieldName) xV) |]
          SList -> [| $(sttxE) $ $(appE (varE fieldName) xV) |]
          SNone -> [| $(sttxE) $ $(appE (varE fieldName) xV) |]
      decSchemaTypeToXml :: DecsQ
      decSchemaTypeToXml =
        let vsts = case con of
              NormalC{} -> error $ ns ++ "You must use record syntax when automatically deriving SchemaType instances"
              RecC _ vsts -> vsts
              _ -> error $ ns ++ "Unsupported constructor for type"
        in do
          let exps = map toXmlOneField vsts
          body <- [| toXMLElement $(varE $ mkName "s") [] $(listE exps) |]
          let clause = Clause [VarP $ mkName "s", AsP (mkName "x") $ RecP conN []] (NormalB body) []

          return $ pure $ FunD (mkName "schemaTypeToXML") [clause]
  decs <- concat <$> sequence [decParseSchemaType, decSchemaTypeToXml]
  -- If there is exactly one record and its a list, automatically derive IsLIst
  let decIsList :: DecsQ
      decIsList =
        case con of
          RecC _ [(fieldName, _, ty)] ->
            case specialType ty of
              SList -> deriveIsList name
              _ -> pure []
          _ -> pure []
  (++) <$> return [ InstanceD context ty decs ] <*> decIsList