module Language.C.Comments
  ( Comment
  , CommentFormat(SingleLine,MultiLine)
  , commentPosition
  , commentText
  , commentTextWithoutMarks
  , commentFormat
  , comments
  , commentsFromString
  ) where
  
import Control.Arrow
import Data.Maybe (maybeToList)
import Language.C.Comments.Lexer
import Language.C.Comments.LineParser (parseLines)
import Language.C.Data.Position

convertPosn :: FilePath -> AlexPosn -> Position
convertPosn file (AlexPn offset line col) = position offset file line col

parseComments :: String -> (String,[(AlexPosn,String,CommentFormat)])
parseComments = 
  alexScanTokens >>> unzip >>> (concat *** concatMap maybeToList)

-- | Comment positions use Language.C.Data.Position for compatibility with
-- Language.C.
data Comment = Comment {
  -- | The position of the comment within the source file.
  commentPosition :: Position,
  -- | The text of a comment (including the comment marks).
  commentText :: String,
  -- | The format of a comment (single- or multi-line).
  commentFormat :: CommentFormat
} deriving (Eq,Show)
  
-- | The text of a comment, but with the comment marks removed.
commentTextWithoutMarks :: Comment -> String
commentTextWithoutMarks c = stripCommentMarks fmt (commentText c)
  where fmt = commentFormat c

-- | Comments are ordered by position within files.
instance Ord Comment where
  compare x y = compare (commentPosition x) (commentPosition y)

stripCommentMarks :: CommentFormat -> String -> String
stripCommentMarks SingleLine = drop 2
stripCommentMarks MultiLine = reverse . drop 2 . reverse . drop 2

makeComment :: FilePath -> (AlexPosn,String,CommentFormat) -> Comment
makeComment file (pos,txt,fmt) = Comment pos' txt fmt
  where pos' = convertPosn file pos

commentsInFile :: FilePath -> String -> [Comment]
commentsInFile file code = map (makeComment file) cmnts
  where joinBrokenLines = unlines . parseLines
        (_,cmnts) = parseComments (joinBrokenLines code)

-- | Extract comments from a C file.
comments :: FilePath -> IO [Comment]
comments file = do
  code <- readFile file
  return $ commentsInFile file code

-- | Extract comments from a string.  A comment's position contains a
-- filename; this method uses the empty string in its place.
commentsFromString :: String -> [Comment]
commentsFromString = commentsInFile ""