{-# 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 (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 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))