{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE KindSignatures        #-}
{-# LANGUAGE LambdaCase            #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds             #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE UndecidableInstances  #-}

{-# OPTIONS_GHC -Wall #-}

{-|
Actual Fortran language operators. For expressions over normal Fortran values
that are actually representable in Fortran.

+, -, *, /, read array, etc...
-}
module Language.Fortran.Model.Op.Core
  (
    CoreOp(..)
  , Op(..)
  , OpKind(..)
  , OpSpec(..)
  ) where

import           Data.Functor.Compose

import           Data.Singletons.Prelude.List
import           Data.Singletons.TypeLits

import           Data.Vinyl
import           Data.Vinyl.Curry

import           Language.Expression
import           Language.Expression.Pretty

import           Language.Fortran.Model.Repr
import           Language.Fortran.Model.Op.Core.Core
import           Language.Fortran.Model.Op.Core.Eval
import           Language.Fortran.Model.Singletons
import           Language.Fortran.Model.Types


data CoreOp t a where
  CoreOp
    :: Op (Length args) ok
    -> OpSpec ok args result
    -> Rec t args
    -> CoreOp t result

instance HFunctor CoreOp where
instance HTraversable CoreOp where
  htraverse f (CoreOp op opr args) = CoreOp op opr <$> rtraverse f args

instance (MonadEvalFortran r m) => HFoldableAt (Compose m CoreRepr) CoreOp where
  hfoldMap = implHfoldMapCompose $ \(CoreOp op opr args) -> evalCoreOp op opr args

instance (MonadEvalFortran r m) => HFoldableAt (Compose m HighRepr) CoreOp where
  hfoldMap = implHfoldMapCompose $ fmap HRCore . hfoldA .
    hmap (\case
               HRCore x -> x
               HRHigh _ -> error "impossible")

instance Pretty2 CoreOp where
  prettys2Prec p (CoreOp op opr args) = prettysPrecOp p opr op args

showsPrim :: Prim p k a -> a -> ShowS
showsPrim = \case
  PInt8   -> shows
  PInt16  -> shows
  PInt32  -> shows
  PInt64  -> shows
  PBool8  -> shows
  PBool16 -> shows
  PBool32 -> shows
  PBool64 -> shows
  PFloat  -> shows
  PDouble -> shows
  PChar   -> shows

prettysPrecOp
  :: Pretty1 t
  => Int
  -> OpSpec ok args result
  -> Op (Length args) ok
  -> Rec t args -> ShowS
prettysPrecOp p = \case
  OSLit px x -> \case
    OpLit -> runcurry $ showsPrim px x
  OSNum1 _ _ _ -> \case
    OpNeg -> runcurry $ prettys1PrecUnop 8 "-" p
    OpPos -> runcurry $ prettys1PrecUnop 8 "+" p
  OSNum2 _ _ _ _ _ -> \case
    OpAdd -> runcurry $ prettys1PrecBinop 5 " + " p
    OpSub -> runcurry $ prettys1PrecBinop 5 " - " p
    OpMul -> runcurry $ prettys1PrecBinop 6 " * " p
    OpDiv -> runcurry $ prettys1PrecBinop 6 " / " p
  OSLogical1 _ _ -> \case
    OpNot -> runcurry $ prettys1PrecUnop 8 "!" p
  OSLogical2 _ _ _ -> \case
    OpAnd      -> runcurry $ prettys1PrecBinop 3 " && " p
    OpOr       -> runcurry $ prettys1PrecBinop 2 " || " p
    OpEquiv    -> runcurry $ prettys1PrecBinop 1 " <=> " p
    OpNotEquiv -> runcurry $ prettys1PrecBinop 1 " </=> " p
  OSEq _ _ _ _ -> \case
    OpEq -> runcurry $ prettys1PrecBinop 4 " = " p
    OpNE -> runcurry $ prettys1PrecBinop 4 " /= " p
  OSRel _ _ _ _ -> \case
    OpLT -> runcurry $ prettys1PrecBinop 4 " < " p
    OpLE -> runcurry $ prettys1PrecBinop 4 " <= " p
    OpGT -> runcurry $ prettys1PrecBinop 4 " > " p
    OpGE -> runcurry $ prettys1PrecBinop 4 " >= " p
  OSLookup _ -> \case
    OpLookup ->
      runcurry $ \arr i ->
      showParen (p > 9) $ prettys1Prec 10 arr .
                          showString "[" . prettys1Prec 0 i .
                          showString "]"
  OSDeref _ fname -> \case
    OpDeref -> runcurry $ \r ->
      showParen (p > 9) $ prettys1Prec 10 r .
      showString "%" .
      showString (withKnownSymbol fname (symbolVal fname))

-- TODO: HEq instance

-- instance HEq CoreOp where
--   liftHEq he le (CoreOp op1 opr1 args1) (CoreOp op2 opr2 args2) =
--     eqOp op1 op2 &&
--     eqOpR opr1 opr2 &&
--     liftEqRec (he _) args1 args2
