{-# LANGUAGE AllowAmbiguousTypes   #-}
{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds             #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeApplications      #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE UndecidableInstances  #-}

module PlutusTx.Blueprint.Class where

import Prelude hiding (maximum, minimum)

import Data.ByteString (ByteString)
import Data.Kind (Type)
import PlutusTx.Blueprint.Schema (ListSchema (..), PairSchema (..), Schema (..), emptyBytesSchema,
                                  emptyIntegerSchema)
import PlutusTx.Blueprint.Schema.Annotation (emptySchemaInfo)
import PlutusTx.Builtins (BuiltinByteString, BuiltinData, BuiltinString)
import PlutusTx.Builtins.Internal (BuiltinBool, BuiltinList, BuiltinPair, BuiltinUnit)

{- |
  A class of types that have a Blueprint schema definition
  and can reference other schema definitions of other types.
-}
class HasBlueprintSchema (t :: Type) (referencedTypes :: [Type]) where
  schema :: Schema referencedTypes

instance HasBlueprintSchema Int referencedTypes where
  schema :: Schema referencedTypes
schema = SchemaInfo -> IntegerSchema -> Schema referencedTypes
forall (referencedTypes :: [*]).
SchemaInfo -> IntegerSchema -> Schema referencedTypes
SchemaInteger SchemaInfo
emptySchemaInfo IntegerSchema
emptyIntegerSchema

instance HasBlueprintSchema Integer referencedTypes where
  schema :: Schema referencedTypes
schema = SchemaInfo -> IntegerSchema -> Schema referencedTypes
forall (referencedTypes :: [*]).
SchemaInfo -> IntegerSchema -> Schema referencedTypes
SchemaInteger SchemaInfo
emptySchemaInfo IntegerSchema
emptyIntegerSchema

instance HasBlueprintSchema BuiltinData referencedTypes where
  schema :: Schema referencedTypes
schema = SchemaInfo -> Schema referencedTypes
forall (referencedTypes :: [*]).
SchemaInfo -> Schema referencedTypes
SchemaBuiltInData SchemaInfo
emptySchemaInfo

instance HasBlueprintSchema BuiltinUnit referencedTypes where
  schema :: Schema referencedTypes
schema = SchemaInfo -> Schema referencedTypes
forall (referencedTypes :: [*]).
SchemaInfo -> Schema referencedTypes
SchemaBuiltInUnit SchemaInfo
emptySchemaInfo

instance HasBlueprintSchema BuiltinBool referencedTypes where
  schema :: Schema referencedTypes
schema = SchemaInfo -> Schema referencedTypes
forall (referencedTypes :: [*]).
SchemaInfo -> Schema referencedTypes
SchemaBuiltInBoolean SchemaInfo
emptySchemaInfo

instance HasBlueprintSchema BuiltinString referencedTypes where
  schema :: Schema referencedTypes
schema = SchemaInfo -> Schema referencedTypes
forall (referencedTypes :: [*]).
SchemaInfo -> Schema referencedTypes
SchemaBuiltInString SchemaInfo
emptySchemaInfo

instance HasBlueprintSchema BuiltinByteString referencedTypes where
  schema :: Schema referencedTypes
schema = SchemaInfo -> BytesSchema -> Schema referencedTypes
forall (referencedTypes :: [*]).
SchemaInfo -> BytesSchema -> Schema referencedTypes
SchemaBytes SchemaInfo
emptySchemaInfo BytesSchema
emptyBytesSchema

instance HasBlueprintSchema ByteString referencedTypes where
  schema :: Schema referencedTypes
schema = SchemaInfo -> BytesSchema -> Schema referencedTypes
forall (referencedTypes :: [*]).
SchemaInfo -> BytesSchema -> Schema referencedTypes
SchemaBytes SchemaInfo
emptySchemaInfo BytesSchema
emptyBytesSchema

instance
  (HasBlueprintSchema a referencedTypes) =>
  HasBlueprintSchema [a] referencedTypes
  where
  schema :: Schema referencedTypes
schema =
    SchemaInfo -> ListSchema referencedTypes -> Schema referencedTypes
forall (referencedTypes :: [*]).
SchemaInfo -> ListSchema referencedTypes -> Schema referencedTypes
SchemaList
      SchemaInfo
emptySchemaInfo
      ( MkListSchema
          { $sel:minItems:MkListSchema :: Maybe Natural
minItems = Maybe Natural
forall a. Maybe a
Nothing
          , $sel:maxItems:MkListSchema :: Maybe Natural
maxItems = Maybe Natural
forall a. Maybe a
Nothing
          , $sel:uniqueItems:MkListSchema :: Maybe Bool
uniqueItems = Maybe Bool
forall a. Maybe a
Nothing
          , $sel:itemSchema:MkListSchema :: Schema referencedTypes
itemSchema = forall a (referencedTypes :: [*]).
HasBlueprintSchema a referencedTypes =>
Schema referencedTypes
schema @a
          }
      )

instance
  (HasBlueprintSchema a referencedTypes) =>
  HasBlueprintSchema (BuiltinList a) referencedTypes
  where
  schema :: Schema referencedTypes
schema = SchemaInfo -> Schema referencedTypes -> Schema referencedTypes
forall (referencedTypes :: [*]).
SchemaInfo -> Schema referencedTypes -> Schema referencedTypes
SchemaBuiltInList SchemaInfo
emptySchemaInfo (forall a (referencedTypes :: [*]).
HasBlueprintSchema a referencedTypes =>
Schema referencedTypes
schema @a)

instance
  ( HasBlueprintSchema a referencedTypes
  , HasBlueprintSchema b referencedTypes
  ) =>
  HasBlueprintSchema (BuiltinPair a b) referencedTypes
  where
  schema :: Schema referencedTypes
schema = SchemaInfo -> PairSchema referencedTypes -> Schema referencedTypes
forall (referencedTypes :: [*]).
SchemaInfo -> PairSchema referencedTypes -> Schema referencedTypes
SchemaBuiltInPair SchemaInfo
emptySchemaInfo (Schema referencedTypes
-> Schema referencedTypes -> PairSchema referencedTypes
forall (referencedTypes :: [*]).
Schema referencedTypes
-> Schema referencedTypes -> PairSchema referencedTypes
MkPairSchema (forall a (referencedTypes :: [*]).
HasBlueprintSchema a referencedTypes =>
Schema referencedTypes
schema @a) (forall a (referencedTypes :: [*]).
HasBlueprintSchema a referencedTypes =>
Schema referencedTypes
schema @b))