module Main where

import Language.Atom
import Data.Word

-- Override some default values
cfg = AtomConfig {
    -- This pulls some stuff in we use below.
    cIncludes = Just ["blink.h"],

    -- The (u)intXX_t types are defined in
    -- stdint.h--which we include from blink.h
    cTyper    = Just $ \t -> case t of
                                Bool   -> "uint8_t"
                                Int8   -> "int8_t"
                                Int16  -> "int16_t"
                                Int32  -> "int32_t"
                                Int64  -> "int64_t"
                                Word8  -> "uint8_t"
                                Word16 -> "uint16_t"
                                Word32 -> "uint32_t"
                                Word64 -> "uint64_t"
                                Float  -> "float"
                                Double -> "double"
}

-- Main just has to compile the Atom expression
main :: IO ()
main = compile "blink_atom" (Just cfg) blink

-- How many cycles do we want to delay before 
-- we flip the LED?
delayCycles :: Word16
delayCycles = 30000

-- Simple Atom to toggle an LED
blink :: Atom ()
blink = do
    -- Is the LED currently on? (Assume it starts False/off)
    isOn    <- bool "isOn" False

    -- Does the toggle counter need a reset? (Assume it starts False/no)
    doReset <- bool "doReset" False

    -- Initialize the toggle counter to delayCycles
    toggle  <- word16 "toggle" delayCycles

    -- Decrements the toggle counter when it
    -- is greater than 0.
    period 1 $ atom "decrement" $ do
        cond $ value toggle >. 0
        toggle <== value toggle - 1

    -- Checks if we need to perform a toggle
    -- reset, and performs it when we need one.
    period 2 $ atom "reset" $ do
        cond $ value doReset
        doReset <== Const False
        toggle  <== Const delayCycles
    
    -- Checks if the toggle counter has expired.
    -- Toggles the LED if it has, then requests
    -- a reset.
    period 2 $ atom "flip" $ do
        cond $ value toggle <=. 0
        setLED isOn
        isOn <== (not_ $ value isOn)
        doReset <== Const True

-- An action (basically raw C code) to set the value
-- of the LED. setLED() is defined in blink.c.
setLED :: V Bool -> Atom ()
setLED v = action (\[x] -> "setLED(" ++ x ++ ")") [v']
    where v' = ue . value $ v
