module Language.Fortran.Parser.Any where

import Language.Fortran.AST
import Language.Fortran.Util.ModFile
import Language.Fortran.ParserMonad (FortranVersion(..), ParseErrorSimple(..), fromParseResult)

import Language.Fortran.Parser.Fortran66 ( fortran66Parser, fortran66ParserWithModFiles )
import Language.Fortran.Parser.Fortran77 ( fortran77Parser, fortran77ParserWithModFiles
                                         , extended77Parser, extended77ParserWithModFiles )
import Language.Fortran.Parser.Fortran90 ( fortran90Parser, fortran90ParserWithModFiles )

import qualified Data.ByteString.Char8 as B
import Data.Char (toLower)
import Data.List (isSuffixOf)

deduceVersion :: String -> FortranVersion
deduceVersion path
  | isExtensionOf ".f"      = Fortran77Extended
  | isExtensionOf ".for"    = Fortran77
  | isExtensionOf ".fpp"    = Fortran77
  | isExtensionOf ".ftn"    = Fortran77
  | isExtensionOf ".f90"    = Fortran90
  | isExtensionOf ".f03"    = Fortran2003
  | isExtensionOf ".f2003"  = Fortran2003
  | isExtensionOf ".f08"    = Fortran2008
  | isExtensionOf ".f2008"  = Fortran2008
  | otherwise               = Fortran90 -- default
  where
    isExtensionOf = flip isSuffixOf $ map toLower path

type Parser = B.ByteString -> String -> Either ParseErrorSimple (ProgramFile A0)
parserVersions :: [(FortranVersion, Parser)]
parserVersions =
  [ (Fortran66, fromParseResult `after` fortran66Parser)
  , (Fortran77, fromParseResult `after` fortran77Parser)
  , (Fortran77Extended, fromParseResult `after` extended77Parser)
  , (Fortran90, fromParseResult `after` fortran90Parser) ]

type ParserWithModFiles = ModFiles -> B.ByteString -> String -> Either ParseErrorSimple (ProgramFile A0)
parserWithModFilesVersions :: [(FortranVersion, ParserWithModFiles)]
parserWithModFilesVersions =
  [ (Fortran66, \m s -> fromParseResult . fortran66ParserWithModFiles m s)
  , (Fortran77, \m s -> fromParseResult . fortran77ParserWithModFiles m s)
  , (Fortran77Extended, \m s -> fromParseResult . extended77ParserWithModFiles m s)
  , (Fortran90, \m s -> fromParseResult . fortran90ParserWithModFiles m s) ]

after g f x = g . (f x)

-- | Deduce the type of parser from the filename and parse the
-- contents of the file.
fortranParser :: Parser
fortranParser contents filename = do
   let Just parserF = lookup (deduceVersion filename) parserVersions
   parserF contents filename

-- | Deduce the type of parser from the filename and parse the
-- contents of the file, within the context of given "mod files".
fortranParserWithModFiles :: ParserWithModFiles
fortranParserWithModFiles mods contents filename = do
   let Just parserF = lookup (deduceVersion filename) parserWithModFilesVersions
   parserF mods contents filename