-- |

module Language.REST where

import Control.Monad.Identity
import Data.Hashable
import Data.Maybe
import qualified Data.HashSet as S
import qualified Data.HashMap.Strict as M

import Language.REST.Types
import Language.REST.OCToAbstract
import Language.REST.OrderingConstraints
import Language.REST.OrderingConstraints.Strict (strictOC)
import Language.REST.OrderingConstraints.Lazy (lazyOC)
import Language.REST.OrderingConstraints.ADT (adtOC)
import Language.REST.RPO
import Language.REST.RuntimeTerm
import Language.REST.Op
import Language.REST.OpOrdering
import qualified Language.REST.WQO as WQO


adtRPO z3 = lift (adtOC z3) rpo
-- lazyRPO = lift lazyOC rpo
-- strictRPO = lift strictOC rpo

-- Assume vars are arity 0, which is usually correct
getVars :: RuntimeTerm -> S.HashSet Op
getVars (App op []) = S.singleton op
getVars (App op xs) = S.unions (map getVars xs)


varsEQ :: RuntimeTerm -> RuntimeTerm -> WQO.WQO Op
varsEQ t1 t2 =
  let
    vars = getVars t1 `S.union` getVars t2
  in
    fromJust $ WQO.mergeAll (map (uncurry (=.)) (pairs (S.toList vars)))
  where
    pairs xs | length xs < 2 = []
    pairs xs | otherwise = zip xs (tail xs)

cgen :: (Show (oc Op), Eq (oc Op), Hashable (oc Op)) => ConstraintGen oc Op RuntimeTerm Identity
cgen impl r oc t1 t2 =
  let
    Identity rpoc = rpo impl r oc t1 t2
  in
    return $ addConstraint impl (varsEQ t1 t2) rpoc