-- SPDX-FileCopyrightText: 2021 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA

-- | A collection of type families and other type-level machinery
-- for working with Michelson types.
module Morley.Michelson.Typed.TypeLevel
  ( -- * Right-combed pairs
    IsPair
  , CombedPairLeafCount
  , CombedPairNodeCount
  , CombedPairLeafCountIsAtLeast
  , CombedPairNodeIndexIsValid
  )
  where

import Morley.Michelson.Typed.T (T(TPair))
import Morley.Util.Peano (Peano, ToPeano, pattern S, pattern Z)

----------------------------------------------------------------------------
-- Right-combed pairs
----------------------------------------------------------------------------

type family IsPair (pair :: T) :: Bool where
  IsPair ('TPair _ _) = 'True
  IsPair _ = 'False

-- | Returns the number of leaves in a right-combed pair.
--
-- > CombedPairLeafCount ('TPair 'TInt ('TPair 'TString 'TUnit))
-- > ~
-- > ToPeano 3
--
-- If the pair contains sub-trees to the left, they will not be accounted for.
-- E.g. the length of @pair w (pair x y) z@ is 3.
type family CombedPairLeafCount (t :: T) :: Peano where
  CombedPairLeafCount ('TPair _ ('TPair y z)) = 'S (CombedPairLeafCount ('TPair y z))
  CombedPairLeafCount ('TPair _ _) = ToPeano 2

-- | Returns the number of nodes in a right-combed pair.
type family CombedPairNodeCount (t :: T) :: Peano where
  CombedPairNodeCount ('TPair _ ('TPair y z)) = 'S ('S (CombedPairNodeCount ('TPair y z)))
  CombedPairNodeCount ('TPair _ _) = ToPeano 3

-- | Checks whether a pair contains at least @n@ elements.
type family CombedPairLeafCountIsAtLeast (n :: Peano) (t :: T) :: Bool where
  CombedPairLeafCountIsAtLeast ('S ('S 'Z)) ('TPair _ _) = 'True
  CombedPairLeafCountIsAtLeast ('S n) ('TPair _ y) = CombedPairLeafCountIsAtLeast n y
  CombedPairLeafCountIsAtLeast ('S _) _ = 'False

-- | For a given node index @i@, this type family checks whether
-- a pair has at least @i+1@ nodes.
--
-- Note that the index 0 is always valid, even if the input type is not a pair.
type family CombedPairNodeIndexIsValid (nodeIndex :: Peano) (pair :: T) :: Bool where
  CombedPairNodeIndexIsValid 'Z                   _                = 'True
  CombedPairNodeIndexIsValid ('S 'Z)              ('TPair _ _)     = 'True
  CombedPairNodeIndexIsValid ('S ('S nodeIndex')) ('TPair _ right) = CombedPairNodeIndexIsValid nodeIndex' right
  CombedPairNodeIndexIsValid _ _                                   = 'False