module Language.Haskell.Tools.Refactor.Builtin.ExtensionOrganizer.Checkers.CPPChecker where

import GHC hiding (Module)
import StringBuffer

import Data.List (isSubsequenceOf)
import Data.String (fromString)
import Text.PortableLines.ByteString.Lazy (lines8)

import Language.Haskell.Tools.PrettyPrint
import Language.Haskell.Tools.Refactor
import Language.Haskell.Tools.Refactor.Builtin.ExtensionOrganizer.ExtMonad

gblChkCPP :: CheckNode Module
gblChkCPP = conditional gblChkCPP' Cpp

-- TODO (after GHC upgrade): Rewrite this with mgLookupModule
gblChkCPP' :: CheckNode Module
gblChkCPP' m = do
  ms <- getModSummary . moduleName . semanticsModule $ m
  let cppSrc = preprocessedSrc ms
  case ml_hs_file . ms_location $ ms of
    Just fp -> do
      let lines'  = lines8 . fromString
          cppSrc' = lines' . rmDefaultIncludes fp $ cppSrc
          origSrc = lines' . prettyPrint $ m
      when (cppSrc' /= origSrc) (addEvidenceLoc Cpp noSrcSpan)
    _ -> addEvidenceLoc Cpp noSrcSpan
  return m

-- CPP inserts a line before and after the default includes,
-- that contain the filepath of the module.
-- We want to remove these lines, and everything between them.
rmDefaultIncludes :: FilePath -> String -> String
rmDefaultIncludes fp = unlines . tail
                     . dropWhile (not . (fp `isSubsequenceOf`))
                     . tail . lines

-- | Returns the preprocessed source code.
-- If it is not present, it returns an empty string.
preprocessedSrc :: ModSummary -> String
preprocessedSrc = maybe "" strBufToStr . ms_hspp_buf

strBufToStr :: StringBuffer -> String
strBufToStr sb@(StringBuffer _ len _) = lexemeToString sb len