    {-# LANGUAGE TemplateHaskell, QuasiQuotes, ParallelListComp #-}

    -- | Template
    module Data.Array.Repa.Stencil.Template
    	(stencil2)
    where
    import Data.Array.Repa.Index
    import Language.Haskell.TH
    import Language.Haskell.TH.Quote
    import qualified Data.List	as List

    -- | QuasiQuoter for producing a static stencil defintion.
    --
    --   A definition like
    --
    --   @
    --     [stencil2|  0 1 0
    --                 1 0 1
    --                 0 1 0 |]
    --   @
    --
    --   Is converted to:
    --
    --   @
    --     makeStencil2 (Z:.3:.3)
    --        (\\ix -> case ix of
    --                  Z :. -1 :.  0  -> Just 1
    --                  Z :.  0 :. -1  -> Just 1
    --                  Z :.  0 :.  1  -> Just 1
    --                  Z :.  1 :.  0  -> Just 1
    --                  _              -> Nothing)
    --   @
    --
    stencil2 :: QuasiQuoter
    stencil2 = QuasiQuoter
    		{ quoteExp	= parseStencil2
    		, quotePat	= undefined
    		, quoteType	= undefined
    		, quoteDec	= undefined }

    -- | Parse a stencil definition.
    --   TODO: make this more robust.
    parseStencil2 :: String -> Q Exp
    parseStencil2 str
     = let
    	-- Determine the extent of the stencil based on the layout.
    	-- TODO: make this more robust. In particular, handle blank
    	--       lines at the start of the definition.
    	line1 : _	= lines str
    	sizeX		= fromIntegral $ length $ lines str
    	sizeY		= fromIntegral $ length $ words line1

    	-- TODO: this probably doesn't work for stencils who's extents are even.
    	minX		= negate (sizeX `div` 2)
    	minY		= negate (sizeY `div` 2)
    	maxX		= sizeX `div` 2
    	maxY		= sizeY `div` 2

    	-- List of coefficients for the stencil.
    	coeffs		= (List.map read $ words str) :: [Integer]

       in	makeStencil2' sizeX sizeY
    	 $ filter (\(_, _, v) -> v /= 0)
    	 $ [ (fromIntegral y, fromIntegral x, fromIntegral v)
    		| y	<- [minX, minX + 1 .. maxX]
    		, x	<- [minY, minY + 1 .. maxY]
    		| v	<- coeffs ]

    makeStencil2'
    	:: Integer -> Integer
    	-> [(Integer, Integer, Integer)]
    	-> Q Exp

    makeStencil2' sizeX sizeY coeffs
     = do	ix'		<- newName "ix"
    	z'		<- [p| Z |]
    	coeffs'		<- newName "coeffs"

    	let fnCoeffs
    		= LamE  [VarP ix']
    	 	$ CaseE (VarE (mkName "ix"))
    	 	$   [ Match	(InfixP (InfixP z' (mkName ":.") (LitP (IntegerL oy)))
                                            (mkName ":.") (LitP (IntegerL ox)))
    				(NormalB $ ConE (mkName "Just") `AppE` LitE (IntegerL v))
    				[] | (oy, ox, v) <- coeffs ]
    	  	    ++ [Match WildP
    				(NormalB $ ConE (mkName "Nothing")) []]

    	return
    	 $ AppE (VarE (mkName "makeStencil2")
                            `AppE` (LitE (IntegerL sizeX))
                            `AppE` (LitE (IntegerL sizeY)))
             $ LetE [ PragmaD (InlineP (mkName "coeffs") Inline FunLike (BeforePhase 0))
    		, ValD 	  (VarP    coeffs')          (NormalB fnCoeffs) [] ]
    		(VarE (mkName "coeffs"))