{-# LANGUAGE OverloadedStrings #-}

module Funcons.Operations.Tuples where

import Funcons.Operations.Internal
import Funcons.Operations.Booleans

library :: HasValues t => Library t
library = libFromList [
    ("tuples", NaryExpr tuples_)
  , ("tuple", NaryExpr tuple_)
  , ("tuple-index", BinaryExpr tuple_index)
  , ("empty-tuple", NullaryExpr  empty_tuple)
  , ("tuple-prepend", BinaryExpr tuple_prepend)
  , ("is-empty", UnaryExpr tuple_is_empty)
  , ("tuple-head", UnaryExpr tupleHeadOp)
  , ("tuple-tail", UnaryExpr tupleTailOp)
  ]

tuples_ :: HasValues t => [OpExpr t] -> OpExpr t
tuples_ = NaryOp "tuples" (Normal . injectT . ADT "tuples")

tuple_is_empty_ :: HasValues t => [OpExpr t] -> OpExpr t
tuple_is_empty_ = unaryOp tuple_is_empty
tuple_is_empty :: HasValues t => OpExpr t -> OpExpr t
tuple_is_empty = vUnaryOp "is-empty" op
  where op (ADTVal "tuple" vs) = Normal $ inject $ tobool (null vs)
        op _ = SortErr "is-empty not applied to a tuple"

empty_tuple_, tuple_prepend_ :: HasValues t => [OpExpr t] -> OpExpr t
empty_tuple_ = nullaryOp empty_tuple
tuple_prepend_ = binaryOp tuple_prepend

empty_tuple :: HasValues t => OpExpr t
empty_tuple = vNullaryOp "empty-tuple" (Normal $ inject (tuple []))

tuple_prepend :: HasValues t => OpExpr t -> OpExpr t -> OpExpr t
tuple_prepend = vBinaryOp "tuple-prepend" op
  where op v (ADTVal "tuple" vs) = Normal $ inject (ADTVal "tuple" (inject v : vs))
        op _ _ = SortErr "tuple-prepend not applied to a value and a tuple"

tuple_ :: HasValues t => [OpExpr t] -> OpExpr t
tuple_ = vNaryOp "tuple" op
  where op ys = Normal $ inject (tuple ys)

tuple_index_ :: HasValues t => [OpExpr t] -> OpExpr t
tuple_index_ = binaryOp tuple_index
tuple_index :: HasValues t => OpExpr t -> OpExpr t -> OpExpr t
tuple_index = vBinaryOp "tuple-index" op
  where op (ADTVal "tuple" ts) v
          | Nat n' <- upcastNaturals v, let n :: Int; n = fromInteger n'
            = case () of
               () | n >= 1 && n <= length ts -> Normal $ ts !! (n - 1)
               _ -> SortErr "tuple-index not in range"
          | otherwise = SortErr ("tuple-index not applied to a natural number: " ++ ppValues (const "_") v)
        op _ _ = SortErr "tuple-index not applied to a tuple"

tuple_head_, tuple_tail_ :: HasValues t => [OpExpr t] -> OpExpr t
tuple_head_ = unaryOp tupleHeadOp
tuple_tail_ = unaryOp tupleTailOp
tupleHeadOp,tupleTailOp :: HasValues t => OpExpr t -> OpExpr t
tupleHeadOp = vUnaryOp "head" op
  where op (ADTVal "tuple" [])      = DomErr "tuple-head of empty tuple"
        op (ADTVal "tuple" (x:xs))  = Normal x
        op _                        = SortErr "tuple-head not applied to a tuple"
tupleTailOp = vUnaryOp "tail" op
  where op (ADTVal "tuple" [])      = DomErr "tupletail of empty tuple"
        op (ADTVal "tuple" (x:xs))  = Normal $ inject (ADTVal "tuple" xs)
        op _                        = SortErr "tuple-tail not applied to a tuple"