{-#LANGUAGE TemplateHaskell#-}
{-# OPTIONS_GHC -fno-warn-missing-fields #-}
-- | Contains things needed by BNFC-meta language definitions and
-- by the code generated from those. Typical users don't need to browse this 
-- module.
module Language.LBNF.Compiletime(
  -- * Happy and Alex 
  HappyStk(..)
  , utf8Encode
  , Posn(Pn)
  , AlexInput
  , alexGetByte
  , ord
  , listArray
  , (!)
  , Array  

  -- * Pretty printing 
  , printTree
  , doc
  , concatD
  , Print(..)
  , prPrec
  , PrintPlain(..)

  -- * Quasi quoting 
  , parseToQuoter, parseToMonQuoter
  , ParseMonad(..)
  , errq
  , Q
  , BNFC_QQType(..), appEPAll, appEPAllL, fromString, fromLit, fromToken, fromPositionToken
  , Lift (..)
  , LocType
  , Literal(..)
  , IsChar(..)
  -- ** Helper functions for defining Anti-quotation
  , printAq
  , stringAq
  
  ) where

import Language.LBNF.Runtime
import Text.Happy.Quote(HappyStk(..))
import Data.Array(listArray, (!), Array)
import Data.Char
import qualified Data.Bits
import Data.Word(Word8)

import Language.Haskell.TH
import Language.Haskell.TH.Quote

import Control.Monad ((>=>),liftM)
import Language.Haskell.TH.Syntax
import Language.Haskell.Meta.Parse

data Posn = Pn !Int !Int !Int
      deriving (Eq, Show,Ord)

type AlexInput = (Posn,     -- current position,
                  Char,         -- previous char
                  [Word8],       -- pending bytes on current char
                  String)       -- current input string

alexMove :: Posn -> Char -> Posn
alexMove (Pn a l c) '\t' = Pn (a+1)  l     (((c+7) `div` 8)*8+1)
alexMove (Pn a l c) '\n' = Pn (a+1) (l+1)   1
alexMove (Pn a l c) _    = Pn (a+1)  l     (c+1)

alexGetByte :: AlexInput -> Maybe (Word8,AlexInput)
alexGetByte (p,c,(b:bs),s) = Just (b,(p,c,bs,s))
alexGetByte (p,c,[],[]) = Nothing
alexGetByte (p,_,[],(c:s))  = let p' = alexMove p c 
                                  (b:bs) = utf8Encode c
                              in p' `seq`  Just (b, (p', c, bs, s))

utf8Encode :: Char -> [Word8]
utf8Encode = map fromIntegral . go . ord
 where
  go oc
   | oc <= 0x7f       = [oc]

   | oc <= 0x7ff      = [ 0xc0 + (oc `Data.Bits.shiftR` 6)
                        , 0x80 + oc Data.Bits..&. 0x3f
                        ]

   | oc <= 0xffff     = [ 0xe0 + (oc `Data.Bits.shiftR` 12)
                        , 0x80 + ((oc `Data.Bits.shiftR` 6) Data.Bits..&. 0x3f)
                        , 0x80 + oc Data.Bits..&. 0x3f
                        ]
   | otherwise        = [ 0xf0 + (oc `Data.Bits.shiftR` 18)
                        , 0x80 + ((oc `Data.Bits.shiftR` 12) Data.Bits..&. 0x3f)
                        , 0x80 + ((oc `Data.Bits.shiftR` 6) Data.Bits..&. 0x3f)
                        , 0x80 + oc Data.Bits..&. 0x3f
                        ]



-- import qualified Language.Haskell.Exts.Parser as Hs


data BNFC_QQType = 
  QQApp (String,LocType) [BNFC_QQType] | 
  QQAq (Q Exp, Q Pat) |
  QQList [BNFC_QQType] |
  QQLit Lit |
  QQPosT (Int,Int) (String,LocType) String


type LocType = (String,String)


errq :: (String -> a) -> ParseMonad a -> Q a
errq e = return . err e



-- appEAll :: [TH_Exp] -> TH_Exp
appEPAll :: LocType -> String -> [BNFC_QQType] -> BNFC_QQType
appEPAll loc s l = QQApp (s,loc) l


appEPAllL :: LocType -> [BNFC_QQType] -> BNFC_QQType
appEPAllL loc l = QQList l


class Literal a where
  lit :: a -> Lit
  
instance Literal Double where
  lit = RationalL . toRational

instance Literal Integer where
  lit = IntegerL

instance Literal Char where
  lit = CharL

class IsChar a where
  toChar :: a -> Char
instance IsChar Char where
  toChar = id

instance IsChar a => Literal [a] where
  lit = StringL . map toChar

fromLit :: Literal a => LocType -> a -> BNFC_QQType
fromLit l a = QQLit $ lit a


fromString l s = fromLit l s -- (litE $ StringL s,litP $ StringL s)


fromToken l t s = QQApp (t,l) [QQLit $ lit s]
--    (
--    appE (mkGName l t >>= conE)(litE $ StringL s), 
--    mkGName l t >>= flip conP [litP $ StringL s]
--    )

fromPositionToken :: LocType -> String -> ((Int,Int),String) -> BNFC_QQType
fromPositionToken l t v@(pos,s) = QQPosT pos (t,l) s


qualify "" f     = f
qualify _ f@"[]" = f
qualify _ f@":"  = f
qualify m  f     = m ++ "." ++ f

-- Dynamic names
-- mkGName :: LocType -> String -> Q Name
-- mkGName (p,m) s = return $ mkName $ qualify m s 

-- Static names
mkGName (p,m) ":" = return $ mkName ":"
mkGName (p,m) "[]" = return $ mkName "[]"
mkGName (p,m) n = return $ Name (mkOccName n) $ 
    NameG DataName (mkPkgName $ p) (mkModName $ m)



parseToQuoter :: (String -> ParseMonad BNFC_QQType) -> QuasiQuoter
parseToQuoter p = QuasiQuoter {
  quoteExp = handle . p >=> toQExp,
  quotePat = handle . p >=> toQPat
  }

parseToMonQuoter :: (String -> ParseMonad BNFC_QQType) -> QuasiQuoter
parseToMonQuoter p = QuasiQuoter {
  quoteExp = handle . p >=> toQMExp,
  quotePat = handle . p >=> toQPat
  }

 -- {quoteExp = fst . handle . p, quotePat = snd . handle . p}

toQExp :: BNFC_QQType -> Q Exp
toQExp qq = case qq of
  QQApp (s,l) qs         -> do 
    const <- mkGName l s 
    foldl appE (conE const) (map toQExp qs)
  QQAq p                 -> fst p
  QQList qs              -> mapM toQExp qs >>= \qs' -> case qs' of
    [ListE es, e]          -> listE (map return $ es ++ [e])
    [ConE _,e]             -> listE $ [return e]
    a                      -> listE $ map return a
  QQLit l                -> litE l
  QQPosT pos (t,l) s     -> do
    constr <- mkGName l t 
    appE (conE constr) (lift (pos,s))

toQMExp :: BNFC_QQType -> Q Exp
toQMExp qq = case qq of
  QQApp (s,l) qs         -> do 
    const <- mkGName l s 
    foldl mAppE (returnE $ conE const) (map toQMExp qs)
  QQAq p                 -> fst p
  QQList qs              -> mapM toQMExp qs >>= \qs' -> case qs' of
    [ListE es, e]          -> sequenceE $ listE (map return $ es ++ [e])
    [ConE _,e]             -> sequenceE $ listE $ [return e]
    a                      -> sequenceE $ listE $ map return a
  QQLit l                -> returnE $ litE l
  QQPosT pos (t,l) s     -> do
    constr <- mkGName l t 
    returnE $ appE (conE constr) (lift (pos,s))

returnE = appE (varE 'return)
sequenceE = appE (varE 'sequence)

mAppE :: Q Exp -> Q Exp -> Q Exp
mAppE mf ma = [| $mf >>= flip liftM $ma |]




toQPat :: BNFC_QQType -> Q Pat
toQPat qq = case qq of
  QQApp (s,l) qs         -> do 
    const <- mkGName l s 
    conP const (map toQPat qs)
  QQAq p                 -> snd p
  QQList qs              -> mapM toQPat qs >>= \qs' -> case qs' of
    [p,ListP ps]           -> listP $ map return $ p : ps
    [x]                    -> listP [return x] 
  QQLit l                -> litP l
  QQPosT (p1,p2) (t,l) s -> mkGName l t >>= flip conP 
      [tupP [
        tupP [litP $ IntegerL $ toInteger p1, litP $ IntegerL $ toInteger p2],
        litP (lit s)
        ]]



printAq :: Print a => a -> BNFC_QQType
printAq a = stringAq $ printTree a

stringAq :: String -> BNFC_QQType
stringAq s = QQAq (
  either error return . parseExp $ s, 
  either error return . parsePat $ s)


handle :: ParseMonad BNFC_QQType -> Q BNFC_QQType
handle (Bad s) = fail s
handle (Ok a)  = return a