module Language.CSharp.Parser.Declaration where



import Text.Parsec                       hiding (Empty)

import Language.CSharp.Lexer

import Language.CSharp.Syntax

import Language.CSharp.Parser.Utility

import Language.CSharp.Parser.Statement

import Language.CSharp.Parser.Expression

import Language.CSharp.Parser.Type

import Language.CSharp.Parser.Attribute



pDeclaration :: P Declaration

pDeclaration = try pNamespace <|> TypeDeclaration <$> pTypeDeclaration



--------------------------------------------------------------------------------

-- Namespace and type declarations.

--------------------------------------------------------------------------------



pNamespace :: P Declaration

pNamespace = do

    attributes <- pGlobalAttributeSections

    pToken TKWnamespace

    name         <- pName

    declarations <- between pOCurly pCCurly (many pDeclaration)

    return $ NamespaceDeclaration attributes name declarations



pTypeDeclaration :: P TypeDeclaration

pTypeDeclaration = choice 

    [ try pClass, try pStruct  , try pInterface

    , try pEnum , try pDelegate ]



--------------------------------------------------------------------------------

-- Enum declarations.

--------------------------------------------------------------------------------



pEnum :: P TypeDeclaration

pEnum = do

    attributes <- pAttributeSections

    modifiers  <- many pEnumModifier

    pToken TKWenum

    name     <- pIdentifier

    inherits <- optionMaybe (pColon *> pIntegralType)

    body     <- betweenCurly pEnumBody

    optional pSemi

    return $ EnumTypeDeclaration attributes modifiers name inherits body



pEnumBody :: P EnumBody

pEnumBody = EnumBody <$> sepBy pEnumMemberDeclaration pComma



pEnumMemberDeclaration :: P EnumMemberDeclaration

pEnumMemberDeclaration =

    EnumMemberDeclaration <$> pIdentifier <*> optionMaybe (pEqualSign *> pExpression)



pEnumModifier :: P Modifier

pEnumModifier = choice [ New       <$ pToken TKWnew

                       , Public    <$ pToken TKWpublic    

                       , Protected <$ pToken TKWprotected 

                       , Internal  <$ pToken TKWinternal  

                       , Private   <$ pToken TKWprivate   ]



--------------------------------------------------------------------------------

-- Struct declarations.

--------------------------------------------------------------------------------



pStruct :: P TypeDeclaration

pStruct = do

    attributes <- pAttributeSections

    modifiers  <- (++) <$> many pStructModifier <*> pOptionPartial

    pToken TKWstruct

    name        <- pIdentifier

    typeParams  <- option [] pTypeParameters

    inherits    <- option [] (pColon *> sepBy1 pTypeName pComma)

    constraints <- many pTypeParameterConstraintClause

    body        <- between pOCurly pCCurly pStructBody

    optional pSemi

    return $ StructTypeDeclaration attributes modifiers name typeParams inherits constraints body



pStructBody :: P StructBody

pStructBody = StructBody <$> many pMemberDeclaration



pStructModifier :: P Modifier

pStructModifier = choice [ New       <$ pToken TKWnew      

                         , Public    <$ pToken TKWpublic   

                         , Protected <$ pToken TKWprotected

                         , Internal  <$ pToken TKWinternal 

                         , Private   <$ pToken TKWprivate   ]



--------------------------------------------------------------------------------

-- Delegate declarations.

--------------------------------------------------------------------------------



pDelegate :: P TypeDeclaration

pDelegate = do

    attributes <- pAttributeSections

    modifiers  <- many pDelegateModifier

    pToken TKWdelegate

    ty          <- pTypeWithVoid

    name        <- pIdentifier

    typeParams  <- pVariantTypeParameters

    params      <- betweenParens pFormalParams

    constraints <- many pTypeParameterConstraintClause

    pSemi

    return $ DelegateTypeDeclaration attributes modifiers ty name typeParams params constraints



pDelegateModifier :: P Modifier

pDelegateModifier = choice

    [ New       <$ pToken TKWnew

    , Public    <$ pToken TKWpublic

    , Protected <$ pToken TKWprotected

    , Internal  <$ pToken TKWinternal

    , Private   <$ pToken TKWprivate ]



--------------------------------------------------------------------------------

-- interface declarations.

--------------------------------------------------------------------------------



pInterface :: P TypeDeclaration

pInterface = do

    attributes <- pAttributeSections

    modifiers  <- (++) <$> many pInterfaceModifier <*> pOptionPartial

    pToken TKWinterface

    name        <- pIdentifier

    typeParams  <- option [] pVariantTypeParameters

    inherits    <- option [] (pColon *> sepBy1 pTypeName pComma)

    constraints <- many pTypeParameterConstraintClause

    body        <- pInterfaceBody

    optional pSemi

    return $ InterfaceTypeDeclaration attributes modifiers name typeParams inherits constraints body



pInterfaceBody :: P InterfaceBody

pInterfaceBody = InterfaceBody <$> betweenCurly (many pInterfaceMemberDeclaration)



pInterfaceMemberDeclaration :: P InterfaceMemberDeclaration

pInterfaceMemberDeclaration = choice

    [ try pInterfaceMethodMemberDeclaration

    , try pInterfacePropertyMemberDeclaration

    , try pInterfaceEventMemberDeclaration

    , pInterfaceIndexerMemberDeclaration ]



pInterfaceMethodMemberDeclaration :: P InterfaceMemberDeclaration

pInterfaceMethodMemberDeclaration = do

    attributes <- pAttributeSections

    modifiers  <- option [] ([New] <$ pToken TKWnew)

    ty         <- pTypeWithVoid

    name       <- pIdentifier

    typeParams <- option [] pTypeParameters

    params     <- betweenParens pFormalParams

    constraints <- many pTypeParameterConstraintClause

    pSemi

    return $ InterfaceMethodMemberDeclaration attributes modifiers ty name typeParams params constraints



pInterfacePropertyMemberDeclaration :: P InterfaceMemberDeclaration

pInterfacePropertyMemberDeclaration = do

    attributes <- pAttributeSections

    modifiers  <- option [] ([New] <$ pToken TKWnew)

    ty         <- pType

    name       <- pIdentifier

    (accessor1, accessor2) <- betweenCurly (eitherOrBoth (try pGetInterfaceAccessor) (try pSetInterfaceAccessor))

    return $ InterfacePropertyMemberDeclaration attributes modifiers ty name accessor1 accessor2

    

pInterfaceEventMemberDeclaration :: P InterfaceMemberDeclaration

pInterfaceEventMemberDeclaration = do

    attributes <- pAttributeSections

    modifiers  <- option [] ([New] <$ pToken TKWnew)

    pToken TKWevent

    ty   <- pType

    name <- pIdentifier

    pSemi

    return $ InterfaceEventMemberDeclaration attributes modifiers ty name



pInterfaceIndexerMemberDeclaration :: P InterfaceMemberDeclaration

pInterfaceIndexerMemberDeclaration = do

    attributes <- pAttributeSections

    modifiers  <- option [] ([New] <$ pToken TKWnew)

    ty         <- pType

    pToken TKWthis

    params <- betweenSquare pFormalParams   

    (accessor1, accessor2) <- betweenCurly (eitherOrBoth (try pGetInterfaceAccessor) (try pSetInterfaceAccessor))

    return $ InterfaceIndexerMemberDeclaration attributes modifiers ty params accessor1 accessor2



pGetInterfaceAccessor :: P InterfaceAccessor

pGetInterfaceAccessor = 

    GetInterfaceAccessor <$> pAttributeSections <* pIdentifierKeyword "get" <* pSemi



pSetInterfaceAccessor :: P InterfaceAccessor

pSetInterfaceAccessor = 

    SetInterfaceAccessor <$> pAttributeSections <* pIdentifierKeyword "set" <* pSemi



pInterfaceModifier :: P Modifier

pInterfaceModifier = choice

    [ New       <$ pToken TKWnew      , Public   <$ pToken TKWpublic

    , Protected <$ pToken TKWprotected, Internal <$ pToken TKWinternal

    , Private   <$ pToken TKWprivate  ]



--------------------------------------------------------------------------------

-- Class declarations.

--------------------------------------------------------------------------------



pClass :: P TypeDeclaration

pClass = do

    attributes <- pAttributeSections

    modifiers  <- (++) <$> many pClassModifier <*> pOptionPartial

    pToken TKWclass

    name        <- pIdentifier

    typeParams  <- option [] pTypeParameters

    inherits    <- option [] (pColon *> sepBy1 pTypeName pComma)

    constraints <- many pTypeParameterConstraintClause

    body        <- between pOCurly pCCurly pClassBody

    optional pSemi

    return $ ClassTypeDeclaration attributes modifiers name typeParams inherits constraints body



pClassModifier :: P Modifier

pClassModifier = choice

    [ New       <$ pToken TKWnew      , Public   <$ pToken TKWpublic   

    , Protected <$ pToken TKWprotected, Internal <$ pToken TKWinternal 

    , Private   <$ pToken TKWprivate  , Abstract <$ pToken TKWabstract 

    , Sealed    <$ pToken TKWsealed   , Static   <$ pToken TKWstatic   ]



pClassBody :: P ClassBody

pClassBody = ClassBody <$> many pMemberDeclaration



pMemberDeclaration :: P MemberDeclaration

pMemberDeclaration = choice [ try pFieldDeclaration

                            , try pMethodDeclaration

                            , try pConstructorDeclaration

                            , try pDestructorDeclaration

                            , try pPropertyDeclaration

                            , try pIndexerDeclaration

                            , try pOperatorDeclaration

                            , TypeMemberDeclaration <$> pTypeDeclaration ]



--------------------------------------------------------------------------------

-- Field declarations.

--------------------------------------------------------------------------------



pFieldDeclaration :: P MemberDeclaration

pFieldDeclaration = do

    attributes  <- pAttributeSections

    modifiers   <- (++) <$> many pFieldModifier <*> option [] ([Const] <$ pToken TKWconst)

    ty          <- pType

    declarators <- sepBy1 pVariableDeclarator pComma

    pSemi

    return $ FieldMemberDeclaration attributes modifiers ty declarators



pFieldModifier :: P Modifier

pFieldModifier = choice [ New       <$ pToken TKWnew

                        , Public    <$ pToken TKWpublic   

                        , Protected <$ pToken TKWprotected

                        , Internal  <$ pToken TKWinternal 

                        , Private   <$ pToken TKWprivate 

                        , Static    <$ pToken TKWstatic  

                        , Readonly  <$ pToken TKWreadonly  

                        , Volatile  <$ pToken TKWvolatile ]



--------------------------------------------------------------------------------

-- Property declarations.

--------------------------------------------------------------------------------



pPropertyDeclaration :: P MemberDeclaration

pPropertyDeclaration = do

    attributes <- pAttributeSections

    modifiers  <- many pPropertyModifier

    ty         <- pType

    name       <- pName

    body       <- pPropertyLambda <|> pPropertyBody

    return $ PropertyMemberDeclaration attributes modifiers ty name body



pPropertyLambda :: P PropertyBody

pPropertyLambda = PropertyLambda <$ pLambda <*> pExpression <* pSemi



pPropertyBody :: P PropertyBody

pPropertyBody = do

    (accessor1, accessor2) <- betweenCurly (eitherOrBoth pPropertyGetAccessor pPropertySetAccessor)

    initializer            <- optionMaybe (pEqualSign *> pVariableInitializer <* pSemi)

    return $ PropertyBody accessor1 accessor2 initializer



pPropertyGetAccessor :: P AccessorDeclaration

pPropertyGetAccessor = do

    attributes <- pAttributeSections

    modifiers <- pPropertyAccessorModifiers

    pIdentifierKeyword "get"

    body <- Nothing <$ pSemi <|> Just <$> betweenCurly pStatements

    return $ GetAccessorDeclaration attributes modifiers body



pPropertySetAccessor :: P AccessorDeclaration

pPropertySetAccessor = do

    attributes <- pAttributeSections

    modifiers <- pPropertyAccessorModifiers

    pIdentifierKeyword "set"

    body <- Nothing <$ pSemi <|> Just <$> betweenCurly pStatements

    return $ SetAccessorDeclaration attributes modifiers body



pPropertyAccessorModifiers :: P [Modifier]

pPropertyAccessorModifiers = 

    choice [ try ([Protected, Internal]  <$ pToken TKWinternal  <* pToken TKWprotected)

           , try ([Internal , Protected] <$ pToken TKWprotected <* pToken TKWinternal)

           , [Protected] <$ pToken TKWprotected

           , [Internal]  <$ pToken TKWinternal 

           , [Private]   <$ pToken TKWprivate  

           , return [] ]



pPropertyModifier :: P Modifier

pPropertyModifier = 

    choice [ New       <$ pToken TKWnew      , Public   <$ pToken TKWpublic   

           , Protected <$ pToken TKWprotected, Internal <$ pToken TKWinternal   

           , Private   <$ pToken TKWprivate  , Static   <$ pToken TKWstatic   

           , Virtual   <$ pToken TKWvirtual  , Sealed   <$ pToken TKWsealed   

           , Override  <$ pToken TKWoverride , Abstract <$ pToken TKWabstract   

           , Extern    <$ pToken TKWextern   ]



--------------------------------------------------------------------------------

-- Constructor and destructor declarations.

--------------------------------------------------------------------------------



pConstructorDeclaration :: P MemberDeclaration

pConstructorDeclaration = do

    attributes  <- pAttributeSections

    modifiers   <- many pConstructorModifier

    name        <- pIdentifier

    params      <- betweenParens pFormalParams

    initializer <- optionMaybe (pColon *> pConstructorInitializer)

    body        <- ConstructorStatementBody <$> betweenCurly pStatements

    return $ ConstructorMemberDeclaration attributes modifiers name params initializer body



pConstructorInitializer :: P ConstructorInitializer

pConstructorInitializer = baseCall <|> thisCall

    where

        baseCall   = ConstructorBaseCall <$ pToken TKWbase <*> pArguments

        thisCall   = ConstructorThisCall <$ pToken TKWthis <*> pArguments



pConstructorModifier :: P Modifier

pConstructorModifier = choice [ Public    <$ pToken TKWpublic   

                              , Protected <$ pToken TKWprotected

                              , Internal  <$ pToken TKWinternal 

                              , Private   <$ pToken TKWprivate  

                              , Extern    <$ pToken TKWextern   ]



pDestructorDeclaration :: P MemberDeclaration

pDestructorDeclaration = do

    attributes <- pAttributeSections

    modifiers  <- option [] ([Extern] <$ pToken TKWextern)

    pToken TOpBitwiseNot

    name <- pIdentifier

    pOParens; pCParens

    body <- DestructorStatementBody <$> betweenCurly pStatements

    return $ DestructorMemberDeclaration attributes modifiers name body



--------------------------------------------------------------------------------

-- Operator declarations.

--------------------------------------------------------------------------------



pOperatorDeclaration :: P MemberDeclaration

pOperatorDeclaration = do

    attributes <- pAttributeSections

    modifiers  <- many1 pOperatorModifier

    declarator <- pOperatorDeclarator

    body       <- pOperatorBody

    return $ OperatorMemberDeclaration attributes modifiers declarator body



pOperatorDeclarator :: P OperatorDeclarator

pOperatorDeclarator = choice

    [ try $ UnaryOperatorDeclarator <$> pType

                                    <*  pToken TKWoperator

                                    <*> pOverloadableUnaryOperator

                                    <* pOParens

                                    <*> pType <*> pIdentifier

                                    <* pCParens



    , try $ BinaryOperatorDeclarator <$> pType

                                     <*  pToken TKWoperator

                                     <*> pOverloadableBinaryOperator

                                     <* pOParens

                                     <*> pType <*> pIdentifier

                                     <* pComma

                                     <*> pType <*> pIdentifier

                                     <* pCParens



    , ImplicitConversionOperatorDeclarator <$ pToken TKWimplicit

                                           <* pToken TKWoperator

                                           <*> pType

                                           <* pOParens

                                           <*> pType <*> pIdentifier

                                           <* pCParens



    , ExplicitConversionOperatorDeclarator <$ pToken TKWexplicit

                                           <* pToken TKWoperator

                                           <*> pType

                                           <* pOParens

                                           <*> pType <*> pIdentifier

                                           <* pCParens ]



pOverloadableUnaryOperator :: P OverloadableUnaryOperator

pOverloadableUnaryOperator = choice

    [ OverloadableUnaryPlus       <$ pToken TOpPlus 

    , OverloadableUnaryMinus      <$ pToken TOpMinus 

    , OverloadableUnaryNot        <$ pToken TOpNot   

    , OverloadableUnaryBitwiseNot <$ pToken TOpBitwiseNot   

    , OverloadableUnaryPlusPlus   <$ pToken TOpPlusPlus 

    , OverloadableUnaryMinusMinus <$ pToken TOpMinusMinus

    , OverloadableUnaryTrue       <$ pToken TKWtrue

    , OverloadableUnaryFalse      <$ pToken TKWfalse ]



pOverloadableBinaryOperator :: P BinaryOperator

pOverloadableBinaryOperator = choice 

    [ BinaryPlus             <$ pToken TOpPlus

    , BinaryMinus            <$ pToken TOpMinus

    , BinaryMultiply         <$ pToken TOpMultiply

    , BinaryDivide           <$ pToken TOpDivide

    , BinaryModulo           <$ pToken TOpModulo

    , BinaryBitwiseAnd       <$ pToken TOpBitwiseAnd

    , BinaryBitwiseOr        <$ pToken TOpBitwiseOr

    , BinaryBitwiseXor       <$ pToken TOpBitwiseXor

    , BinaryShiftLeft        <$ pToken TOpLeftShift

    , BinaryShiftRight       <$ pToken TOpRightShift

    , BinaryEquals           <$ pToken TOpEqual

    , BinaryNotEquals        <$ pToken TOpNotEqual

    , BinaryGreaterThan      <$ pToken TOpGreaterThan

    , BinaryGreaterThanEqual <$ pToken TOpGreaterThanEqual

    , BinaryLessThan         <$ pToken TOpLessThan

    , BinaryLessThanEqual    <$ pToken TOpLessThanEqual ]





pOperatorBody :: P OperatorBody

pOperatorBody = choice

    [ OperatorStatementBody  <$> betweenCurly pStatements

    , OperatorExpressionBody <$  pLambda <*> pExpression <* pSemi

    , OperatorNoBody         <$  pSemi ]



pOperatorModifier :: P Modifier

pOperatorModifier = choice 

    [ Public <$ pToken TKWpublic 

    , Static <$ pToken TKWstatic

    , Extern <$ pToken TKWextern ]



--------------------------------------------------------------------------------

-- Event declarations.

--------------------------------------------------------------------------------



pEventDeclaration :: P MemberDeclaration

pEventDeclaration = do

    attributes <- pAttributeSections

    modifiers  <- many pEventModifier

    pToken TKWevent

    ty <- pType

    choice [ EventVariableMemberDeclaration attributes modifiers ty 

             <$> sepBy1 pVariableDeclarator pComma

             <*  pSemi

            

           , try $ EventAccessorMemberDeclaration attributes modifiers ty

                <$> pName

                <* pOCurly <*> pAddEventAccessor <*> pRemoveEventAccessor

                <* pCCurly

                

           , try $ EventAccessorMemberDeclaration attributes modifiers ty

                <$> pName

                <* pOCurly <*> pRemoveEventAccessor <*> pAddEventAccessor

                <* pCCurly ]



pAddEventAccessor :: P EventAccessor

pAddEventAccessor = do

    attributes <- pAttributeSections

    pIdentifierKeyword "add"

    body <- betweenCurly pStatements

    return $ AddEventAccessor attributes body

    

pRemoveEventAccessor :: P EventAccessor

pRemoveEventAccessor = do

    attributes <- pAttributeSections

    pIdentifierKeyword "remove"

    body <- betweenCurly pStatements

    return $ RemoveEventAccessor attributes body



pEventModifier :: P Modifier

pEventModifier = choice

    [ New       <$ pToken TKWnew      , Public   <$ pToken TKWpublic

    , Protected <$ pToken TKWprotected, Internal <$ pToken TKWinternal

    , Private   <$ pToken TKWprivate  , Static   <$ pToken TKWstatic

    , Virtual   <$ pToken TKWvirtual  , Sealed   <$ pToken TKWsealed

    , Override  <$ pToken TKWoverride , Abstract <$ pToken TKWabstract

    , Extern    <$ pToken TKWextern   ]



--------------------------------------------------------------------------------

-- Indexer declarations.

--------------------------------------------------------------------------------



pIndexerDeclaration :: P MemberDeclaration

pIndexerDeclaration = do

    attributes <- pAttributeSections

    modifiers  <- many pIndexerModifier

    declarator <- pIndexerDeclarator

    body       <- pIndexerBody

    return $ IndexerMemberDeclaration attributes modifiers declarator body



pIndexerDeclarator :: P IndexerDeclarator

pIndexerDeclarator = choice

    [ try (IndexerDeclaratorThis <$> pType 

                                 <*  pToken TKWthis 

                                 <*> betweenSquare pFormalParams)



    , IndexerDeclaratorInterface <$> pType 

                                 <*> pInterfaceType 

                                 <*  pPeriod 

                                 <*  pToken TKWthis 

                                 <*> betweenSquare pFormalParams

    ]



pIndexerBody :: P IndexerBody

pIndexerBody = choice

    [ uncurry IndexerAccessor <$> betweenCurly (eitherOrBoth pPropertyGetAccessor pPropertySetAccessor)

    , IndexerLambda           <$ pLambda <*> pExpression <* pSemi ]



pIndexerModifier :: P Modifier

pIndexerModifier = choice 

    [ New       <$ pToken TKWnew      , Public   <$ pToken TKWpublic   

    , Protected <$ pToken TKWprotected, Internal <$ pToken TKWinternal 

    , Private   <$ pToken TKWprivate  , Virtual  <$ pToken TKWprivate  

    , Sealed    <$ pToken TKWprivate  , Override <$ pToken TKWprivate  

    , Abstract  <$ pToken TKWprivate  , Extern   <$ pToken TKWextern ]



--------------------------------------------------------------------------------

-- Method declarations.

--------------------------------------------------------------------------------



pMethodDeclaration :: P MemberDeclaration

pMethodDeclaration = MethodMemberDeclaration

    <$> pAttributeSections

    <*> ((++) <$> many pMethodModifier <*> pOptionPartial)

    <*> pTypeWithVoid

    <*> pName

    <*> option [] (betweenDiamond (sepBy1 pTypeParameter pComma))

    <*> betweenParens pFormalParams

    <*> many pTypeParameterConstraintClause

    <*> pMethodBody

    

pMethodBody :: P MethodBody

pMethodBody = 

    MethodNoBody         <$  pSemi                                <|>

    MethodExpressionBody <$  pLambda <*> pExpression <* pSemi     <|>

    MethodStatementBody  <$> between pOCurly pCCurly pStatements



pMethodModifier :: P Modifier

pMethodModifier = choice

    [ New       <$ pToken TKWnew      , Public    <$ pToken TKWpublic    

    , Protected <$ pToken TKWprotected, Internal  <$ pToken TKWinternal  

    , Private   <$ pToken TKWprivate  , Static    <$ pToken TKWstatic    

    , Virtual   <$ pToken TKWvirtual  , Sealed    <$ pToken TKWsealed    

    , Override  <$ pToken TKWoverride , Abstract  <$ pToken TKWabstract  

    , Extern    <$ pToken TKWextern   , Async     <$ pIdentifierKeyword "async" ]  



--------------------------------------------------------------------------------

-- Formal parameters.

--------------------------------------------------------------------------------



pFormalParams :: P FormalParams

pFormalParams = do

    params     <- sepBy pFormalParam pComma

    paramArray <- optionMaybe pParamArray

    return $ FormalParams params paramArray



pFormalParam :: P FormalParam

pFormalParam = do

    modifier   <- optionMaybe pParameterModifier

    ty         <- pType

    name       <- pIdentifier

    defaultArg <- optionMaybe (pEqualSign *> pExpression)

    return $ FormalParam modifier ty name defaultArg



pParamArray :: P ParamArray

pParamArray = ParamArray <$ pToken TKWparams <*> pArrayType <*> pIdentifier



pParameterModifier :: P ParameterModifier

pParameterModifier =

    RefParam  <$ pToken TKWref  <|>

    OutParam  <$ pToken TKWout  <|>

    ThisParam <$ pToken TKWthis



--------------------------------------------------------------------------------

-- Utility.

--------------------------------------------------------------------------------



pOptionPartial :: P [Modifier]

pOptionPartial = option [] ([Partial] <$ pIdentifierKeyword "partial")