{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE RecordWildCards #-}

module ADP.Fusion.Examples.Palindrome where

import Data.Vector.Fusion.Stream.Monadic (Stream (..))
import qualified Data.Vector.Fusion.Stream.Monadic as S
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector as V
import Data.Array.Repa.Index
import Control.Monad
import Control.Monad.Trans.Class
import qualified Control.Monad.Trans.Writer.Lazy as W
import qualified Control.Arrow as A

import Data.PrimitiveArray as PA
import Data.PrimitiveArray.Zero as PA

import ADP.Fusion hiding (empty)
import ADP.Fusion.Empty hiding (empty)
import ADP.Fusion.Chr
import ADP.Fusion.Table
import Data.Array.Repa.Index.Subword
import ADP.Fusion.TH



data SignatureT m x r = Signature
  { pair  :: Char -> x -> Char -> x
  , empty :: () -> x
  , h     :: Stream m x -> m r
  }

-- makeAlgebraProduct ''SignatureT

{-

-- gPalindrome :: Signature m x -> Empty -> Char -> tbl -> (tbl, Subword -> m x)

gPalindrome Signature{..} e c s =
  ( s, (  empty <<< e         |||
          pair  <<< c % s % c ... h
       )
  )
{-# INLINE gPalindrome #-}



aPair :: Monad m => Signature m Int Int
aPair = Signature
  { pair = \l x r -> if l==r then (x+4983) else -999999
  , empty = \() -> 4711
  , h = S.foldl' max (-888888)
  }
{-# INLINE aPair #-}

aPretty :: Monad m => Signature m String (Stream m String)
aPretty = Signature
  { pair = \l x r -> "(" ++ x ++ ")"
  , empty = \() -> ""
  , h = return . id
  }

(<**) :: (Monad m, CombElem x' x) => Signature m x x' -> Signature m y y' -> Signature m (x,Stream m y) y'
(<**) x y = Signature
  { pair = \l (zx,zy) r -> (pair x l zx r, S.map (\z -> pair y l z r) zy)
  , empty = \() -> (empty x (), S.singleton $ empty y ())
  , h = \zs -> do hfst <- h x $ S.map fst zs
                  h y $ S.concatMap snd . S.filter (combElem hfst . fst) $ zs
  }
{-# INLINE (<**) #-}

(***) :: (Monad m) => Signature m x x' -> Signature m y y' -> Signature m (x,y) (x',y')
(***) x y = Signature
  { pair = \l (zx,zy) r -> (pair x l zx r, pair y l zy r)
  , empty = \() -> (empty x (), empty y ())
--  , h = \zs -> do hfst <- h x $ S.map fst zs
--                  let phfs = S.concatMap snd . S.filter (combElem hfst . fst) $ zs
--                  hsnd <- h y phfs
  }
{-# INLINE (***) #-}

class CombElem x y where
  combElem :: x -> y -> Bool

instance (Eq x) => CombElem x x where
  combElem = (==)

instance (VU.Unbox x, Eq x) => CombElem (VU.Vector x) x where
  combElem xs y = VU.elem y xs

instance (Eq x) => CombElem (V.Vector x) x where
  combElem xs y = V.elem y xs

palindromeFill :: VU.Vector Char -> IO (PA.Unboxed (Z:.Subword) Int)
palindromeFill inp = do
  let n = VU.length inp
  !t' <- newWithM (Z:.subword 0 0) (Z:.subword 0 n) 0
  let t= mTblSw EmptyT t'
  let b = chr inp
  let e = Empty
  fillTable $ gPalindrome aPair e b t
  freeze t'
{-# NOINLINE palindromeFill #-}

fillTable (MTbl _ tbl, f) = do
  let (_,Z:.Subword (0:.n)) = boundsM tbl
  forM_ [n,n-1..0] $ \i -> forM_ [i..n] $ \j -> do
    (f $ subword i j) >>= writeM tbl (Z:.subword i j)
{-# INLINE fillTable #-}

-}