module Data.Semiring.TH where

import Control.Monad
import Language.Haskell.TH

typeNames :: Int -> Q [Name]
typeNames = traverse (pure . mkName) . map pure . flip take ['a'..]

varNames :: Int -> Q [Name]
varNames n =
    (traverse newName . map pure . reverse . take n . reverse . take 26)
        ['a' ..]

repN :: Int -> String -> Q Dec
repN n nm = do
    let v = VarP (mkName nm)
    rhs <- TupE <$> replicateM n (pure (VarE (mkName nm)))
    return $ ValD v (NormalB rhs) []

appN :: Int -> String -> Q Dec
appN n nm = do
    let f = VarE (mkName nm)
    xs <- varNames n
    let args = [TupP (map VarP xs)]
        ntup = TupE (map (AppE f . VarE) xs)
    return $ FunD (mkName nm) [Clause args (NormalB ntup) []]

cmbN :: Int -> String -> Q Dec
cmbN n nm = do
    let f = VarE (mkName nm)
    xs <- varNames n
    ys <- varNames n
    let args = [TupP (map VarP xs), TupP (map VarP ys)]
        ntup = TupE (zipWith (AppE . AppE f) (map VarE xs) (map VarE ys))
    return $ FunD (mkName nm) [Clause args (NormalB ntup) []]

starIns :: Int -> Q Dec
starIns n = do
    names <- typeNames n
    let c = ConT (mkName "StarSemiring")
        ct = map (AppT c . VarT) names
    InstanceD Nothing ct (AppT c $ foldl AppT (TupleT n) (map VarT names)) <$>
        sequence [appN n "star", pure (inline "star"), appN n "plus", pure (inline "plus")]

inline :: String -> Dec
inline n = PragmaD (InlineP (mkName n) Inline FunLike AllPhases)

semiringIns :: Int -> Q Dec
semiringIns n = do
    names <- typeNames n
    let c = ConT (mkName "Semiring")
        ct = map (AppT c . VarT) names
    InstanceD Nothing ct (AppT c $ foldl AppT (TupleT n) (map VarT names)) <$>
        sequence
            [ cmbN n "<+>"
            , pure (inline "<+>")
            , cmbN n "<.>"
            , pure (inline "<.>")
            , repN n "zero"
            , pure (inline "zero")
            , repN n "one"
            , pure (inline "one")]

zeroIns :: Int -> Q Dec
zeroIns n = do
    names <- typeNames n
    let c = ConT (mkName "DetectableZero")
        ct = map (AppT c . VarT) names
    InstanceD Nothing ct (AppT c $ foldl AppT (TupleT n) (map VarT names)) <$>
      sequence [andAll n, pure (inline "isZero")]

andAll :: Int -> Q Dec
andAll n = do
  let f = VarE (mkName "&&")
  let isZ = VarE (mkName "isZero")
  xs <- replicateM n (newName "x")
  let args = [TupP (map VarP xs)]
      res = foldl1 (\a e -> AppE (AppE f a) e ) (map (AppE isZ . VarE) xs)
  return $ FunD (mkName "isZero") [Clause args (NormalB res) []]