module Sqel.Product where

import Generics.SOP.GGP (GCode, GDatatypeInfoOf)
import Sqel.Comp (CompColumn (compColumn), CompName, MetaFor, ProductFields)
import Sqel.Data.Dd (
  Comp (Prod),
  CompInc (Nest),
  Dd (Dd),
  DdInc (DdNest),
  DdK (DdK),
  DdSort (DdProd),
  DdStruct (DdComp),
  DdType,
  ProdType (Reg),
  Struct (Comp),
  )
import Sqel.Data.Mods (pattern NoMods, NoMods)
import Sqel.Data.Sel (MkTSel (mkTSel), Sel (SelAuto), SelW (SelWAuto))
import Sqel.Names.Rename (Rename (rename))
import Sqel.Names.Set (SetName)

class DdType s ~ a => ProductSel sel a arg s | sel a arg -> s where
  prodSel :: arg -> Dd s

instance (
    MkTSel sel,
    fields ~ ProductFields (GDatatypeInfoOf a) (GCode a),
    meta ~ MetaFor "product type" ('ShowType a) "prod",
    CompColumn meta fields a arg s
  ) => ProductSel sel a arg ('DdK 'SelAuto NoMods a ('Comp sel ('Prod 'Reg) 'Nest s)) where
    prodSel :: arg -> Dd ('DdK 'SelAuto NoMods a ('Comp sel ('Prod 'Reg) 'Nest s))
prodSel arg
arg =
      forall (sel :: Sel) (mods :: [*]) (s1 :: Struct) a.
SelW sel -> Mods mods -> DdStruct s1 -> Dd ('DdK sel mods a s1)
Dd SelW 'SelAuto
SelWAuto forall (ps :: [*]). (ps ~ NoMods) => Mods ps
NoMods (forall (sel :: TSel) (c :: Comp) (i :: CompInc) (sub :: [DdK]).
TSelW sel
-> DdSort c -> DdInc i -> NP Dd sub -> DdStruct ('Comp sel c i sub)
DdComp forall (sel :: TSel). MkTSel sel => TSelW sel
mkTSel DdSort ('Prod 'Reg)
DdProd DdInc 'Nest
DdNest (forall (meta :: CompMeta) (fields :: [ProductField]) a arg
       (s :: [DdK]).
CompColumn meta fields a arg s =>
arg -> NP Dd s
compColumn @meta @fields @a arg
arg))

class DdType s ~ a => Product a arg s | a arg -> s where
  prod :: arg -> Dd s

instance (
    CompName a sel,
    ProductSel sel a arg s
  ) => Product a arg s where
    prod :: arg -> Dd s
prod =
      forall {k} (sel :: k) a arg (s :: DdK).
ProductSel sel a arg s =>
arg -> Dd s
prodSel @sel @a

prodAs ::
   (name :: Symbol) (a :: Type) (s :: DdK) (arg :: Type) .
  Product a arg s =>
  Rename s (SetName s name) =>
  arg ->
  Dd (SetName s name)
prodAs :: forall (name :: Symbol) a (s :: DdK) arg.
(Product a arg s, Rename s (SetName s name)) =>
arg -> Dd (SetName s name)
prodAs =
  forall (s0 :: DdK) (s1 :: DdK). Rename s0 s1 => Dd s0 -> Dd s1
rename forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a arg (s :: DdK). Product a arg s => arg -> Dd s
prod @_ @_ @s