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

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

module Language.Bond.Codegen.Cpp.ApplyOverloads (applyOverloads, Protocol(..)) where

import Data.Monoid
import Prelude
import Data.Text.Lazy (Text)
import Text.Shakespeare.Text
import Language.Bond.Syntax.Types
import Language.Bond.Codegen.TypeMapping
import Language.Bond.Codegen.Util

-- | Protocol data type is used to specify what protocols the @Apply@ function
-- overloads should be generated for.
data Protocol =
    ProtocolReader String | -- ^ Name of the class implementing the protocol reader.
    ProtocolWriter String   -- ^ Name of the class implementing the protocol writer.


-- Apply overloads
applyOverloads :: [Protocol] -> MappingContext -> Text -> Text -> Declaration -> Text
applyOverloads protocols cpp attr extern s@Struct {..} | null declParams = [lt|
    //
    // Extern template specializations of Apply function with common
    // transforms for #{declName}.
    //

    #{extern}template #{attr}
    bool Apply(const ::bond::To< #{qualifiedName}>& transform,
               const ::bond::bonded< #{qualifiedName}>& value);

    #{extern}template #{attr}
    bool Apply(const ::bond::InitSchemaDef& transform,
               const #{qualifiedName}& value);

    #{extern}template #{attr}
    bool Apply(const ::bond::Null& transform,
               const ::bond::bonded< #{qualifiedName}, ::bond::SimpleBinaryReader< ::bond::InputBuffer>&>& value);
    #{newlineSep 1 applyOverloads' protocols}|]
  where
    qualifiedName = getDeclTypeName cpp s

    applyOverloads' p = [lt|#{deserialization p}#{newlineSep 1 (serialization p) serializingTransforms}|]

    serializingTransforms =
        [ [lt|Serializer|]
        , [lt|Marshaler|]
        ]

    deserialization (ProtocolWriter _) = mempty
    deserialization (ProtocolReader protocolReader) = [lt|
    #{extern}template #{attr}
    bool Apply(const ::bond::To< #{qualifiedName}>& transform,
               const ::bond::bonded< #{qualifiedName}, #{protocolReader}&>& value);

    #{extern}template #{attr}
    bool Apply(const ::bond::To< #{qualifiedName}>& transform,
               const ::bond::bonded<void, #{protocolReader}&>& value);|]

    serialization (ProtocolReader _) _ = mempty
    serialization (ProtocolWriter protocolWriter) transform = [lt|
    #{extern}template #{attr}
    bool Apply(const ::bond::#{transform}<#{protocolWriter} >& transform,
               const #{qualifiedName}& value);

    #{extern}template #{attr}
    bool Apply(const ::bond::#{transform}<#{protocolWriter} >& transform,
               const ::bond::bonded< #{qualifiedName}>& value);
    #{newlineSep 1 transcoding protocols}|]
      where
        transcoding (ProtocolWriter _) = mempty
        transcoding (ProtocolReader protocolReader) = [lt|
    #{extern}template #{attr}
    bool Apply(const ::bond::#{transform}<#{protocolWriter} >& transform,
               const ::bond::bonded< #{qualifiedName}, #{protocolReader}&>& value);|]

applyOverloads _ _ _ _ _ = mempty