{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

module YamlParse.Applicative.Explain where

import qualified Data.Text as T
import Data.Text (Text)
import Data.Validity
import Data.Validity.Text ()
import GHC.Generics (Generic)
import YamlParse.Applicative.Parser

-- A schema for a parser.
--
-- This is used to produce documentation for what/how the parser parses.
data Schema
  = EmptySchema
  | AnySchema
  | ExactSchema Text
  | NullSchema
  | MaybeSchema Schema
  | BoolSchema (Maybe Text)
  | NumberSchema (Maybe Text)
  | StringSchema (Maybe Text)
  | ArraySchema (Maybe Text) Schema
  | ObjectSchema (Maybe Text) Schema
  | FieldSchema Text Bool (Maybe Text) Schema
  | ListSchema Schema
  | MapSchema Schema
  | MapKeysSchema Schema
  | ApSchema Schema Schema -- We'll take this to mean 'and'
  | AltSchema [Schema]
  | CommentSchema Text Schema
  deriving (Show, Eq, Generic)

instance Validity Schema

-- | Use a parser to produce a schema that describes it for documentation.
--
-- Nothing means that nothing even needs to be parsed, you just get the 'a' without parsing anything.
-- This is for the 'pure' case.
explainParser :: Parser i o -> Schema
explainParser = go
  where
    go :: Parser i o -> Schema
    go = \case
      ParseAny -> AnySchema
      ParseExtra _ p -> go p
      ParseEq _ t _ -> ExactSchema t
      ParseNull -> NullSchema
      ParseMaybe p -> MaybeSchema $ go p
      ParseBool t _ -> BoolSchema t
      ParseNumber t _ -> NumberSchema t
      ParseString t ParseAny -> StringSchema t
      ParseString _ p -> go p
      ParseArray t p -> ArraySchema t $ go p
      ParseObject t p -> ObjectSchema t $ go p
      ParseField k fp -> case fp of
        FieldParserRequired p -> FieldSchema k True Nothing $ go p
        FieldParserOptional p -> FieldSchema k False Nothing $ go p
        FieldParserOptionalWithDefault p d -> FieldSchema k False (Just $ T.pack $ show d) $ go p
      ParseList p -> ListSchema $ go p
      ParseMap p -> MapSchema $ go p
      ParseMapKeys _ p -> MapKeysSchema $ go p
      ParsePure _ -> EmptySchema
      ParseFmap _ p -> go p
      ParseAp pf p -> ApSchema (go pf) (go p)
      ParseAlt ps -> AltSchema $ map go ps
      ParseComment t p -> CommentSchema t $ go p