content: {-# LANGUAGE DeriveGeneric #-}
  
  module Unused.Types
      ( SearchTerm(..)
      , TermMatch(..)
      , TermResults(..)
      , TermMatchSet
      , RemovalLikelihood(..)
      , Removal(..)
      , Occurrences(..)
      , GitContext(..)
      , GitCommit(..)
      , searchTermToString
      , resultsFromMatches
      , tmDisplayTerm
      , totalFileCount
      , totalOccurrenceCount
      , appOccurrenceCount
      , removalLikelihood
      , resultAliases
      ) where
  
  import Control.Monad (liftM2)
  import Data.Csv (FromRecord, ToRecord)
  import qualified Data.List as L
  import qualified Data.Map.Strict as Map
  import qualified Data.Maybe as M
  import qualified GHC.Generics as G
  import qualified Unused.Regex as R
  
  data SearchTerm
      = OriginalTerm String
      | AliasTerm String
                  String
      deriving (Eq, Show)
  
  searchTermToString :: SearchTerm -> String
  searchTermToString (OriginalTerm s) = s
  searchTermToString (AliasTerm _ a) = a
  
  data TermMatch = TermMatch
      { tmTerm :: String
      , tmPath :: String
      , tmAlias :: Maybe String
      , tmOccurrences :: Int
      } deriving (Eq, Show, G.Generic)
  
  instance FromRecord TermMatch
  
  instance ToRecord TermMatch
  
  data Occurrences = Occurrences
      { oFiles :: Int
      , oOccurrences :: Int
      } deriving (Eq, Show)
  
  data TermResults = TermResults
      { trTerm :: String
      , trTerms :: [String]
      , trMatches :: [TermMatch]
      , trTestOccurrences :: Occurrences
      , trAppOccurrences :: Occurrences
      , trTotalOccurrences :: Occurrences
      , trRemoval :: Removal
      , trGitContext :: Maybe GitContext
      } deriving (Eq, Show)
  
  data Removal = Removal
      { rLikelihood :: RemovalLikelihood
      , rReason :: String
      } deriving (Eq, Show)
  
  newtype GitContext = GitContext
      { gcCommits :: [GitCommit]
      } deriving (Eq, Show)
  
  newtype GitCommit = GitCommit
      { gcSha :: String
      } deriving (Eq, Show)
  
  data RemovalLikelihood
      = High
      | Medium
      | Low
      | Unknown
      | NotCalculated
      deriving (Eq, Show)
  
  type TermMatchSet = Map.Map String TermResults
  
  totalFileCount :: TermResults -> Int
  totalFileCount = oFiles . trTotalOccurrences
  
  totalOccurrenceCount :: TermResults -> Int
  totalOccurrenceCount = oOccurrences . trTotalOccurrences
  
  appOccurrenceCount :: TermResults -> Int
  appOccurrenceCount = oOccurrences . trAppOccurrences
  
  removalLikelihood :: TermResults -> RemovalLikelihood
  removalLikelihood = rLikelihood . trRemoval
  
  resultAliases :: TermResults -> [String]
  resultAliases = trTerms
  
  tmDisplayTerm :: TermMatch -> String
  tmDisplayTerm = liftM2 M.fromMaybe tmTerm tmAlias
  
  resultsFromMatches :: [TermMatch] -> TermResults
  resultsFromMatches tms =
      TermResults
      { trTerm = resultTerm terms
      , trTerms = L.sort $ L.nub terms
      , trMatches = tms
      , trAppOccurrences = appOccurrence
      , trTestOccurrences = testOccurrence
      , trTotalOccurrences =
            Occurrences
                (sum $ map oFiles [appOccurrence, testOccurrence])
                (sum $ map oOccurrences [appOccurrence, testOccurrence])
      , trRemoval = Removal NotCalculated "Likelihood not calculated"
      , trGitContext = Nothing
      }
    where
      testOccurrence = testOccurrences tms
      appOccurrence = appOccurrences tms
      terms = map tmDisplayTerm tms
      resultTerm (x:_) = x
      resultTerm _ = ""
  
  appOccurrences :: [TermMatch] -> Occurrences
  appOccurrences ms = Occurrences appFiles appOccurrences'
    where
      totalFiles = length $ L.nub $ map tmPath ms
      totalOccurrences = sum $ map tmOccurrences ms
      tests = testOccurrences ms
      appFiles = totalFiles - oFiles tests
      appOccurrences' = totalOccurrences - oOccurrences tests
  
  testOccurrences :: [TermMatch] -> Occurrences
  testOccurrences ms = Occurrences totalFiles totalOccurrences
    where
      testMatches = filter termMatchIsTest ms
      totalFiles = length $ L.nub $ map tmPath testMatches
      totalOccurrences = sum $ map tmOccurrences testMatches
  
  testDir :: String -> Bool
  testDir = R.matchRegex "(spec|tests?|features)\\/"
  
  testSnakeCaseFilename :: String -> Bool
  testSnakeCaseFilename = R.matchRegex ".*(_spec|_test)\\."
  
  testCamelCaseFilename :: String -> Bool
  testCamelCaseFilename = R.matchRegex ".*(Spec|Test)\\."
  
  termMatchIsTest :: TermMatch -> Bool
  termMatchIsTest TermMatch {tmPath = path} =
      testDir path || testSnakeCaseFilename path || testCamelCaseFilename path
  