{- | formerly /Format/

    substitutes placeholder values with values from args
    
    in lieu of /format/ function there are 2 operators: '<' and '>': no need to remember arg order.  

    ==== placeholder syntax:

       * index based

            { Int }

       * key based

            { 'String' | 'ByteString' | 'Text' }

    other placeholders may be implemented with

    "Text.Regex.Do.Replace.Latin" or "Text.Regex.Do.Replace.Utf8"       
    
    to avoid clash with 'Prelude':
    
    @import Prelude hiding((\<),(\>))@       

    or qualify '<' with alias e.g. (assuming this module is imported with F alias):
       
    @F.<@       -}

module Text.Regex.Do.Replace.Template
    (Template(..),
    ReplaceOne(),
    Formatable) where

import Prelude as P hiding ((<),(>))
import Text.Regex.Do.Replace.Fast as S (replace)
import Text.Regex.Do.Type.Convert
import Data.ByteString as B
import Data.Text as T


type Formatable a = (Template a [a], Template a [(a,a)])

{- | ==== implemented a:

    * 'String'
    * 'ByteString'
    * 'Text'      
    
    a: template e.g. "today is {0}"
    
    repl: replacement: [a] or [(a,a)]
    -}
class Template a repl where
   (<)::a -> repl -> a
   (>)::repl -> a -> a
   (>) repl0 template0 = template0 < repl0
   

instance ReplaceOne Int a =>
    Template a [a] where
   (<) t0 a0 = foldr_idx foldFn_idx t0 a0
{- ^ === index based
    >>> ["цветы", "мороженое"] > "даме {0}, детям {1}" 
    
    "даме цветы, детям мороженое"

     >>> "Polly {0} a {1}" < ["wants","cracker"]

     "Polly wants a cracker"
     
     >>> ["перловка"] > "на первое {0}, на второе {0}"
    
     "на первое перловка, на второе перловка"
-}

foldFn_idx::ReplaceOne k v =>
    v -> (k, v) -> v
foldFn_idx v0 (i0, body1) = replaceOne body1 i0 v0



instance ReplaceOne a a =>
    Template a [(a, a)] where
   (<) = P.foldr foldFn_map
{- ^=== key based
     key may be {any a}

     >>> "овчинка {a} не {b}" < [("a","выделки"),("b","стоит")]

     "овчинка выделки не стоит"
-}



foldFn_map::ReplaceOne k v =>
    (k, v) -> v -> v
foldFn_map (k0, v0) body1 = replaceOne body1 k0 v0



--  fold with index
type CustomerFn a b = (a -> (Int,b) -> b)

foldr_idx::CustomerFn a b -> b -> [a] -> b
foldr_idx fn0 init1 list0 = b1
   where i0 = P.length list0 - 1
         (-1,b1) = P.foldr (foldFn fn0) (i0,init1) list0


foldFn::CustomerFn a b -> a -> (Int, b) -> (Int, b)
foldFn fn0 val0 t0@(i0, _) = (i0 - 1, b1)
   where b1 = fn0 val0 t0


class ReplaceOne idx a where
    replaceOne::a -> idx -> a -> a


instance ReplaceOne Int String where
    replaceOne body0 k0 v0 = toString bs1
       where pat1 = toByteString $ "{" ++ (show k0) ++ "}"
             repl1 = toByteString v0
             bs1 = S.replace pat1 repl1 $ toByteString body0


instance ReplaceOne String String where
    replaceOne body0 k0 v0 = toString bs1
       where pat1 = toByteString $ "{" ++ k0 ++ "}"
             repl1 = toByteString v0
             bs1 = S.replace pat1 repl1 $ toByteString body0


instance ReplaceOne Int ByteString where
    replaceOne body0 k0 v0 = S.replace pat1 repl1 body0
       where pat1 =  toByteString $ "{" ++ (show k0) ++ "}"
             repl1 = v0


instance ReplaceOne ByteString ByteString where
    replaceOne body0 k0 v0 = S.replace pat1 repl1 body0
       where pat1 = B.concat [toByteString "{", k0, toByteString "}"]
             repl1 = v0



instance ReplaceOne Int Text where
    replaceOne body0 k0 v0 = T.replace pat1 v0 body0
       where pat1 = T.pack $ "{" ++ (show k0) ++ "}"


instance ReplaceOne Text Text where
    replaceOne body0 k0 v0 = T.replace pat1 v0 body0
       where pat1 = T.concat [T.pack "{", k0, T.pack "}"]