{-# LANGUAGE CPP #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
#if __GLASGOW_HASKELL__ < 710
{-# LANGUAGE DeriveDataTypeable #-}
#endif

module Argon.Types (ComplexityBlock(CC), AnalysisResult, Config(..)
                   , OutputMode(..), GhcParseError(..), defaultConfig)
    where

import Data.List (intercalate)
import Data.Aeson
import Data.Typeable
import Control.Exception (Exception)

import qualified DynFlags as GHC
import Argon.Loc


data GhcParseError = GhcParseError {
    loc :: Loc
  , msg :: String
} deriving (Typeable)

-- | Hold the data associated to a function binding:
--   @(location, function name, complexity)@.
newtype ComplexityBlock = CC (Loc, String, Int)
                        deriving (Show, Eq, Ord)

-- | Represent the result of the analysis of one file.
--   It can either be an error message or a list of
--   'ComplexityBlock's.
type AnalysisResult = Either String [ComplexityBlock]

-- | Type holding all the options passed from the command line.
data Config = Config {
    -- | Minimum complexity a block has to have to be shown in results.
    minCC       :: Int
    -- | Path to the main Cabal file
  , exts        :: [GHC.ExtensionFlag]
    -- | Header files to be automatically included before preprocessing
  , headers     :: [FilePath]
    -- | Additional include directories for the C preprocessor
  , includeDirs :: [FilePath]
    -- | Describe how the results should be exported.
  , outputMode  :: OutputMode
  }

-- | Type describing how the results should be exported.
data OutputMode = BareText -- ^ Text-only output, no colors.
                | Colored  -- ^ Text-only output, with colors.
                | JSON     -- ^ Data is serialized to JSON.
                deriving (Show, Eq)

-- | Default configuration options.
--
--   __Warning__: These are not Argon's default options.
defaultConfig :: Config
defaultConfig = Config { minCC       = 1
                       , exts        = []
                       , headers     = []
                       , includeDirs = []
                       , outputMode  = JSON
                       }

instance Exception GhcParseError

instance Show GhcParseError where
    show e = tagMsg (loc e) $ fixNewlines (msg e)
        where fixNewlines = intercalate "\n\t\t" . lines

instance ToJSON ComplexityBlock where
    toJSON (CC ((s, c), func, cc)) =
        object [ "lineno"     .= s
               , "col"        .= c
               , "name"       .= func
               , "complexity" .= cc
               ]

instance {-# OVERLAPPING #-} ToJSON (FilePath, AnalysisResult) where
    toJSON (p, Left err) = object [ "path"    .= p
                                  , "type"    .= ("error" :: String)
                                  , "message" .= err
                                  ]
    toJSON (p, Right rs) = object [ "path"   .= p
                                  , "type"   .= ("result" :: String)
                                  , "blocks" .= rs
                                  ]