{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
module Test.NoAnnotations where

import Data.Algorithm.Diff
import Data.Algorithm.DiffOutput
import Data.List

import Language.Haskell.GHC.ExactPrint
import Language.Haskell.GHC.ExactPrint.Parsers
import Language.Haskell.GHC.ExactPrint.Types
import Language.Haskell.GHC.ExactPrint.Utils

import qualified Control.Monad.IO.Class as GHC
import qualified GHC                    as GHC hiding (parseModule)
import qualified GHC.Hs.Dump            as GHC
import qualified GHC.Utils.Outputable   as GHC

import System.Directory
import System.FilePath

import Test.Common

import Test.HUnit

{-# ANN module "HLint: ignore Eta reduce" #-}

-- ---------------------------------------------------------------------

noAnnotationTests :: Test
noAnnotationTests = TestLabel "no annotation tests" $ TestList
  [
    TestLabel "no annotations"
       (TestList noAnnTests)
  ]

noAnnTests :: [Test]
noAnnTests = [

        ]

-- ---------------------------------------------------------------------

mkPrettyRoundtrip :: LibDir -> FilePath -> FilePath -> Test
mkPrettyRoundtrip libdir dir fp = mkParsingTest (prettyRoundtripTest libdir) dir fp

prettyRoundtripTest :: LibDir -> FilePath -> IO Report
prettyRoundtripTest libdir origFile = do
      -- res <- parseModuleApiAnnsWithCpp defaultCppOptions origFile
      res <- parseModuleEpAnnsWithCpp libdir defaultCppOptions origFile
      case res of
        Left m -> return . Left $ ParseFailure (showErrorMessages m)
        Right (injectedComments, _dflags, parsed)  -> do
          res2 <- GHC.liftIO (runPrettyRoundTrip libdir origFile parsed injectedComments)
          case res2 of
            Left m -> return . Left $ ParseFailure (showErrorMessages m)
            Right parsed' -> do
              let
                originalStructure  = astStructure parsed
                roundtripStructure = astStructure parsed'
                (status,debugTxt') = if roundtripStructure == originalStructure
                  then (Success, "ok")
                  else (RoundTripFailure,diffText originalStructure roundtripStructure
                         ++ sep ++ originalStructure ++ sep ++ roundtripStructure)
                cppStatus = Nothing
                debugTxt = intercalate sep [ debugTxt'
                                           , originalStructure
                                           , roundtripStructure
                                           , showAst parsed
                                           ]
                sep = "\n=====================================\n"
              return $ Right Report {debugTxt,status,cppStatus}

-- ---------------------------------------------------------------------

runPrettyRoundTrip :: LibDir -> FilePath -> GHC.ParsedSource
                   -> [GHC.LEpaComment]
                   -> IO (ParseResult GHC.ParsedSource)
runPrettyRoundTrip libdir origFile !parsedOrig _cs = do
  let priorComments = GHC.priorComments $ GHC.epAnnComments $ GHC.hsmodAnn
        $ GHC.hsmodExt $ GHC.unLoc parsedOrig
  let comments = concatMap tokComment priorComments
  let pragmas = filter (\(Comment c _ _ _) -> isPrefixOf "{-#" c ) comments
  let pragmaStr = intercalate "\n" $ map commentContents pragmas

  let !printed = pragmaStr ++ "\n" ++ exactPrint parsedOrig

  parseString libdir origFile printed parsedOrig


parseString :: LibDir -> FilePath -> String -> GHC.ParsedSource
            -> IO (ParseResult GHC.ParsedSource)
parseString libdir origFile src origParsed = do
  tmpDir <- getTemporaryDirectory
  let workDir = tmpDir </> "ghc-exactprint" </> "noannotations"
  -- putStrLn $ "workDir=" ++ workDir
  createDirectoryIfMissing True workDir
  let fileName = workDir </> takeFileName origFile
  writeFile (workDir </> takeFileName origFile <.> ".anns")
      (showAst  origParsed)
  writeFile fileName src
  parseModule libdir fileName

-- ---------------------------------------------------------------------

diffText :: String -> String -> String
diffText f1 f2 = diff
  where
    d = getGroupedDiff (lines f1) (lines f2)
    diff = ppDiff d

-- ---------------------------------------------------------------------

-- |Convert an AST with comments into a string representing the structure only
-- (i.e. ignoring locations), to be used for comparisons between the original
-- AST and the one after pretty-print roundtripping.
astStructure :: GHC.ParsedSource -> String
astStructure parsed = r
  where
    r = GHC.showSDocUnsafe
        $ GHC.showAstData GHC.BlankSrcSpanFile GHC.NoBlankEpAnnotations parsed
