content: -----------------------------------------------------------------------------
  -- |
  -- Module      :  System.Hardware.Arduino.SamplePrograms.NumGuess
  -- Copyright   :  (c) Levent Erkok
  -- License     :  BSD3
  -- Maintainer  :  erkokl@gmail.com
  -- Stability   :  experimental
  --
  -- Simple number guessing game on the OSEPP Keyboard shield.
  --
  -- /Thanks to David Palmer for lending me his OSEPP shield to play with!/
  -------------------------------------------------------------------------------
  
  module System.Hardware.Arduino.SamplePrograms.NumGuess where
  
  import System.Hardware.Arduino
  import System.Hardware.Arduino.Parts.LCD
  
  -- | The OSepp LCD Shield is a 16x2 LCD using a Hitachi Controller
  -- Furthermore, it has backlight, and 5 buttons. The hook-up is
  -- quite straightforward, using our existing Hitachi44780 controller
  -- as an example. More information on this shield can be found at:
  --
  --     <http://osepp.com/products/shield-arduino-compatible/16x2-lcd-display-keypad-shield/>
  osepp :: LCDController
  osepp = Hitachi44780 { lcdRS = digital 8
                       , lcdEN = digital 9
                       , lcdD4 = digital 4
                       , lcdD5 = digital 5
                       , lcdD6 = digital 6
                       , lcdD7 = digital 7
                       , lcdRows = 2
                       , lcdCols = 16
                       , dotMode5x10 = False
                       }
  
  -- | There are 5 keys on the OSepp shield.
  data Key = KeyRight
           | KeyLeft
           | KeyUp
           | KeyDown
           | KeySelect
  
  -- | Initialize the shield. This is essentially simply registering the
  -- lcd with the HArduino library. In addition, we return two values to
  -- the user:
  --
  --   * A function to control the back-light
  --
  --   * A function to read (if any) key-pressed
  initOSepp :: Arduino (LCD, Bool -> Arduino (), Arduino (Maybe Key))
  initOSepp = do lcd <- lcdRegister osepp
                 let button = analog 0
                     light  = digital 10
                 setPinMode button ANALOG
                 setPinMode light  OUTPUT
                 -- Analog values obtained from OSEPP site, seems reliable
                 let threshHolds = [ (KeyRight,   30)
                                   , (KeyUp,     150)
                                   , (KeyDown,   360)
                                   , (KeyLeft,   535)
                                   , (KeySelect, 760)
                                   ]
                     backLight  = digitalWrite light
                     readButton = do val <- analogRead button
                                     let walk []            = Nothing
                                         walk ((k, t):keys)
                                           | val < t        = Just k
                                           | True           = walk keys
                                     return $ walk threshHolds
                 return (lcd, backLight, readButton)
  
  -- | Number guessing game, as a simple LCD demo. User thinks of a number
  -- between @0@ and @1000@, and the Arduino guesses it.
  numGuess :: LCD -> (Bool -> Arduino ()) -> Arduino (Maybe Key) -> Arduino ()
  numGuess lcd light readKey = game
    where home  = lcdHome      lcd
          write = lcdWrite     lcd
          clear = lcdClear     lcd
          go    = lcdSetCursor lcd
          at (r, c) s = go (c, r) >> write s
          getKey = do mbK <- readKey
                      case mbK of
                        Nothing -> getKey
                        Just k  -> do delay 500 -- stabilize by waiting 0.5s
                                      return k
          game = do clear
                    home
                    light True
                    at (0, 4) "HArduino!"
                    at (1, 0) "# Guessing game"
                    delay 2000
                    guess 1 0 1000
          newGame = getKey >> game
          guess :: Int -> Int -> Int -> Arduino ()
          guess rnd l h
            | h == l = do clear
                          at (0, 0) $ "It must be: " ++ show h
                          at (1, 0) $ "Guess no: " ++ show rnd
                          newGame
            | h < l = do clear
                         at (0, 0) "You lied!"
                         newGame
            | True  = do clear
                         let g = (l+h) `div` 2
                         at (0, 0) $ "(" ++ show rnd ++ ") Is it " ++ show g ++ "?"
                         k <- getKey
                         case k of
                           KeyUp     -> guess (rnd+1) (g+1) h
                           KeyDown   -> guess (rnd+1) l (g-1)
                           KeySelect -> do at (1, 0) $ "Got it in " ++ show rnd ++ "!"
                                           newGame
                           _         -> do at (1, 0) "Use up/down/select only.."
                                           delay 1000
                                           guess rnd l h
  
  -- | Entry to the classing number guessing game. Simply initialize the
  -- shield and call our game function.
  guessGame :: IO ()
  guessGame = withArduino False "/dev/cu.usbmodemFD131" $ do
                   (lcd, light, readButton) <- initOSepp
                   numGuess lcd light readButton
  