{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Language.LOL.Symantic.Expr.If where

import Data.Proxy (Proxy(..))
import Data.Type.Equality ((:~:)(Refl))
import Language.LOL.Symantic.Expr.Bool
import Language.LOL.Symantic.Expr.Common
import Language.LOL.Symantic.Expr.Lambda
import Language.LOL.Symantic.Repr.Dup
import Language.LOL.Symantic.Trans.Common
import Language.LOL.Symantic.Type

-- * Class 'Sym_If'

class Sym_If repr where
	if_  :: repr Bool -> repr a -> repr a -> repr a
	default if_ :: Trans t repr => t repr Bool -> t repr a -> t repr a -> t repr a
	if_  = trans_map3 if_
class Sym_When repr where
	when :: repr Bool -> repr () -> repr ()
	default when :: Trans t repr => t repr Bool -> t repr () -> t repr ()
	when = trans_map2 when

instance -- Sym_If Dup
 ( Sym_If r1
 , Sym_If r2
 ) => Sym_If (Dup r1 r2) where
	if_ (c1 `Dup` c2) (ok1 `Dup` ok2) (ko1 `Dup` ko2) =
		if_ c1 ok1 ko1 `Dup`
		if_ c2 ok2 ko2
instance -- Sym_When Dup
 ( Sym_When r1
 , Sym_When r2
 ) => Sym_When (Dup r1 r2) where
	when (c1 `Dup` c2) (ok1 `Dup` ok2) =
		when c1 ok1 `Dup`
		when c2 ok2

-- * Type 'Expr_If'
-- | Expression.
data Expr_If (root:: *)
type instance Root_of_Expr      (Expr_If root)      = root
type instance Type_of_Expr      (Expr_If root)      = No_Type
type instance Sym_of_Expr       (Expr_If root) repr = (Sym_If repr)
type instance Error_of_Expr ast (Expr_If root)      = No_Error_Expr

if_from
 :: forall root ty ast hs ret.
 ( ty ~ Type_Root_of_Expr (Expr_If root)
 , Type_Root_Lift Type_Bool (Type_Root_of_Expr root)
 , Type_Eq (Type_Root_of_Expr root)
 , Expr_from ast root
 , Error_Expr_Lift (Error_Expr (Error_of_Type ast ty) ty ast)
                   (Error_of_Expr ast root)
 , Root_of_Expr root ~ root
 ) => ast -> ast -> ast
 -> Expr_From ast (Expr_If root) hs ret
if_from ast_cond ast_ok ast_ko ex ast ctx k =
	expr_from (Proxy::Proxy root) ast_cond ctx $
	 \(ty_cond::Type_Root_of_Expr root h_n) (Forall_Repr_with_Context cond) ->
	expr_from (Proxy::Proxy root) ast_ok ctx $
	 \(ty_ok::Type_Root_of_Expr root h_ok) (Forall_Repr_with_Context ok) ->
	expr_from (Proxy::Proxy root) ast_ko ctx $
	 \(ty_ko::Type_Root_of_Expr root h_ko) (Forall_Repr_with_Context ko) ->
	when_type_eq ex ast type_bool ty_cond $ \Refl ->
	when_type_eq ex ast ty_ok ty_ko $ \Refl ->
		k ty_ok $ Forall_Repr_with_Context $
		 \c -> if_ (cond c) (ok c) (ko c)

-- * Type 'Expr_When'
-- | Expression.
data Expr_When (root:: *)
type instance Root_of_Expr      (Expr_When root)      = root
type instance Type_of_Expr      (Expr_When root)      = No_Type
type instance Sym_of_Expr       (Expr_When root) repr = (Sym_When repr)
type instance Error_of_Expr ast (Expr_When root)      = No_Error_Expr

when_from
 :: forall root ty ast hs ret.
 ( ty ~ Type_Root_of_Expr (Expr_When root)
 , Type_Root_Lift Type_Bool (Type_Root_of_Expr root)
 , Type_Root_Lift Type_Unit (Type_Root_of_Expr root)
 , Type_Eq (Type_Root_of_Expr root)
 , Expr_from ast root
 , Error_Expr_Lift (Error_Expr (Error_of_Type ast ty) ty ast)
                   (Error_of_Expr ast root)
 , Root_of_Expr root ~ root
 ) => ast -> ast
 -> Expr_From ast (Expr_When root) hs ret
when_from ast_cond ast_ok ex ast ctx k =
	expr_from (Proxy::Proxy root) ast_cond ctx $
	 \(ty_cond::Type_Root_of_Expr root h_n) (Forall_Repr_with_Context cond) ->
	expr_from (Proxy::Proxy root) ast_ok ctx $
	 \(ty_ok::Type_Root_of_Expr root h_ok) (Forall_Repr_with_Context ok) ->
	when_type_eq ex ast type_bool ty_cond $ \Refl ->
	when_type_eq ex ast type_unit ty_ok $ \Refl ->
		k ty_ok $ Forall_Repr_with_Context $
		 \c -> when (cond c) (ok c)

-- ** Type 'Expr_Lambda_Bool_If'
-- | Convenient alias.
type Expr_Lambda_If_Bool lam
 =   Expr_Root (Expr_Alt (Expr_Lambda lam)
                         (Expr_Alt Expr_If
                                   Expr_Bool))

-- | Convenient alias around 'expr_from'.
expr_lambda_if_bool_from
 :: forall lam ast root.
 ( Expr_from ast (Expr_Lambda_If_Bool lam)
 , root ~ Expr_Lambda_If_Bool lam
 ) => Proxy lam
 -> ast
 -> Either (Error_of_Expr ast root)
           (Exists_Type_and_Repr (Type_Root_of_Expr root)
                                 (Forall_Repr root))
expr_lambda_if_bool_from _lam ast =
	expr_from (Proxy::Proxy root) ast
	 Context_Empty $ \ty (Forall_Repr_with_Context repr) ->
		Right $ Exists_Type_and_Repr ty $
			Forall_Repr $ repr Context_Empty