{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}

module Funcons.Operations.Types where

import Prelude hiding (null)

import Funcons.Operations.Booleans
import Funcons.Operations.Internal
import qualified Data.Set as S
import qualified Data.MultiSet as MS
import qualified Data.Vector as V

library :: (HasValues t, Ord t) => Library t
library = libFromList [
    ("types", NullaryExpr types)
  , ("value-types", NullaryExpr value_types)
  , ("empty-type", NullaryExpr empty_type)
--  , ("null-type", NullaryExpr nulltype)
--  , ("null", NullaryExpr null)
  , ("values", NullaryExpr values)
  , ("type-member", BinaryExpr type_member)
--  , ("is-value", UnaryExpr is_value)
--  , ("is-val", UnaryExpr is_value)
  , ("value-type", UnaryExpr value_type)
  , ("datatype-values", NullaryExpr datatype_values)
  , ("ground-values", NullaryExpr ground_values)
  ]

datatype_values_ :: HasValues t => [OpExpr t] -> OpExpr t
datatype_values_ = nullaryOp datatype_values
datatype_values :: HasValues t => OpExpr t
datatype_values = vNullaryOp "datatype-values" (Normal $ injectT ADTs)

ground_values_ :: HasValues t => [OpExpr t] -> OpExpr t
ground_values_ = nullaryOp ground_values
ground_values :: HasValues t => OpExpr t
ground_values = vNullaryOp "ground-values" (Normal $ injectT (ADT "ground-values" []))

types_ :: HasValues t => [OpExpr t] -> OpExpr t
types_ = nullaryOp types
types :: HasValues t => OpExpr t
types = NullaryOp "types" (Normal $ injectT Types)

value_types_ :: HasValues t => [OpExpr t] -> OpExpr t
value_types_ = nullaryOp value_types
value_types :: HasValues t => OpExpr t
value_types = NullaryOp "value-types" (Normal $ injectT Types)

empty_type_ :: HasValues t => [OpExpr t] -> OpExpr t
empty_type_ = nullaryOp empty_type
empty_type :: HasValues t => OpExpr t
empty_type = NullaryOp "empty-types" (Normal $ injectT EmptyType)

nulltype_ :: HasValues t => [OpExpr t] -> OpExpr t
nulltype_ = nullaryOp nulltype
nulltype :: HasValues t => OpExpr t
nulltype = NullaryOp "null-type" (Normal $ injectT NullType)

null_ :: HasValues t => [OpExpr t] -> OpExpr t
null_ = nullaryOp null
null :: HasValues t => OpExpr t
null = NullaryOp "null" (Normal $ inject null__)

values_ :: HasValues t => [OpExpr t] -> OpExpr t
values_ = nullaryOp values
values :: HasValues t => OpExpr t
values = NullaryOp "values" (Normal $ injectT Values)


is_value_ :: HasValues t => [OpExpr t] -> OpExpr t
is_value_ = unaryOp is_value
is_value :: HasValues t => OpExpr t -> OpExpr t
is_value = UnaryOp "is-value" op
  where op _ = Normal $ inject (tobool True)

value_type_ :: HasValues t => [OpExpr t] -> OpExpr t
value_type_ = unaryOp value_type
value_type :: HasValues t => OpExpr t -> OpExpr t
value_type = vUnaryOp "value-type" (Normal . injectT . tyOf)

tyOf :: HasValues t => Values t -> Types t
tyOf (ADTVal "true" [])         = ADT "booleans" []
tyOf (ADTVal "false" [])        = ADT "booleans" []
tyOf (ADTVal c [p]) | c == unicode_cons = UnicodeCharacters
tyOf (Int _)                    = Integers
tyOf (Nat _)                    = Naturals
tyOf (ADTVal _ _)               = ADTs
tyOf (Atom _)                   = Atoms
tyOf (ComputationType (Type _)) = Types
tyOf (ComputationType _)        = ComputationTypes
tyOf (Float f)                  = IEEEFloats Binary32
--tyOf (Type _)                   = Types 
tyOf (IEEE_Float_32 _)          = IEEEFloats Binary32
tyOf (IEEE_Float_64 _)          = IEEEFloats Binary64
tyOf (Rational _)               = Rationals
tyOf (Map m)                    = maps (injectT Values) (injectT Values) -- TODO find "strongest common type"
tyOf (Set s)                    | S.null s = sets Values
                                | otherwise = sets (tyOf (S.findMax s))
tyOf (Multiset s)               | MS.null s = multisets Values
                                | otherwise = multisets (tyOf (MS.findMax s))
tyOf (Vector v)                 | V.null v = vectors Values
                                | otherwise = vectors (tyOf (v V.! 0))
tyOf VAny                       = Values
tyOf (ValSeq ts)                = Values

type_member_ :: HasValues t => [OpExpr t] -> OpExpr t
type_member_ = binaryOp type_member
-- | Type membership check for primitive types and
-- predefined composite types (non-ADTs).
type_member :: HasValues t => OpExpr t -> OpExpr t -> OpExpr t
type_member = vBinaryOp "type-member" op
  where op v mty = case mty of
         ComputationType (Type t) -> proceed v t
         ComputationType (ComputesType t) -> proceed v t
         ComputationType (ComputesFromType _ t) -> proceed v t
         _ -> SortErr "type-member(V,Ty) not applied to a value and a type"

        proceed v ty = case isInType v ty of
          Nothing -> DomErr "type-member applied to an ADT or a non-type"
          Just b  -> Normal $ inject (tobool b)

isInType :: HasValues t => Values t -> Types t -> Maybe Bool
isInType _ EmptyType = return False
isInType v Values = return True --(not (isNull v)) 
isInType n NullType = return (isNull n)
isInType v (ADT "ground-values" []) = return (isGround v)
isInType v (ADT "strings" []) = return (isString_ v)
isInType (ADTVal "list" vs') (ADT "lists" [ty'])
  | Just ty <- projectT ty', Just vs <- sequence (map project vs') =
  and <$> mapM (flip isInType ty) vs

isInType v (ADT nm tys) = Nothing
isInType (ADTVal _ _) ADTs = return True
isInType (Atom _) Atoms = return True
isInType v Characters | Just _ <- upcastCharacter v = return True
isInType v (IntegersFrom n)
    | Int i <- upcastIntegers v = return (i >= n)
isInType v (IntegersUpTo n)
    | Int i <- upcastIntegers v = return (i <= n)
isInType (ComputationType _) Types = return True
isInType (ComputationType (ComputesFromType _ _)) ComputationTypes = return True
isInType (ComputationType (ComputesType _)) ComputationTypes = return True
isInType (ComputationType (Type _)) ComputationTypes = return True
isInType (IEEE_Float_32 _) (IEEEFloats Binary32) = return True
isInType (IEEE_Float_64 _) (IEEEFloats Binary64) = return True
isInType v Integers | Int _ <- upcastIntegers v = return True
isInType v Naturals | Nat _ <- upcastNaturals v = return True
isInType v Rationals | Rational _ <- upcastRationals v = return True
isInType (ADTVal c [p]) UnicodeCharacters |  c == unicode_cons = return True
isInType v AsciiCharacters = isInType v UnicodeCharacters -- requires interpreter to check whether character is in the character range 
isInType v ISOLatinCharacters = isInType v UnicodeCharacters -- requires interpreter to check whether character is in the character range 
isInType v BMPCharacters = isInType v UnicodeCharacters -- requires interpreter to check whether character is in the character range 
isInType v (Union ty1 ty2) = (||) <$> isInType v ty1 <*> isInType v ty2
isInType v (Complement ty) = not <$> isInType v ty
isInType v (Intersection ty1 ty2) = (&&) <$> isInType v ty1 <*> isInType v ty2
isInType _ _ = return False

isInTupleType :: HasValues t => [Values t] -> [Types t] -> Maybe Bool
isInTupleType vs ttparams = and <$> sequence (zipWith isInType vs ttparams)