{-# LANGUAGE TypeFamilies, ScopedTypeVariables, NoMonomorphismRestriction, Rank2Types, TemplateHaskell #-}

module Hardware.KansasLava.RS232 (rs232out, rs232in) where -- , liftWithUART) where

import Data.Ratio

import Data.Sized.Unsigned
import Data.Sized.Signed
import Data.Sized.Ix
import Data.Sized.Unsigned as U
import Data.Sized.Matrix as M

import Hardware.KansasLava.Rate
import Hardware.KansasLava.FIFO(fifo)

import Language.KansasLava 
import qualified Language.KansasLava as KL
import Data.Maybe as Maybe
import Data.Char as Char
import Control.Monad
import Data.Default
import Data.Word
import Debug.Trace


-- Lava implementation of RS232

type SAMPLE_RATE = X16

data RS232_TX
	= TX_Idle
	| TX_Send X10
	deriving (Show,Eq,Ord)

isTX_Idle :: (sig ~ Signal c) => sig RS232_TX -> sig Bool
isTX_Idle = funMap $ \ tx -> return $ tx == TX_Idle

withTX_Send :: (sig ~ Signal c) => sig RS232_TX -> sig (Enabled X10)
withTX_Send = funMap $ \ tx -> return $ case tx of
		TX_Send i -> Just i
		_         -> Nothing


instance BitRep RS232_TX where
    bitRep =
	[ (TX_Idle, 	0) ] ++
	[ (TX_Send v, 	fromIntegral $ fromIntegral v + 1) | v <- [0..9] ]


$(repBitRep ''RS232_TX 4)

{-
-- Template Haskell would help here.
fromRS232_TX :: RS232_TX -> X11
fromRS232_TX TX_Idle = 0
fromRS232_TX (TX_Send n) = fromIntegral n + 1

toRS232_TX :: X11 -> RS232_TX
toRS232_TX 0 = TX_Idle
toRS232_TX n = TX_Send (fromIntegral (n - 1))

instance Rep RS232_TX where
    data X RS232_TX    		= X_RS232_TX (Maybe RS232_TX)
    type W RS232_TX             = X4
    unX (X_RS232_TX v) 		= v
    optX b           		= X_RS232_TX b
    repType Witness     	= repType (Witness :: Witness X11)
    toRep (X_RS232_TX v)	= toRep (optX (fmap fromRS232_TX v))
    fromRep v			= X_RS232_TX (fmap toRS232_TX (unX (fromRep v)))
    showRep (X_RS232_TX v)	= show v
-}

(.*&.) :: (sig ~ Signal c, Rep a) => sig (Enabled a) -> sig Bool -> sig (Enabled a)
(.*&.) en_a bool = packEnabled (en .&&. bool) a
  where
	(en,a) = unpackEnabled en_a

resize :: (sig ~ Signal c, Integral x, Rep x, Num y, Rep y) => sig x -> sig y
resize = funMap $ \ x -> return (fromIntegral x)

findBit :: forall sig c . (sig ~ Signal c) => (Num (sig X10)) => sig U8 -> sig X10 -> sig Bool
findBit byte x = (bitwise) byte .!. ((unsigned) (loopingDecS x) :: sig X8)

rs232out :: forall clk sig a . (Clock clk, sig ~ Signal clk)
	=> Integer			-- ^ Baud Rate.
	-> Integer			-- ^ Clock rate, in Hz.
        -> Patch (sig (Enabled U8)) 	(sig Bool)
		 (sig Ack)		()
rs232out baudRate clkRate ~(inp0,()) = (toAck (ready .&&. in_en),out)
  where
	-- at the baud rate for transmission
	fastTick :: Signal clk Bool
    	fastTick = rate (Witness :: Witness X16) $
--    	        accurateTo
    	                (fromIntegral baudRate / fromIntegral clkRate)
--    	                0.99

    	(in_en,in_val) 	= unpack inp0

    	(ready,out) = runRTL $ do
--		readVal <- newArr (Witness :: Witness X10)
		state  <- newReg (TX_Idle       :: RS232_TX)
		char   <- newReg (0     	:: U8)
		output <- newReg (True		:: Bool)	-- RS232, SPACE => high

--		DEBUG "state" state

		let ready = isTX_Idle (reg state)

		CASE [ IF (ready .&&. in_en) $ do
			state := pureS (TX_Send 0)	-- causes full to be set on next clock
			char  := in_val
		     ]

		WHEN fastTick $ CASE
		     [ match (withTX_Send (reg state)) $ \ ix -> do
			CASE [ IF (ix .==. maxBound) $ do
				state  := pureS TX_Idle
			     , OTHERWISE $ do
				state := funMap (\ x -> if x == maxBound
							then return (TX_Send 0)
							else return (TX_Send (x + 1))) ix
			     ]
			CASE [ IF (ix .==. 0) $ do
				output := low	-- start bit
			     , IF (ix .==. 9) $ do
				output := high	-- stop bit
			     , OTHERWISE $ do
				output := findBit (reg char) ix
			     ]
		     ]

		-- We need to use 'var accept', because we need to accept the
		-- the on *this* cycle, not next cycle.
		return (ready,reg output)


-- | rs232in accepts data from UART line, and turns it into bytes.
--   There is no Ack or Ready, because there is no way to pause the 232.
--   For the same reason, this does not use a Patch.

rs232in :: forall clk sig a . (Clock clk, sig ~ Signal clk)
	=> Integer			-- ^ Baud Rate.
	-> Integer			-- ^ Clock rate, in Hz.
	-> Patch (sig Bool)  (sig (Enabled U8))
		 ()	     ()
rs232in baudRate clkRate ~(in_val0,()) = ((),out)
  where
	-- 16 times the baud rate for transmission,
	-- so we can spot the start bit's edge.
	fastTick :: Signal clk Bool
	fastTick = rate (Witness :: Witness X16) $
--                        accurateTo
                                (16 * fromIntegral baudRate / fromIntegral clkRate)
--                                0.99


        -- the filter, currently length 4
--        in_vals = in_val0 : map (register True) (take 4 in_vals)

	-- if 4 highs (lows) then go high (low), otherwise as you were.

        inp = in_val0
{-
        inp = register True
                        (cASE [ (foldr1 (.&&.) in_vals, high)
                              , (foldr1 (.&&.) (map bitNot in_vals), low)
                              ]
                         inp)
-}
	findByte :: [sig Bool] -> sig U8
	findByte xs = bitwise (pack (matrix xs :: M.Matrix X8 (sig Bool)) :: sig (M.Matrix X8 Bool))

	out = runRTL $ do
		reading <- newReg False
		theByte <- newArr (Witness :: Witness X16)
		outVal  <- newReg (Nothing :: Enabled U8)
		ready	<- newReg (False :: Bool)
		counter <- newReg (0 :: U8)

		let lowCounter, highCounter :: sig U4
		    (lowCounter,highCounter) = unappendS (reg counter)

		WHEN fastTick $ do
	 		CASE [ IF ((reg reading .==. low) .&&. (inp .==. low)) $ do
				counter := 0
				reading := high
                                        -- check to see the edge *is* an edge
--                             , IF ((reg counter .>. 0) .&&. (reg counter .<. 8) .&&. (inp .==. high)) $ do
--				counter := 0
--				reading := low
			     , OTHERWISE $ do
				counter := reg counter + 1
			     ]

			-- We have a 3 sample average, so we wait an aditional 5
			-- to be in the middle of the 16-times super-sample.
			-- So, 5 is 16 / 2 - 3
			WHEN ((reg reading .==. high) .&&. (lowCounter .==. 8)) $ CASE
			     [ IF (highCounter .<. 9) $ do
				theByte ((unsigned) highCounter) := inp
			     , IF ((highCounter .==. 9) .&&.
				   (reg (theByte 0) .==. low) .&&.
				   (inp .==. high)
				  ) $ do
				-- This should be the stop bit
				outVal := enabledS
					$ findByte [ reg (theByte (fromIntegral i))
						   | i <- [1..8] :: [Int]
						   ]
                                -- start looking for the start bit now
                                counter := 0
                                reading := low
			     , OTHERWISE $ do
				-- restart; should never happen with good signals
                                counter := 0
				reading := low
			     ]

		-- If you send something out, then do not do so on the next cycle.
		WHEN (isEnabled (reg outVal)) $ do
			outVal := pureS Nothing

		return $ (reg outVal)