-- Copyright (c) Microsoft. All rights reserved.
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.

{-# LANGUAGE OverloadedStrings, RecordWildCards, DeriveGeneric #-}

{-|
Copyright   : (c) Microsoft
License     : MIT
Maintainer  : adamsap@microsoft.com
Stability   : provisional
Portability : portable

A suite of types describing the abstract syntax tree of the Bond
<https://microsoft.github.io/bond/manual/compiler.html#idl-syntax schema definition language>.
-}

module Language.Bond.Syntax.Types
    ( -- * Schema definition file
      Bond(..)
    , QualifiedName
    , Import(..)
    , Namespace(..)
      -- ** Declarations
    , Declaration(..)
    , Field(..)
    , Default(..)
    , Modifier(..)
    , Constant(..)
      -- ** Types
    , Type(..)
    , TypeParam(..)
    , Constraint(..)
      -- ** Comm
    , Method(..)
      -- ** Metadata
    , Attribute(..)
      -- ** Deprecated
    , Language(..)
    ) where

import Data.Word

-- | Represents fully qualified name
type QualifiedName = [String]

-- | Specifies whether a field is required or optional.
data Modifier =
    Optional |                              -- ^ field is optional and may be omitted during serialization
    Required |                              -- ^ field is required, deserialization will fail if it is missing
    RequiredOptional                        -- ^ deserialization will not fail if the field is missing but it can't be omitted during serialization
    deriving (Eq, Show)

-- | Type in the Bond type system
data Type =
    BT_Int8 | BT_Int16 | BT_Int32 | BT_Int64 |
    BT_UInt8 | BT_UInt16 | BT_UInt32 | BT_UInt64 |
    BT_Float | BT_Double |
    BT_Bool |
    BT_String | BT_WString |
    BT_MetaName | BT_MetaFullName |
    BT_Blob |
    BT_Maybe Type |                         -- ^ type for fields with the default value of @nothing@
    BT_List Type |
    BT_Vector Type |
    BT_Nullable Type |
    BT_Set Type |
    BT_Map Type Type |
    BT_Bonded Type |
    BT_IntTypeArg Int |                     -- ^ an integer argument in an instance of a generic type 'Alias'
    BT_TypeParam TypeParam |                -- ^ type parameter of a generic 'Struct' or 'Alias' declaration
    BT_UserDefined Declaration [Type]       -- ^ user defined type or an instance of a generic type with the specified type arguments
    deriving (Eq, Show)

-- | Default value of a field.
data Default =
    DefaultBool Bool |
    DefaultInteger Integer |
    DefaultFloat Double |
    DefaultString String |
    DefaultEnum String |                    -- ^ name of an enum 'Constant'
    DefaultNothing                          -- ^ explicitly specified default value of @nothing@
    deriving (Eq, Show)

-- | <https://microsoft.github.io/bond/manual/compiler.html#custom-attributes Attribute> for attaching user defined metadata to a 'Declaration' or a 'Field'
data Attribute =
    Attribute
        { attrName :: QualifiedName         -- attribute name
        , attrValue :: String               -- value
        }
    deriving (Eq, Show)

-- | Definition of a 'Struct' field.
data Field =
    Field
        { fieldAttributes :: [Attribute]    -- zero or more attributes
        , fieldOrdinal :: Word16            -- ordinal
        , fieldModifier :: Modifier         -- field modifier
        , fieldType :: Type                 -- type
        , fieldName :: String               -- field name
        , fieldDefault :: Maybe Default     -- optional default value
        }
    deriving (Eq, Show)

-- | Definition of an 'Enum' constant.
data Constant =
    Constant
        { constantName :: String            -- enum constant name
        , constantValue :: Maybe Int        -- optional constant value
        }
    deriving (Eq, Show)

-- | Constraint on a 'TypeParam'.
data Constraint = Value                     -- ^ the type parameter allows only value types
    deriving (Eq, Show)

-- | Type parameter of a <https://microsoft.github.io/bond/manual/compiler.html#generics generic> 'Struct' or type 'Alias'
data TypeParam =
    TypeParam
        { paramName :: String
        , paramConstraint :: Maybe Constraint
        }
    deriving (Eq, Show)

-- | Method of a service
data Method =
    Function
        { methodAttributes :: [Attribute]   -- zero or more attributes
        , methodResult :: Maybe Type        -- method result
        , methodName :: String              -- method name
        , methodInput :: Maybe Type         -- method parameter
        }
    |
    Event
        { methodAttributes :: [Attribute]   -- zero or more attributes
        , methodName :: String              -- method name
        , methodInput :: Maybe Type         -- method parameter
        }
    deriving (Eq, Show)

-- | Bond schema declaration
data Declaration =
    Struct
        { declNamespaces :: [Namespace]     -- namespace(s) in which the struct is declared
        , declAttributes :: [Attribute]     -- zero or more attributes
        , declName :: String                -- struct identifier
        , declParams :: [TypeParam]         -- list of type parameters for generics
        , structBase :: Maybe Type          -- optional base struct
        , structFields :: [Field]           -- zero or more fields
        }
    |                                       -- ^ <https://microsoft.github.io/bond/manual/compiler.html#struct-definition struct definition>
    Enum
        { declNamespaces :: [Namespace]     -- namespace(s) in which the enum is declared
        , declAttributes :: [Attribute]     -- zero or more attributes
        , declName :: String                -- enum identifier
        , enumConstants :: [Constant]       -- one or more enum constant values
        }
    |                                       -- ^ <https://microsoft.github.io/bond/manual/compiler.html#enum-definition enum definition>
    Forward
        { declNamespaces :: [Namespace]     -- namespace(s) in which the struct is declared
        , declName :: String                -- struct identifier
        , declParams :: [TypeParam]         -- type parameters for generics
        }
    |                                       -- ^ <https://microsoft.github.io/bond/manual/compiler.html#forward-declaration forward declaration>
    Alias
        { declNamespaces :: [Namespace]     -- namespace(s) in which the type alias is declared
        , declName :: String                -- alias identifier
        , declParams :: [TypeParam]         -- type parameters for generics
        , aliasType :: Type                 -- aliased type
        }                                   -- ^ <https://microsoft.github.io/bond/manual/compiler.html#type-aliases type alias definition>
    |
    Service
        { declNamespaces :: [Namespace]     -- namespace(s) in which the service is declared
        , declAttributes :: [Attribute]     -- zero or more attributes
        , declName :: String                -- service name
        , declParams :: [TypeParam]         -- type parameters for generic service
        , serviceMethods :: [Method]        -- zero or more methods
        }                                   -- ^ <https://microsoft.github.io/bond/manual/compiler.html#service-definition service definition>
    deriving (Eq, Show)

-- | <https://microsoft.github.io/bond/manual/compiler.html#import-statements Import> declaration.
data Import = Import FilePath
    deriving (Eq, Show)

-- | Language annotation for namespaces. Note that language-specific
-- namespaces are only supported for backward compatibility and are not
-- recommended.
data Language = Cpp | Cs | Java
    deriving (Eq, Show)

-- | <https://microsoft.github.io/bond/manual/compiler.html#namespace-definition Namespace> declaration.
data Namespace =
    Namespace
        { nsLanguage :: Maybe Language
        , nsName :: QualifiedName
        }
    deriving (Eq, Show)

-- | The top level type representing the Bond schema definition abstract syntax tree.
data Bond =
    Bond
        { bondImports :: [Import]
        , bondNamespaces :: [Namespace]
        , bondDeclarations :: [Declaration]
        }
    deriving (Eq, Show)