{-|
Module      : Text.Ogmarkup
Copyright   : (c) Ogma Project, 2016
License     : MIT
Stability   : experimental

The ogmarkup library provides an ogmarkup document compiler. This module is the
only one you should need to import in your project.

The library is still in an early stage of development, hence the "experimental"
stability. Be aware the exposed interface may change in future realase.
-}

module Text.Ogmarkup
    (
      -- * Parse and Generate
      Strategy (..),
      ogmarkup,

      -- * Generation Configuration
      Conf.GenConf (..),
      Conf.Template,

      -- * Typography
      Typo.frenchTypo,
      Typo.englishTypo,
      Typo.Typography (..),

      -- * Punctuation marks and space
      Ast.Mark (..),
      Typo.Space (..),
    ) where

import           Data.String
import           Data.List
import           Data.Monoid

import qualified Text.Ogmarkup.Private.Config     as Conf
import qualified Text.Ogmarkup.Private.Ast        as Ast
import qualified Text.Ogmarkup.Private.Generator  as Gen
import qualified Text.Ogmarkup.Private.Parser     as Parser
import qualified Text.Ogmarkup.Private.Typography as Typo

-- | With the best-effort compilation feature of ogmarkup, when the parser
--   encounters an error, it can apply two different strategies with the
--   remaining input to find a new synchronization point. It can search
--   character by character or line by line.
data Strategy =
    ByLine
  | ByChar

-- | From a String, parse and generate an output according to a generation configuration.
--   The inner definitions of the parser and the generator imply that the output
--   type has to be an instance of the 'IsString' and 'Monoid' classes.
ogmarkup :: (IsString a, Monoid a, Conf.GenConf c a)
         => Strategy       -- ^ Best-effort compilation strategy
         -> String         -- ^ The input string
         -> c              -- ^ The generator configuration
         -> a
ogmarkup be input conf = Gen.runGenerator (Gen.document (_ogmarkup be "" input [])) conf
  where
    _ogmarkup :: (IsString a, Monoid a)
              => Strategy -- best-effort compilation strategy
              -> String   -- acc
              -> String   -- input
              -> Ast.Document a
              -> Ast.Document a

    _ogmarkup _ "" "" ast = ast

    _ogmarkup _ acc "" ast = ast `mappend` [Ast.Failing . fromString $ acc]

    _ogmarkup be acc input ast =
      case Parser.parse Parser.document "" input of
        Right (ast', input') ->
          if input == input'  -- nothing has been parsed
          then let (c, rst) = applyStrat be input
               in _ogmarkup be (acc `mappend` c) rst ast
          else if acc == ""
               then _ogmarkup be [] input' (ast `mappend` ast')
               else let f = Ast.Failing . fromString $ acc
                    in _ogmarkup be [] input' (ast `mappend` (f:ast'))
        Left err -> error $ show err

    applyStrat :: Strategy
               -> String
               -> (String, String)

    applyStrat _ [] = ([], [])
    applyStrat ByLine input = break (== '\n') input
    applyStrat ByChar (c:rst) = ([c], rst)