--------------------------------------------------------------------------------
-- |
--  Module      :  Types
--  License     :  Public Domain
--
--  Maintainer  :  Douglas Burke
--  Stability   :  experimental
--  Portability :  Haskell 98
--
-- Useful data types. The block types should probably move to
-- "Data.MineCraft.Pi.Block".
--
--------------------------------------------------------------------------------

module Data.MineCraft.Pi.Types 
   (
     Pos(..)
   , IPos
   , FPos

   , BlockType(..)
   , BlockData(..)

   ) where

import Data.List (intercalate)
import Data.List.Split (splitOn)
import Data.Word (Word16)

import Network.MineCraft.Pi.Client

-- | Represent the position of an entity. Note that we use
--   @(X,Y,Z)@ order rather than @(X,Z,Y)@ used by the MineCraft API.
--
--   This *should* be replaced by one of the \"standard\" Haskell
--   3D vector types.
--
data Pos a = Pos { _x :: a, _y :: a, _z :: a }
   deriving (Eq, Show)

type IPos = Pos Int
type FPos = Pos Float

instance FromMineCraft a => FromMineCraft (Pos a) where
  fromMC s = let toks = map fromMC $ splitOn "," s
             in case toks of
                    (x:z:y:[]) -> Pos x y z
                    _ -> error $ "*Expected 3 values* " ++ s

instance ToMineCraft a => ToMineCraft (Pos a) where
  toMC (Pos x y z) = intercalate "," $ map toMC [x,z,y]

-- | Represent a block.
--
--   We should probably combine `BlockType` and `BlockData`.
--   I have not looked to see whether it is worth using
--   an integer (as I currently do) or just an enumerated
--   type (e.g. if there are large ranges of the range 0 to 1023
--   that do not represent a valid block).
--
--   Use `Data.MineCraft.Pi.Block.showBlock` for a more readable
--   way to convert to a @String@.
newtype BlockType = BlockType Word16 
  deriving (Eq, Ord, Show)

-- | Data on a block.
newtype BlockData = BlockData Int
  deriving (Eq, Ord, Show)

{- Looks like this range includes entities and other items than just
blocks.
instance Bounded BlockType where
  minBound = BlockType 0
  maxBound = BlockType 1023
-}

instance ToMineCraft BlockType where
  toMC (BlockType bt) = show bt

instance FromMineCraft BlockType where
  fromMC val = let i = eRead val
               in if i >= 0 && i < 1024
                  then BlockType i
                  else error $ "*invalid block type: " ++ val

instance ToMineCraft BlockData where
  toMC (BlockData bd) = show bd

instance FromMineCraft BlockData where
  fromMC = BlockData . eRead