-- | This module defines an abstract syntax tree for the @GraphQL@ language based on
--   <https://facebook.github.io/graphql/ Facebook's GraphQL Specification>.
--
-- Target AST for Parser.
module Language.GraphQL.AST
    ( Alias
    , Argument(..)
    , Definition(..)
    , Directive(..)
    , Document
    , Field(..)
    , FragmentDefinition(..)
    , FragmentSpread(..)
    , InlineFragment(..)
    , Name
    , NonNullType(..)
    , ObjectField(..)
    , OperationDefinition(..)
    , OperationType(..)
    , Selection(..)
    , SelectionSet
    , SelectionSetOpt
    , Type(..)
    , TypeCondition
    , Value(..)
    , VariableDefinition(..)
    ) where

import Data.Int (Int32)
import Data.List.NonEmpty (NonEmpty)
import Data.Text (Text)

-- * Document

-- | GraphQL document.
type Document = NonEmpty Definition

-- | Name
type Name = Text

-- | Directive.
data Directive = Directive Name [Argument] deriving (Eq, Show)

-- * Operations

-- | Top-level definition of a document, either an operation or a fragment.
data Definition
    = DefinitionOperation OperationDefinition
    | DefinitionFragment FragmentDefinition
    deriving (Eq, Show)

-- | Operation definition.
data OperationDefinition
    = OperationSelectionSet SelectionSet
    | OperationDefinition OperationType
                          (Maybe Name)
                          [VariableDefinition]
                          [Directive]
                          SelectionSet
    deriving (Eq, Show)

-- | GraphQL has 3 operation types: queries, mutations and subscribtions.
--
-- Currently only queries and mutations are supported.
data OperationType = Query | Mutation deriving (Eq, Show)

-- * Selections

-- | "Top-level" selection, selection on an operation or fragment.
type SelectionSet = NonEmpty Selection

-- | Field selection.
type SelectionSetOpt = [Selection]

-- | Single selection element.
data Selection
    = SelectionField Field
    | SelectionFragmentSpread FragmentSpread
    | SelectionInlineFragment InlineFragment
    deriving (Eq, Show)

-- * Field

-- | Single GraphQL field.
--
-- The only required property of a field is its name. Optionally it can also
-- have an alias, arguments or a list of subfields.
--
-- Given the following query:
--
-- @
-- {
--   zuck: user(id: 4) {
--     id
--     name
--   }
-- }
-- @
--
-- * "user", "id" and "name" are field names.
-- * "user" has two subfields, "id" and "name".
-- * "zuck" is an alias for "user". "id" and "name" have no aliases.
-- * "id: 4" is an argument for "user". "id" and "name" don't have any
-- arguments.
data Field
    = Field (Maybe Alias) Name [Argument] [Directive] SelectionSetOpt
    deriving (Eq, Show)

-- | Alternative field name.
--
-- @
-- {
--   smallPic: profilePic(size: 64)
--   bigPic: profilePic(size: 1024)
-- }
-- @
--
-- Here "smallPic" and "bigPic" are aliases for the same field, "profilePic",
-- used to distinquish between profile pictures with different arguments
-- (sizes).
type Alias = Name

-- | Single argument.
--
-- @
-- {
--   user(id: 4) {
--     name
--   }
-- }
-- @
--
--  Here "id" is an argument for the field "user" and its value is 4.
data Argument = Argument Name Value deriving (Eq,Show)

-- * Fragments

-- | Fragment spread.
data FragmentSpread = FragmentSpread Name [Directive] deriving (Eq, Show)

-- | Inline fragment.
data InlineFragment = InlineFragment (Maybe TypeCondition) [Directive] SelectionSet
                      deriving (Eq, Show)

-- | Fragment definition.
data FragmentDefinition
    = FragmentDefinition Name TypeCondition [Directive] SelectionSet
    deriving (Eq, Show)

-- * Inputs

-- | Input value.
data Value = Variable Name
           | Int Int32
           | Float Double
           | String Text
           | Boolean Bool
           | Null
           | Enum Name
           | List [Value]
           | Object [ObjectField]
           deriving (Eq, Show)

-- | Key-value pair.
--
-- A list of 'ObjectField's represents a GraphQL object type.
data ObjectField = ObjectField Name Value deriving (Eq, Show)

-- | Variable definition.
data VariableDefinition = VariableDefinition Name Type (Maybe Value)
                          deriving (Eq, Show)

-- | Type condition.
type TypeCondition = Name

-- | Type representation.
data Type = TypeNamed   Name
          | TypeList    Type
          | TypeNonNull NonNullType
            deriving (Eq, Show)

-- | Helper type to represent Non-Null types and lists of such types.
data NonNullType = NonNullTypeNamed Name
                 | NonNullTypeList  Type
                   deriving (Eq, Show)