{-# LANGUAGE TypeFamilies, ExistentialQuantification, FlexibleInstances, UndecidableInstances, FlexibleContexts,
    ScopedTypeVariables, MultiParamTypeClasses, FunctionalDependencies,ParallelListComp,
    RankNTypes, TypeOperators, NoMonomorphismRestriction  #-}

module Hardware.KansasLava.Text where

import Language.KansasLava as KL
import Data.Sized.Unsigned
import Data.Sized.Ix
import Data.Sized.Arith
import Data.Sized.Matrix as M
import Control.Applicative
import Data.Char
import qualified Data.Bits as B
import Data.Maybe as Maybe

-- | 'mm_text_driver' is a memory-mapped driver for a (small) display.
-- It gets passed the background "image", and the mapping from
-- active location number to row,col on the screen.
-- It outputs values sutable for input into the LCD mm drivers.
mm_text_driver :: forall c sig row col loc . 
	( Clock c, sig ~ Signal c
	, Rep loc, Rep row, Rep col
	, Size row, Size col
	, Rep (MUL row col)
	, Num (MUL row col)
	, Size (MUL row col)
	) 
	=> Matrix (row,col) U8		-- backscreen
	-> (loc -> (row,col))	-- active content mapping
	-> Patch (sig (Enabled (loc,U8)))	(sig (Enabled ((row,col),U8)))
		 (sig Ack)			(sig Ack)		
mm_text_driver m f = 
	mapP g $$
	prependP (matrix (M.toList m') :: Matrix (MUL row col) ((row,col),U8))
   where
	m' :: Matrix (row,col) ((row,col),U8)
	m' = forEach m $ \ addr ix -> (addr,ix)
	g :: forall comb . Signal comb (loc,U8) -> Signal comb ((row,col),U8)
	g arg = pack (funMap (return . f) addr,u8)
	   where (addr,u8) = unpack arg

{-
joinWrites :: (Clock clk, sig ~ Signal clk)
           => Patch (Matrix x (sig (Enabled (loc,U8)))) (sig (Enabled (loc,U8)))
                    (Matrix x (sig Ack))                (sig Ack)
joinWrites = undefined
-}
-- | Simple digit counter.
aliveGlyph :: forall c sig . (Clock c, sig ~ Signal c)
     => Patch (sig (Enabled ()))	(sig (Enabled (X1,U8)))
	      (sig Ack)			(sig Ack)
aliveGlyph 
      = openP $$
	fstP (cycleP (matrix $ map ordU8 ".oOo" :: Matrix X4 U8) $$
		  mapP (\ x -> pack (0,x))
		 ) $$
	zipP $$
	mapP (\ ab -> let (a,b) = unpack ab in a)


-- | In a scrollbar, what ever you write appears on the right hand side, pushing everything to the left.

scrollBar :: forall c sig x comb . (Clock c, sig ~ Signal c, Size x, Bounded x, Num x, Enum x, Rep x)
        => Patch (sig (Enabled U8))	(sig (Enabled (x,U8)))
	         (sig Ack)		(sig Ack)
scrollBar = 
        prependP (matrix [32] :: Matrix X1 U8) $$
        loopP patch             $$
        mapP wt_cmds            $$
        matrixToElementsP       
  where
     patch = 
        zipP $$ 
        mapP fn $$
        fifo1 $$
        dupP $$ 
        fstP (prependP (matrix [pure 32] :: Matrix X1 (Matrix x U8)))

     fn :: forall comb . Signal comb (Matrix x U8,U8) -> Signal comb (Matrix x U8)
     fn  ab = let (a:: Signal comb (Matrix x U8),b :: Signal comb U8) = unpack ab
                  a' = unpack a :: Matrix x (Signal comb U8)
              in pack $  matrix ([ a' ! x 
                                 | x <- [1..maxBound]
                                 ] ++ [b])

     wt_cmds :: forall comb . Signal comb (Matrix x U8) -> Signal comb (Matrix x (x,U8))
     wt_cmds = pack . (\ m -> forAll $ \ i -> pack (pureS i,m M.! i)) . unpack
     


-- show a hex number
hexForm :: forall c sig w .
 	( Clock c, sig ~ Signal c, Size (MUL X4 w), Integral (MUL X4 w)
	, Integral w, Bounded w, Rep w, Size w
	) =>
	Patch (sig (Enabled (Unsigned (MUL X4 w))))	(sig (Enabled (w,U8)))
      	      (sig Ack)					(sig Ack)
hexForm
    = matrixDupP $$
    matrixStackP (forAll $ \ i -> 
		mapP (\ v -> witnessS (Witness :: Witness U4) $ (unsigned) (v `B.shiftR` (fromIntegral (maxBound - i) * 4))) $$
		mapP (funMap (\ x -> if x >= 0 && x <= 9 
                     then return (0x30 + fromIntegral x)
                     else return (0x41 + fromIntegral x - 10))) $$
		mapP (\ ch -> pack (pureS i,ch))) $$
    matrixMergeP PriorityMerge


-- | ord for U8.
ordU8 :: Char -> U8
ordU8 = fromIntegral . ord

-- | chr for U8.
chrU8 :: U8 -> Char
chrU8 = chr . fromIntegral

-- | Turn a string into a 1D matrix
rowU8 :: (Size x) => String -> Matrix x U8
rowU8 = matrix . fmap ordU8

-- | Turn a string into a 2D matrix, ready for background.
boxU8 :: forall x row col . (Size x, Size row,Num row, Enum row, Size col, Num col, Enum col, x ~ MUL row col) 
      => [String] 
      -> Matrix x ((row,col),U8)
boxU8 inp = matrix
      [ ((row,col),ch)
      | (chs,row) <- zip (fmap (fmap ordU8) inp) [0..]
      , (ch,col)  <- zip chs [0..]
      ]

boxU8' :: forall row col . (Size row,Num row, Enum row, Size col, Num col, Enum col) 
      => [String] 
      -> Matrix (row,col) U8
boxU8' = matrix . concat . fmap (fmap ordU8)