--------------------------------------------------------------------------------
-- |
--  Module      :  Block
--  License     :  Public Domain
--
--  Maintainer  :  Douglas Burke
--  Stability   :  experimental
--  Portability :  Haskell 98
--
-- Block handling.
--
-- It probably makes sense for the block types in
-- "Data.MineCraft.Pi.Types" - namely `Data.MineCraft.Pi.Types.BlockType`
-- and `Data.MineCraft.Pi.Types.BlockData`
-- to be moved into this module.
--
-- See <http://www.minecraftwiki.net/wiki/Data_values_(Pocket_Edition)> and
-- <http://www.minecraftwiki.net/wiki/Pi_Edition_version_history>, although I
-- have not cross-matched and verified all this information.
--
--------------------------------------------------------------------------------

module Data.MineCraft.Pi.Block
    (
      -- * Queries
      getBlock
    , getBlockData
    , getBlocks

      -- * Commands
    , setBlock
    , setBlockData
    , setBlocks
    , setBlocksData

      -- * Utilities

    , showBlock

      -- * Block types
    , air
    , stone
    , grass
    , dirt
    , cobblestone
    , woodPlanks
    , sapling
    , bedrock
    , water
    , waterStationary
    , lava
    , lavaStationary
    , sand
    , gravel
    , goldOre
    , ironOre
    , coalOre
    , wood
    , leaves
    , glass
    , lapisLazuliOre
    , lapisLazuliBlock
    , sandstone
    , bed
    , cobweb
    , grassTall
    , wool
    , flowerYellow
    , flowerCyan
    , mushroomBrown
    , mushroomRed
    , goldBlock
    , ironBlock
    , stoneSlabDouble
    , stoneSlab
    , brickBlock
    , tnt
    , bookshelf
    , mossStone
    , obsidian
    , torch
    , fire
    , stairsWood
    , chest
    , diamondOre
    , diamondBlock
    , craftingTable
    , farmland
    , furnaceInactive
    , furnaceActive
    , doorWood
    , ladder
    , stairsCobblestone
    , doorIron
    , redstoneOre
    , snow
    , ice
    , snowBlock
    , cactus
    , clay
    , sugarCane
    , fence
    , glowstoneBlock
    , bedrockInvisible
    , stoneBrick
    , glassPane
    , melon
    , fenceGate
    , glowingObsidian
    , netherReactorCore
    ) where

import Control.Monad (liftM)

import Data.Maybe (fromMaybe)
import Data.Word (Word16)

import Data.MineCraft.Pi.Types
import Network.MineCraft.Pi.Client
import Network.MineCraft.Pi.Client.Internal

-- | A type of a block.
air, stone, grass, dirt, cobblestone,
  woodPlanks, sapling, bedrock, water,
  waterStationary, lava, lavaStationary, sand,
  gravel, goldOre, ironOre, coalOre,
  wood, leaves, glass,
  lapisLazuliOre, lapisLazuliBlock,
  sandstone, bed, cobweb, grassTall, wool,
  flowerYellow, flowerCyan,
  mushroomBrown, mushroomRed,
  goldBlock, ironBlock, stoneSlabDouble, stoneSlab,
  brickBlock, tnt, bookshelf, mossStone, obsidian,
  torch, fire, stairsWood, chest, diamondOre,
  diamondBlock, craftingTable, farmland,
  furnaceInactive, furnaceActive, doorWood, ladder,
  stairsCobblestone, doorIron, redstoneOre,
  snow, ice, snowBlock, cactus, clay, sugarCane,
  fence, glowstoneBlock, bedrockInvisible,
  stoneBrick, glassPane, melon, fenceGate,
  glowingObsidian, netherReactorCore
  :: BlockType

air = BlockType 0
stone = BlockType 1
grass = BlockType 2
dirt = BlockType 3
cobblestone = BlockType 4
woodPlanks = BlockType 5
sapling = BlockType 6
bedrock = BlockType 7
water = BlockType 8
waterStationary = BlockType 9
lava = BlockType 10
lavaStationary = BlockType 11
sand = BlockType 12
gravel = BlockType 13
goldOre = BlockType 14
ironOre = BlockType 15
coalOre = BlockType 16
wood = BlockType 17
leaves = BlockType 18

glass = BlockType 20

lapisLazuliOre = BlockType 21
lapisLazuliBlock = BlockType 22

sandstone = BlockType 24

bed = BlockType 26

cobweb = BlockType 30
grassTall = BlockType 31

wool = BlockType 35

flowerYellow = BlockType 37
flowerCyan = BlockType 38
mushroomBrown = BlockType 39
mushroomRed = BlockType 40
goldBlock = BlockType 41
ironBlock = BlockType 42
stoneSlabDouble = BlockType 43
stoneSlab = BlockType 44
brickBlock = BlockType 45
tnt = BlockType 46
bookshelf = BlockType 47
mossStone = BlockType 48
obsidian = BlockType 49
torch = BlockType 50
fire = BlockType 51

stairsWood = BlockType 53
chest = BlockType 54

diamondOre = BlockType 56
diamondBlock = BlockType 57
craftingTable = BlockType 58 

farmland = BlockType 60
furnaceInactive = BlockType 61
furnaceActive = BlockType 62

doorWood = BlockType 64
ladder = BlockType 65

stairsCobblestone = BlockType 67

doorIron = BlockType 71

redstoneOre = BlockType 73

snow = BlockType 78
ice = BlockType 79
snowBlock = BlockType 80
cactus = BlockType 81
clay = BlockType 82
sugarCane = BlockType 83

fence = BlockType 85

glowstoneBlock = BlockType 89

bedrockInvisible = BlockType 95

stoneBrick = BlockType 98

glassPane = BlockType 102
melon = BlockType 103

fenceGate = BlockType 107

glowingObsidian = BlockType 246
netherReactorCore = BlockType 247

-- For now assume the table is small enough it is not
-- worth using a map.
blockNames :: [(Word16, String)]
blockNames = 
    [ (0, "Air")
    , (1, "Stone")
    , (2, "Grass")
    , (3, "Dirt")
    , (4, "Cobblestone")
    , (5, "Wooden Plank")
    , (6, "Sapling")
    , (7, "BedRock")
    , (8, "Water")
    , (9, "Stationary water")
    , (10, "Lava")
    , (11, "Stationary lava")
    , (12, "Sand")
    , (13, "Gravel")
    , (14, "Gold Ore")
    , (15, "Iron Ore")
    , (16, "Coal Ore")
    , (17, "Wood")
    , (18, "Leaves")
    , (20, "Glass")
    , (21, "Lapis Lazuli Ore")
    , (22, "Lapis Lazuli Block")
    , (24, "Sandstone")
    , (26, "Bed")
    , (30, "Cobweb")
    , (31, "Tall Grass")
    , (35, "Wool")
    , (37, "Yellow Flower")
    , (38, "Cyan Flower")
    , (39, "Brown Mushroom")
    , (40, "Brown Mushroom")
    , (41, "Gold Block")
    , (42, "Iron Block")
    , (43, "Double Stone Slab")
    , (44, "Stone Slab")
    , (45, "Brick Block")
    , (46, "TNT")
    , (47, "Bookshelf")
    , (48, "Moss Stone")
    , (49, "Obsidian")
    , (50, "Torch")
    , (51, "Fire")
    , (53, "Wooden Stairs")
    , (54, "Chest")
    , (56, "Diamond Ore")
    , (57, "Diamond Block")
    , (58, "Crafting Table")
    , (59, "Wheat Seeds") -- valid?
    , (60, "Farmland")
    , (61, "Furnace")
    , (62, "Burning Furnace")
    , (63, "Sign Post")
    , (64, "Wooden Door")
    , (65, "Ladder")
    , (67, "Cobblestone Stairs")
    , (68, "Wall Sign") -- valid?
    , (71, "Iron Door")
    , (73, "Redstone Ore")
    , (74, "Glowing Redstone Ore") -- valid?
    , (78, "Snow")
    , (79, "Ice")
    , (80, "Snow Block")
    , (81, "Cactus")
    , (82, "Clay")
    , (83, "Sugar Cane")
    , (85, "Fence")
    , (87, "Netherrack") -- valid?
    , (89, "Glowstone Block")
    , (95, "Invisible Bedrock")
    , (98, "Stone Brick")
    , (102, "Glass Pane")
    , (103, "Melon")
    , (105, "Melon Stem") -- valid?
    , (107, "Fence Gate")
    , (108, "Brick Stairs") -- valid?
    , (109, "Stone Brick Stairs") -- valid?
    , (112, "Nether Brick") -- valid?
    , (114, "Nether Brick Stairs") -- valid?
    , (128, "Sandstone Stairs") -- valid?
    , (155, "Block of Quartz") -- valid?
    , (156, "Quartz Stairs") -- valid?
    , (245, "Stone Cutter") -- valid?
    , (246, "Glowing Obsidian")
    , (247, "Nether Reactor Core")
    , (249, "Update Game Block") -- valid?
    , (253, "Grass Block (mimic)") -- valid?
    , (254, "Leaves (mysterious)") -- valid?
    , (255, ".name") -- valid?
    ]

-- | Return a name for the block type, or "Unknown block <number>"
--   if unknown.
showBlock :: BlockType -> String
showBlock (BlockType n) =
    fromMaybe ("Unknown block <" ++ show n ++ ">")
              $ lookup n blockNames

-- | What is the block at this position? See also `getBlockData`.
getBlock :: IPos -> MCPI BlockType
getBlock pos = fromMC `liftM` query "world.getBlock" [toMC pos]

-- | What is the block at this position? See also `getBlock`.
getBlockData :: IPos -> MCPI (BlockType, BlockData)
getBlockData pos = fromMC `liftM` query "world.getBlockWithData" [toMC pos]

-- | Get the blocks in the cuboid defined by the start and end positions.
--
getBlocks :: 
    IPos      -- ^ One corner of the cuboid. 
    -> IPos   -- ^ Opposite corner.
    -> MCPI [BlockType] -- ^ The order has not been specified.
getBlocks spos epos = fromMC `liftM`
                      query "world.getBlocks" [toMC spos, toMC epos]

-- | Change the block at the position. See also `setBlockData`.
setBlock :: IPos -> BlockType -> MCPI ()
setBlock pos bt = command "world.setBlock" [toMC pos, toMC bt]

-- | Change the block at the position. See also `setBlock`.
setBlockData :: IPos -> (BlockType, BlockData) -> MCPI ()
setBlockData pos (bt, bd) =
  command "world.setBlock" [toMC pos, toMC bt, toMC bd]

-- | Set all the blocks in the cuboid to the same type. See also
--   `setBlocksData`.
setBlocks :: IPos -> IPos -> BlockType -> MCPI ()
setBlocks spos epos bt =
  command "world.setBlocks" [toMC spos, toMC epos, toMC bt]

-- | Set all the blocks in the cuboid to the same type. See also
--   `setBlocks`.
setBlocksData :: IPos -> IPos -> (BlockType, BlockData) -> MCPI ()
setBlocksData spos epos (bt, bd) =
  command "world.setBlocks" [toMC spos, toMC epos, toMC bt, toMC bd]