module Cfg.Source.NestedConfig where

import Cfg.Options (ConfigOptions (..))
import Cfg.Source (NestedConfig (..))
import Data.Kind (Type)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Tree (Tree (..))
import GHC.Generics

defaultToNestedConfig :: forall a. (Generic a, GConfigForest (Rep a)) => ConfigOptions -> [Tree Text]
defaultToNestedConfig :: forall a.
(Generic a, GConfigForest (Rep a)) =>
ConfigOptions -> [Tree Text]
defaultToNestedConfig ConfigOptions
opts = forall (a :: * -> *).
GConfigForest a =>
ConfigOptions -> [Tree Text]
gToForest @(Rep a) ConfigOptions
opts

{- | Generic typeclass machinery for inducting on the structure
 of the type, such that we can thread `Display` instances through
 the structure of the type. The primary use case is for implementing
 `RecordInstance`, which does this "threading" for record fields. This
 machinery does, crucially, depend on child types (i.e. the type of a
 record field) having a `Display` instance.

 @since 0.0.1.0
-}
class GConfigForest (a :: Type -> Type) where
    gToForest :: ConfigOptions -> [Tree Text]

instance GConfigForest V1 where
    gToForest :: ConfigOptions -> [Tree Text]
gToForest ConfigOptions
_ = []

instance GConfigForest U1 where
    gToForest :: ConfigOptions -> [Tree Text]
gToForest ConfigOptions
_ = []

instance NestedConfig a => GConfigForest (K1 R a) where
    gToForest :: ConfigOptions -> [Tree Text]
gToForest ConfigOptions
_ = forall a. NestedConfig a => [Tree Text]
toNestedConfig @a

instance GConfigForest f => GConfigForest (M1 D s f) where
    gToForest :: ConfigOptions -> [Tree Text]
gToForest ConfigOptions
opts = forall (a :: * -> *).
GConfigForest a =>
ConfigOptions -> [Tree Text]
gToForest @f ConfigOptions
opts 

instance (Constructor c, GConfigForest f) => GConfigForest (M1 C c f) where
    gToForest :: ConfigOptions -> [Tree Text]
gToForest ConfigOptions
opts = forall (a :: * -> *).
GConfigForest a =>
ConfigOptions -> [Tree Text]
gToForest @f ConfigOptions
opts

instance (Selector s, GConfigForest f) => GConfigForest (M1 S s f) where
    gToForest :: ConfigOptions -> [Tree Text]
gToForest ConfigOptions
opts =
        if Any s f Any -> [Char]
forall {k} (s :: k) k1 (t :: k -> (k1 -> *) -> k1 -> *)
       (f :: k1 -> *) (a :: k1).
Selector s =>
t s f a -> [Char]
forall k1 (t :: Meta -> (k1 -> *) -> k1 -> *) (f :: k1 -> *)
       (a :: k1).
t s f a -> [Char]
selName Any s f Any
forall (t :: Meta -> (* -> *) -> * -> *) a. t s f a
m [Char] -> [Char] -> Bool
forall a. Eq a => a -> a -> Bool
== [Char]
""
            then [Char] -> [Tree Text]
forall a. HasCallStack => [Char] -> a
error [Char]
"Can only create a tree for named product types i.e. Records with named fields"
            else
                [ Text -> [Tree Text] -> Tree Text
forall a. a -> [Tree a] -> Tree a
Node
                    (ConfigOptions -> Text -> Text
configOptionsLabelModifier ConfigOptions
opts (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ [Char] -> Text
T.pack (Any s f Any -> [Char]
forall {k} (s :: k) k1 (t :: k -> (k1 -> *) -> k1 -> *)
       (f :: k1 -> *) (a :: k1).
Selector s =>
t s f a -> [Char]
forall k1 (t :: Meta -> (k1 -> *) -> k1 -> *) (f :: k1 -> *)
       (a :: k1).
t s f a -> [Char]
selName Any s f Any
forall (t :: Meta -> (* -> *) -> * -> *) a. t s f a
m))
                    (forall (a :: * -> *).
GConfigForest a =>
ConfigOptions -> [Tree Text]
gToForest @f ConfigOptions
opts)
                ]
      where
        m :: t s f a
        m :: forall (t :: Meta -> (* -> *) -> * -> *) a. t s f a
m = t s f a
forall a. HasCallStack => a
undefined

instance (GConfigForest a, GConfigForest b) => GConfigForest (a :*: b) where
    gToForest :: ConfigOptions -> [Tree Text]
gToForest ConfigOptions
opts = forall (a :: * -> *).
GConfigForest a =>
ConfigOptions -> [Tree Text]
gToForest @a ConfigOptions
opts [Tree Text] -> [Tree Text] -> [Tree Text]
forall a. Semigroup a => a -> a -> a
<> forall (a :: * -> *).
GConfigForest a =>
ConfigOptions -> [Tree Text]
gToForest @b ConfigOptions
opts

instance GConfigForest (a :+: b) where
    gToForest :: ConfigOptions -> [Tree Text]
gToForest ConfigOptions
_ = []