{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE KindSignatures        #-}
{-# LANGUAGE LambdaCase            #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds             #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeOperators         #-}

module Language.Expression.GeneralOp where

-- import           Data.Typeable

import           Data.Vinyl
-- import           Data.Vinyl.Functor
-- import           Data.Vinyl.TypeLevel

import           Language.Expression
import           Language.Expression.Pretty

data GeneralOp op t a where
  Op :: op as r -> Rec t as -> GeneralOp op t r

class EvalOpAt k op where
  evalMany :: op as r -> Rec k as -> k r

-- class EqOpMany op where
--   liftEqMany
--     :: op as a -> op bs b

--     -> (forall xs. (AllConstrained Eq xs, RecApplicative xs) =>
--         Rec f xs -> Rec g xs -> Bool)

--     -> Rec f as -> Rec g bs -> Bool


class PrettyOp op where
  prettysPrecOp :: Pretty1 t => Int -> op as a -> Rec t as -> ShowS

instance HFunctor (GeneralOp op) where

instance HTraversable (GeneralOp op) where
  htraverse f = \case
    Op o args -> Op o <$> rtraverse f args


instance (EvalOpAt k op) => HFoldableAt k (GeneralOp op) where
  hfoldMap f = \case
    Op o args -> evalMany o (rmap f args)


-- instance EqOpMany op => HEq (GeneralOp op) where
--   liftHEq le _ (Op o1 (xs :: Rec f as)) (Op o2 (ys :: Rec g bs)) =
--     liftEqMany o1 o2 liftEqAll xs ys

--     where
--       liftEqAll
--         :: (AllConstrained Eq xs, RecApplicative xs)
--         => Rec f xs -> Rec g xs -> Bool
--       liftEqAll (xs' :: Rec f xs) ys' =
--         let
--           eqList :: Rec (Lift (->) f (Lift (->) g (Const Bool))) xs
--           eqList = rpureConstrained (Proxy :: Proxy Eq)
--             (Lift $ \x -> Lift $ Const . le (==) x)
--         in and . recordToList $ eqList <<*>> xs' <<*>> ys'

instance PrettyOp op => Pretty2 (GeneralOp op) where
  prettys2Prec p = \case
    Op op args -> prettysPrecOp p op args