{-# LANGUAGE NamedFieldPuns             #-}

module Data.Morpheus.Parsing.Internal.Internal
  ( Parser
  , Position
  , getLocation
  , processParser
  )
where

import qualified Data.List.NonEmpty            as NonEmpty
import           Data.Morpheus.Types.Internal.AST
                                                ( Position(..)
                                                , GQLError(..)
                                                , GQLErrors
                                                )
import           Data.Morpheus.Types.Internal.Resolving
                                                ( Eventless
                                                , failure
                                                , Result(..)
                                                )
import           Data.Text                      ( Text
                                                , pack
                                                )
import           Text.Megaparsec                ( ParseError
                                                , ParseErrorBundle
                                                  ( ParseErrorBundle
                                                  )
                                                , ParsecT
                                                , SourcePos
                                                , attachSourcePos
                                                , bundleErrors
                                                , bundlePosState
                                                , errorOffset
                                                , getSourcePos
                                                , parseErrorPretty
                                                , runParserT
                                                , SourcePos(..)
                                                , unPos
                                                )
import           Data.Void                      (Void)

getLocation :: Parser Position
getLocation = fmap toLocation getSourcePos

toLocation :: SourcePos -> Position
toLocation SourcePos { sourceLine, sourceColumn } =
  Position { line = unPos sourceLine, column = unPos sourceColumn }

type MyError = Void
type Parser = ParsecT MyError Text Eventless
type ErrorBundle = ParseErrorBundle Text MyError

processParser :: Parser a -> Text -> Eventless a
processParser parser txt = case runParserT parser [] txt of
  Success { result } -> case result of
    Right root       -> pure root
    Left  parseError -> failure (processErrorBundle parseError)
  Failure { errors } -> failure errors

processErrorBundle :: ErrorBundle -> GQLErrors
processErrorBundle = map parseErrorToGQLError . bundleToErrors
 where
  parseErrorToGQLError :: (ParseError Text MyError, SourcePos) -> GQLError
  parseErrorToGQLError (err, position) = GQLError
    { message   = pack (parseErrorPretty err)
      , locations = [toLocation position]
    }
  bundleToErrors
    :: ErrorBundle -> [(ParseError Text MyError, SourcePos)]
  bundleToErrors ParseErrorBundle { bundleErrors, bundlePosState } =
    NonEmpty.toList $ fst $ attachSourcePos errorOffset
                                            bundleErrors
                                            bundlePosState