{-# Language AllowAmbiguousTypes #-}
{-# Language DataKinds #-}
{-# Language FlexibleContexts #-}
{-# Language FlexibleInstances #-}
{-# Language KindSignatures #-}
{-# Language MultiParamTypeClasses #-}
{-# Language ScopedTypeVariables #-}
{-# Language TypeApplications #-}
{-# Language TypeFamilies #-}
{-# Language TypeOperators #-}
{-# Language UndecidableInstances #-}

{-# OPTIONS_HADDOCK hide,not-home #-}

{-# OPTIONS_GHC -Wno-redundant-constraints #-}   -- rdrop

-- | Hiding fields.

module Data.Ruin.Hide (
  Hide,
  hide,
  rdrop,
  rtake,
  ) where

import GHC.TypeLits

import Data.Ruin.All
import Data.Ruin.Eval
import Data.Ruin.Internal
import Data.Ruin.R

-- | Deny the @'Has'@ instance for each of @ss@.
newtype Hide (ss :: [Symbol]) rc = MkHide rc

-- | Deny (\"forget\") a @'Has' s@ instance.
hide :: Labels ss -> rc -> Hide ss rc
hide _ = MkHide

instance Has_Hide (Elem s sHiddens) s rc => Has s (Hide sHiddens rc) where
  type FieldType s (Hide sHiddens rc) = FieldType s rc
  {-# INLINE extricate1 #-}
  extricate1 = extricate1_Hide @(Elem s sHiddens)

class Has_Hide (eq :: Bool) (s :: Symbol) (rc :: *) where
  extricate1_Hide :: Label s -> Hide sHiddens rc -> Eval (FieldType s rc)

instance TypeError (FieldIsHidden s rc) => Has_Hide 'True s rc where
  extricate1_Hide = undefined

instance Has s rc => Has_Hide 'False s rc where
  {-# INLINE extricate1_Hide #-}
  extricate1_Hide = \s (MkHide rc) -> extricate1 s rc

-----

type FieldIsHidden (s :: Symbol) (top :: *) =
             'Text "ruin: The field `"
       ':<>: 'Text s
       ':<>: 'Text "' is hidden in the type"
  ':$$: Render top

-----

-- | Create an anonymous record that contains the fields of @t@ that
-- are not named in @fs@.
rdrop ::
     ( rc ~ Rcrd (DifferenceByFst (Fields t) fs)
     , Build rc
     , t `IsSubtypeOf` rc
     )
  => Labels fs
  -> t
  -> rc
{-# INLINE rdrop #-}
rdrop = \_ -> rup

-- | Split a record into two separate types, where the second type is
-- an anonymous record defined as the leftovers from the first type.
rtake ::
     ( leftovers ~ Rcrd (DifferenceByFst (Fields t) (FieldNames taken))
     , Build (taken,leftovers)
     , t `IsSymmetricRecordOf` (taken,leftovers)
     )
  => t
  -> (taken,leftovers)
{-# INLINE rtake #-}
rtake = rsym