{-# LANGUAGE KindSignatures, TypeInType, TypeFamilies, TypeOperators, UndecidableInstances #-}

module Clr.Resolver.ImplicitConversions (ImplicitConvExists) where

import Clr.ListTuple
import Clr.Types
import Clr.Inheritance

import Data.Kind
import GHC.TypeLits
import Data.Type.Bool
import Data.Type.Equality

--
-- ImplicitConvExists x y is true if there is an implicit conversion
-- from x to y as defined by ECMA-364 section 13.1
--
type family ImplicitConvExists (x::Type) (y::Type) :: Bool where
  ImplicitConvExists t  t  = 'True -- identity conversion
  ImplicitConvExists t1 t2 = ImpNumConvExists   t1 t2
                          || ImpEnumConvExists  t1 t2
                          || ImpRefConvExists   t1 t2
                          || ImpBoxConvExists   t1 t2
                          || ImpConstConvExists t1 t2

type family ImpNumConvExists (x::Type) (y::Type) :: Bool where
  ImpNumConvExists T_sbyte  T_short   = 'True
  ImpNumConvExists T_sbyte  T_int     = 'True
  ImpNumConvExists T_sbyte  T_long    = 'True
  ImpNumConvExists T_sbyte  T_float   = 'True
  ImpNumConvExists T_sbyte  T_double  = 'True
  ImpNumConvExists T_sbyte  T_decimal = 'True
  ImpNumConvExists T_byte   T_short   = 'True
  ImpNumConvExists T_byte   T_ushort  = 'True
  ImpNumConvExists T_byte   T_int     = 'True
  ImpNumConvExists T_byte   T_uint    = 'True
  ImpNumConvExists T_byte   T_long    = 'True
  ImpNumConvExists T_byte   T_ulong   = 'True
  ImpNumConvExists T_byte   T_float   = 'True
  ImpNumConvExists T_byte   T_double  = 'True
  ImpNumConvExists T_byte   T_decimal = 'True
  ImpNumConvExists T_short  T_int     = 'True
  ImpNumConvExists T_short  T_long    = 'True
  ImpNumConvExists T_short  T_float   = 'True
  ImpNumConvExists T_short  T_double  = 'True
  ImpNumConvExists T_short  T_decimal = 'True
  ImpNumConvExists T_ushort T_int     = 'True
  ImpNumConvExists T_ushort T_uint    = 'True
  ImpNumConvExists T_ushort T_long    = 'True
  ImpNumConvExists T_ushort T_ulong   = 'True
  ImpNumConvExists T_ushort T_float   = 'True
  ImpNumConvExists T_ushort T_double  = 'True
  ImpNumConvExists T_ushort T_decimal = 'True
  ImpNumConvExists T_int    T_long    = 'True
  ImpNumConvExists T_int    T_float   = 'True
  ImpNumConvExists T_int    T_double  = 'True
  ImpNumConvExists T_int    T_decimal = 'True
  ImpNumConvExists T_uint   T_long    = 'True
  ImpNumConvExists T_uint   T_ulong   = 'True
  ImpNumConvExists T_uint   T_float   = 'True
  ImpNumConvExists T_uint   T_double  = 'True
  ImpNumConvExists T_uint   T_decimal = 'True
  ImpNumConvExists T_long   T_float   = 'True
  ImpNumConvExists T_long   T_double  = 'True
  ImpNumConvExists T_long   T_decimal = 'True
  ImpNumConvExists T_ulong  T_float   = 'True
  ImpNumConvExists T_ulong  T_double  = 'True
  ImpNumConvExists T_ulong  T_decimal = 'True
  ImpNumConvExists T_char   T_ushort  = 'True
  ImpNumConvExists T_char   T_int     = 'True
  ImpNumConvExists T_char   T_uint    = 'True
  ImpNumConvExists T_char   T_long    = 'True
  ImpNumConvExists T_char   T_ulong   = 'True
  ImpNumConvExists T_char   T_float   = 'True
  ImpNumConvExists T_char   T_double  = 'True
  ImpNumConvExists T_char   T_decimal = 'True
  ImpNumConvExists T_float  T_double  = 'True
  ImpNumConvExists t1       t2        = 'False

-- TODO
type family ImpEnumConvExists (x::Type) (y::Type) :: Bool where
  ImpEnumConvExists t1 t2 = 'False

-- TODO: more complicated than this see 13.1.4
type family ImpRefConvExists (x::Type) (y::Type) :: Bool where
  ImpRefConvExists t1 t2 = t1 `Implements` t2 || t1 `Implements` t2

-- TODO: probably handled by above though
type family ImpBoxConvExists (x::Type) (y::Type) :: Bool where
  ImpBoxConvExists t1 t2 = 'False

-- TODO
type family ImpConstConvExists (x::Type) (y::Type) :: Bool where
  ImpConstConvExists t1 t2 = 'False