--------------------------------------------------------------------------------
-- |
--  Module      :  Client
--  License     :  Public Domain
--
--  Maintainer  :  Douglas Burke
--  Stability   :  experimental
--  Portability :  Haskell 98
--
-- The main interface for connecting to the Raspberry-PI version
-- of MineCraft. The "Network.MineCraft.Pi.Client.Internal" module
-- provides lower-level access in case this module in insufficient.
--
-- There are two types of calls to MineCraft: \"command\" and \"query\".
-- Commands change the state of the server and do not return anything,
-- queries return information from the server, and presumably does not
-- change the server state. This terminology may change. At present
-- there is no check that the call succeeded.
--
-- Example:
--
-- In this example we move the player 10 tiles in the X direction,
-- as long as the tile is empty (there is /no/ check that there
-- is anything to stand on).
--
-- > {-# LANGUAGE RecordWildCards #-}
-- >
-- > module Main where
-- >
-- > import Control.Monad (when)
-- >
-- > import Data.MineCraft.Pi.Block 
-- > import Data.MineCraft.Pi.Player
-- > import Data.MineCraft.Pi.Other
-- > import Data.MineCraft.Pi.Types
-- >
-- > import Network.MineCraft.Pi.Client
-- >
-- > -- | Move the player by 10 tiles in the X direction,
-- > --   if it is not filled.
-- > movePlayer :: MCPI ()
-- > movePlayer = do
-- >     Pos {..} <- getPlayerTile
-- >     let newPos = Pos (_x+10) _y _z
-- >     bType <- getBlock newPos
-- >     when (bType == air) $ do
-- >       setPlayerTile newPos
-- >       chatPost "*jump*"
-- >
-- > main :: IO ()
-- > main = runMCPI movePlayer
--
--------------------------------------------------------------------------------

module Network.MineCraft.Pi.Client
    ( MCPI
    , runMCPI

    -- * Conversion routines
    --
    -- | I am not sure the use of the `FromMineCraft` and `ToMineCraft`
    --   type classes is justified, given that the API has a very limited
    --   set of types.
    --
    , FromMineCraft(..)
    , ToMineCraft(..)

    -- * Utility routine
    , eRead
    ) where

import Data.List (intercalate)
import Data.List.Split (splitOn)
import Data.Maybe (fromMaybe, listToMaybe)

import Network.MineCraft.Pi.Client.Internal

-- | Convert the return value from MineCraft into
--   a Haskell type.
class FromMineCraft a where
  fromMC :: String -> a

-- | Convert a value into a form that can be sent to MineCraft.
class ToMineCraft a where
  toMC :: a -> String

-- Isn't this in base somewhere by now?
maybeRead :: Read a => String -> Maybe a
maybeRead = fmap fst . listToMaybe . reads

-- | Convert a value, but error out if it can not be
--   converted.
eRead :: (Read a) => String -> a
eRead s = fromMaybe (error ("*Conversion error* " ++ s)) $ maybeRead s

instance FromMineCraft Int where
  fromMC = eRead 

instance ToMineCraft Int where
  toMC = show

instance FromMineCraft Float where
  fromMC = eRead

instance ToMineCraft Float where
  toMC = show

instance FromMineCraft a => FromMineCraft [a] where
  fromMC = map fromMC . splitOn ","

instance ToMineCraft a => ToMineCraft [a] where
  toMC = intercalate "," . map toMC

instance (FromMineCraft a, FromMineCraft b) => FromMineCraft (a, b) where
  fromMC s = case splitOn "," s of
                 (a:b:[]) -> (fromMC a, fromMC b)
                 _ -> error $ "*Expected 2 values* " ++ s

instance (ToMineCraft a, ToMineCraft b) => ToMineCraft (a, b) where
  toMC (a,b) = toMC a ++ "," ++ toMC b