{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE TypeOperators         #-}
{-# LANGUAGE PolyKinds             #-}
{-# LANGUAGE GADTs                 #-}
{-# OPTIONS_GHC -Wno-orphans       #-}
-- |change classification algorithm
module Data.HDiff.Change.Classify where

import Data.List (nub)
import Data.Proxy
import Data.Type.Equality
-------------------------------
import Generics.MRSOP.Util
import Generics.MRSOP.Base
import Generics.MRSOP.Holes
-------------------------------
import Data.Exists
import Data.HDiff.Change
import Data.HDiff.MetaVar

-----------------------------------------
-- Change Classification algo

instance (EqHO ki , TestEquality ki) => Eq (Exists (Holes ki codes (MetaVarIK ki))) where
  (Exists v) == (Exists u) =
    case testEquality v u of
      Nothing   -> False
      Just Refl -> v == u

getConstrSNat :: (IsNat n) => Constr sum n -> SNat n
getConstrSNat _ = getSNat (Proxy :: Proxy n)

holesGetMultiplicities :: Int -> Holes ki codes f at -> [Exists (Holes ki codes f)]
holesGetMultiplicities k utx
  | holesArity utx == k = [Exists utx]
  | otherwise = case utx of
      HPeel _ _ p -> concat $ elimNP (holesGetMultiplicities k) p
      _           -> []


data ChangeClass
  = CPerm | CMod | CId | CIns | CDel
  deriving (Eq , Show , Ord)

changeClassify :: (EqHO ki , TestEquality ki)
               => CChange ki codes at -> ChangeClass
changeClassify c
  | isCpy c   = CId
  | otherwise =
  let mis = holesGetMultiplicities 0 (cCtxIns c)
      mds = holesGetMultiplicities 0 (cCtxDel c)
      vi = holesGetHolesAnnWith' metavarGet (cCtxIns c)
      vd = holesGetHolesAnnWith' metavarGet (cCtxDel c)
      -- permutes = vi == vd
      dups     = vi /= nub vi || vd /= nub vd
   in case (length mis , length mds) of
        (0 , 0) -> CPerm -- can't duplicate as one variable on one side would
                         -- be left unused; Can't have that so a tree with
                         -- multiplicity 0 would be there
        (0 , _) -> if dups       then CMod else CDel
        (_ , 0) -> if dups       then CMod else CIns
        (_ , _) -> if mis == mds then CPerm else CMod

isIns , isDel :: (TestEquality ki , EqHO ki) => CChange ki codes ix -> Bool
isIns c = changeClassify c == CIns
isDel c = changeClassify c == CDel