{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
-- | Measuring the complexity of type class declarations
module Language.Haskell.Homplexity.TypeClassComplexity
  ( NonTypeDeclCount
  , nonTypeDeclCountT
  , AssocTypeCount
  , assocTypeCountT
  ) where

import           Data.Data
import           Data.Generics.Uniplate.Data              ()
import           Data.Maybe
--import Language.Haskell.Exts.SrcLoc
import           Language.Haskell.Exts.Syntax
import           Language.Haskell.Homplexity.CodeFragment
import           Language.Haskell.Homplexity.Metric
import           Language.Haskell.Homplexity.Utilities

-- NOTE: It is non-trivial to decide whether a class declaration
-- is a method or a value. To correctly do that, we would have to
-- see through type synonyms. So, we will aggregate methods and values.

-- * Type class method count
-- | Represents the number of methods and value in a type class.
newtype NonTypeDeclCount = NonTypeDeclCount { unNonTypeDeclCount :: Int }
  deriving (Eq, Ord, Enum, Num, Real, Integral)

-- | For passing @NonTypeDeclCount@ type as parameter.
nonTypeDeclCountT :: Proxy NonTypeDeclCount
nonTypeDeclCountT  = Proxy

instance Show NonTypeDeclCount where
  showsPrec _ (NonTypeDeclCount mc) = ("method + value count of " ++)
                                    . shows mc

instance Metric NonTypeDeclCount TypeClass where
  measure = NonTypeDeclCount . sumOf method . fromMaybe [] . tcDecls where

    method :: ClassDecl l -> Int
    method (ClsDecl _ TypeSig{}) = 1
    method _                     = 0

-- * Type class associated types count
-- | Represents the number of associated types in a type class.
-- It includes both associated type and data families.
newtype AssocTypeCount = AssocTypeCount { unAssocTypeCount :: Int }
  deriving (Eq, Ord, Enum, Num, Real, Integral)

-- | For passing @AssocTypeCount@ type as parameter.
assocTypeCountT :: Proxy AssocTypeCount
assocTypeCountT  = Proxy

instance Show AssocTypeCount where
  showsPrec _ (AssocTypeCount atc) = ("associated type count of " ++)
                                   . shows atc

instance Metric AssocTypeCount TypeClass where
  measure = AssocTypeCount . sumOf assocType . fromMaybe [] . tcDecls where

    assocType :: ClassDecl l -> Int
    assocType ClsTyFam{}   = 1
    assocType ClsTyDef{}   = 1
    assocType ClsDataFam{} = 1
    assocType _            = 0