module Data.GI.CodeGen.OverloadedLabels
    ( genOverloadedLabels
    ) where

#if !MIN_VERSION_base(4,8,0)
import Control.Applicative ((<$>))
#endif
import Data.Maybe (isNothing)
#if !MIN_VERSION_base(4,11,0)
import Data.Monoid ((<>))
#endif
import Control.Monad (forM_)
import qualified Data.Set as S
import Data.Text (Text)
import qualified Data.Text as T

import Data.GI.CodeGen.API
import Data.GI.CodeGen.Code
import Data.GI.CodeGen.SymbolNaming
import Data.GI.CodeGen.Util (lcFirst)

-- | A list of all overloadable identifiers in the set of APIs (current
-- properties and methods).
findOverloaded :: [(Name, API)] -> CodeGen [Text]
findOverloaded :: [(Name, API)] -> CodeGen [Text]
findOverloaded apis :: [(Name, API)]
apis = Set Text -> [Text]
forall a. Set a -> [a]
S.toList (Set Text -> [Text])
-> ReaderT
     CodeGenConfig (StateT (CGState, ModuleInfo) (Except e)) (Set Text)
-> ReaderT
     CodeGenConfig (StateT (CGState, ModuleInfo) (Except e)) [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Name, API)] -> Set Text -> CodeGen (Set Text)
go [(Name, API)]
apis Set Text
forall a. Set a
S.empty
    where
      go :: [(Name, API)] -> S.Set Text -> CodeGen (S.Set Text)
      go :: [(Name, API)] -> Set Text -> CodeGen (Set Text)
go [] set :: Set Text
set = Set Text
-> ReaderT
     CodeGenConfig (StateT (CGState, ModuleInfo) (Except e)) (Set Text)
forall (m :: * -> *) a. Monad m => a -> m a
return Set Text
set
      go ((_, api :: API
api):apis :: [(Name, API)]
apis) set :: Set Text
set =
        case API
api of
          APIInterface iface :: Interface
iface -> [(Name, API)] -> Set Text -> CodeGen (Set Text)
go [(Name, API)]
apis (Interface -> Set Text -> Set Text
scanInterface Interface
iface Set Text
set)
          APIObject object :: Object
object -> [(Name, API)] -> Set Text -> CodeGen (Set Text)
go [(Name, API)]
apis (Object -> Set Text -> Set Text
scanObject Object
object Set Text
set)
          APIStruct s :: Struct
s -> [(Name, API)] -> Set Text -> CodeGen (Set Text)
go [(Name, API)]
apis (Struct -> Set Text -> Set Text
scanStruct Struct
s Set Text
set)
          APIUnion u :: Union
u -> [(Name, API)] -> Set Text -> CodeGen (Set Text)
go [(Name, API)]
apis (Union -> Set Text -> Set Text
scanUnion Union
u Set Text
set)
          _ -> [(Name, API)] -> Set Text -> CodeGen (Set Text)
go [(Name, API)]
apis Set Text
set

      scanObject :: Object -> S.Set Text -> S.Set Text
      scanObject :: Object -> Set Text -> Set Text
scanObject o :: Object
o set :: Set Text
set =
          let props :: [Text]
props = ((Property -> Text) -> [Property] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Property -> Text
propToLabel ([Property] -> [Text])
-> (Object -> [Property]) -> Object -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Object -> [Property]
objProperties) Object
o
              methods :: [Text]
methods = ((Method -> Text) -> [Method] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Method -> Text
methodToLabel ([Method] -> [Text]) -> (Object -> [Method]) -> Object -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Method] -> [Method]
filterMethods ([Method] -> [Method])
-> (Object -> [Method]) -> Object -> [Method]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Object -> [Method]
objMethods) Object
o
          in [Set Text] -> Set Text
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
S.unions [Set Text
set, [Text] -> Set Text
forall a. Ord a => [a] -> Set a
S.fromList [Text]
props, [Text] -> Set Text
forall a. Ord a => [a] -> Set a
S.fromList [Text]
methods]

      scanInterface :: Interface -> S.Set Text -> S.Set Text
      scanInterface :: Interface -> Set Text -> Set Text
scanInterface i :: Interface
i set :: Set Text
set =
          let props :: [Text]
props = ((Property -> Text) -> [Property] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Property -> Text
propToLabel ([Property] -> [Text])
-> (Interface -> [Property]) -> Interface -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Interface -> [Property]
ifProperties) Interface
i
              methods :: [Text]
methods = ((Method -> Text) -> [Method] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Method -> Text
methodToLabel ([Method] -> [Text])
-> (Interface -> [Method]) -> Interface -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Method] -> [Method]
filterMethods ([Method] -> [Method])
-> (Interface -> [Method]) -> Interface -> [Method]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Interface -> [Method]
ifMethods) Interface
i
          in [Set Text] -> Set Text
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
S.unions [Set Text
set, [Text] -> Set Text
forall a. Ord a => [a] -> Set a
S.fromList [Text]
props, [Text] -> Set Text
forall a. Ord a => [a] -> Set a
S.fromList [Text]
methods]

      scanStruct :: Struct -> S.Set Text -> S.Set Text
      scanStruct :: Struct -> Set Text -> Set Text
scanStruct s :: Struct
s set :: Set Text
set =
          let attrs :: [Text]
attrs = ((Field -> Text) -> [Field] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Field -> Text
fieldToLabel ([Field] -> [Text]) -> (Struct -> [Field]) -> Struct -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Field] -> [Field]
filterFields ([Field] -> [Field]) -> (Struct -> [Field]) -> Struct -> [Field]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Struct -> [Field]
structFields) Struct
s
              methods :: [Text]
methods = ((Method -> Text) -> [Method] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Method -> Text
methodToLabel ([Method] -> [Text]) -> (Struct -> [Method]) -> Struct -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Method] -> [Method]
filterMethods ([Method] -> [Method])
-> (Struct -> [Method]) -> Struct -> [Method]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Struct -> [Method]
structMethods) Struct
s
          in [Set Text] -> Set Text
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
S.unions [Set Text
set, [Text] -> Set Text
forall a. Ord a => [a] -> Set a
S.fromList [Text]
attrs, [Text] -> Set Text
forall a. Ord a => [a] -> Set a
S.fromList [Text]
methods]

      scanUnion :: Union -> S.Set Text -> S.Set Text
      scanUnion :: Union -> Set Text -> Set Text
scanUnion u :: Union
u set :: Set Text
set =
          let attrs :: [Text]
attrs = ((Field -> Text) -> [Field] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Field -> Text
fieldToLabel ([Field] -> [Text]) -> (Union -> [Field]) -> Union -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Field] -> [Field]
filterFields ([Field] -> [Field]) -> (Union -> [Field]) -> Union -> [Field]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Union -> [Field]
unionFields) Union
u
              methods :: [Text]
methods = ((Method -> Text) -> [Method] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map Method -> Text
methodToLabel ([Method] -> [Text]) -> (Union -> [Method]) -> Union -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Method] -> [Method]
filterMethods ([Method] -> [Method]) -> (Union -> [Method]) -> Union -> [Method]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Union -> [Method]
unionMethods) Union
u
          in [Set Text] -> Set Text
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
S.unions [Set Text
set, [Text] -> Set Text
forall a. Ord a => [a] -> Set a
S.fromList [Text]
attrs, [Text] -> Set Text
forall a. Ord a => [a] -> Set a
S.fromList [Text]
methods]

      propToLabel :: Property -> Text
      propToLabel :: Property -> Text
propToLabel = Text -> Text
lcFirst (Text -> Text) -> (Property -> Text) -> Property -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
hyphensToCamelCase (Text -> Text) -> (Property -> Text) -> Property -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Property -> Text
propName

      methodToLabel :: Method -> Text
      methodToLabel :: Method -> Text
methodToLabel = Name -> Text
lowerName (Name -> Text) -> (Method -> Name) -> Method -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Method -> Name
methodName

      fieldToLabel :: Field -> Text
      fieldToLabel :: Field -> Text
fieldToLabel = Text -> Text
lcFirst (Text -> Text) -> (Field -> Text) -> Field -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
underscoresToCamelCase (Text -> Text) -> (Field -> Text) -> Field -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Field -> Text
fieldName

      filterMethods :: [Method] -> [Method]
      filterMethods :: [Method] -> [Method]
filterMethods = (Method -> Bool) -> [Method] -> [Method]
forall a. (a -> Bool) -> [a] -> [a]
filter (\m :: Method
m -> (Maybe Text -> Bool
forall a. Maybe a -> Bool
isNothing (Maybe Text -> Bool) -> (Method -> Maybe Text) -> Method -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Method -> Maybe Text
methodMovedTo) Method
m Bool -> Bool -> Bool
&&
                                    Method -> MethodType
methodType Method
m MethodType -> MethodType -> Bool
forall a. Eq a => a -> a -> Bool
== MethodType
OrdinaryMethod)

      filterFields :: [Field] -> [Field]
      filterFields :: [Field] -> [Field]
filterFields = (Field -> Bool) -> [Field] -> [Field]
forall a. (a -> Bool) -> [a] -> [a]
filter (\f :: Field
f -> Field -> Bool
fieldVisible Field
f Bool -> Bool -> Bool
&&
                            (Bool -> Bool
not (Bool -> Bool) -> (Field -> Bool) -> Field -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Bool
T.null (Text -> Bool) -> (Field -> Text) -> Field -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Field -> Text
fieldName) Field
f)

genOverloadedLabel :: Text -> CodeGen ()
genOverloadedLabel :: Text -> CodeGen ()
genOverloadedLabel l :: Text
l = BaseCodeGen e () -> BaseCodeGen e ()
forall e a. BaseCodeGen e a -> BaseCodeGen e a
group (BaseCodeGen e () -> BaseCodeGen e ())
-> BaseCodeGen e () -> BaseCodeGen e ()
forall a b. (a -> b) -> a -> b
$ do
  Text -> BaseCodeGen e ()
Text -> CodeGen ()
line (Text -> BaseCodeGen e ()) -> Text -> BaseCodeGen e ()
forall a b. (a -> b) -> a -> b
$ "_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
l Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> " :: IsLabelProxy \"" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
l Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> "\" a => a"
  Text -> BaseCodeGen e ()
Text -> CodeGen ()
line (Text -> BaseCodeGen e ()) -> Text -> BaseCodeGen e ()
forall a b. (a -> b) -> a -> b
$ "_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
l Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> " = fromLabelProxy (Proxy :: Proxy \""
           Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
l Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> "\")"
  HaddockSection -> Text -> CodeGen ()
export HaddockSection
ToplevelSection ("_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
l)

genOverloadedLabels :: [(Name, API)] -> CodeGen ()
genOverloadedLabels :: [(Name, API)] -> CodeGen ()
genOverloadedLabels allAPIs :: [(Name, API)]
allAPIs = do
  [Text] -> CodeGen ()
setLanguagePragmas ["DataKinds", "FlexibleContexts", "CPP"]
  [ModuleFlag] -> CodeGen ()
setModuleFlags [ModuleFlag
ImplicitPrelude]

  Text -> BaseCodeGen e ()
Text -> CodeGen ()
line (Text -> BaseCodeGen e ()) -> Text -> BaseCodeGen e ()
forall a b. (a -> b) -> a -> b
$ "import Data.Proxy (Proxy(..))"
  Text -> BaseCodeGen e ()
Text -> CodeGen ()
line (Text -> BaseCodeGen e ()) -> Text -> BaseCodeGen e ()
forall a b. (a -> b) -> a -> b
$ "import Data.GI.Base.Overloading (IsLabelProxy(..))"
  BaseCodeGen e ()
CodeGen ()
blank

  [Text]
labels <- [(Name, API)] -> CodeGen [Text]
findOverloaded [(Name, API)]
allAPIs
  [Text] -> (Text -> BaseCodeGen e ()) -> BaseCodeGen e ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [Text]
labels ((Text -> BaseCodeGen e ()) -> BaseCodeGen e ())
-> (Text -> BaseCodeGen e ()) -> BaseCodeGen e ()
forall a b. (a -> b) -> a -> b
$ \l :: Text
l -> do
      Text -> CodeGen ()
genOverloadedLabel Text
l
      BaseCodeGen e ()
CodeGen ()
blank