#ifndef _WORLD_H
#define _WORLD_H

#include "mytypes.h"
#include "graph.h"
#include "array.h"
#include "list.h"

class Rivers;

class City;

// the first 4 bits give the terrain type
// LAND is only used to make the world after that it is replaced
// by the right type
enum { WATER = 0, LAND, MOUNTAINS, HILLS, DESERT,
       SWAMP, PLAINS, GRASS, GRASS1, FOREST, JUNGLE, RIVER,
       LAST_TERRAIN_TYPE };
const ushort TERRAIN = 0x0f; // use this mask to get the terrain type

// depends on terrain type
// forest -> game, hills -> coal, jungle -> gems, mountains -> gold
// plains -> horses, swamp -> oil, desert -> oasis
const ushort GOODIE = 0x10;
const ushort ROAD = 0x20;
const ushort IRRIG = 0x40;
const ushort RAILROAD = 0x80;
const ushort MINED = 0x100;

const int HorizSpace = 64;
const int VertSpace = 8;

const int SquareWidth = 16;
const int SquareHeight = 16;

const int SquaresWide = (ScreenWidth-HorizSpace)/SquareWidth;
const int SquaresHigh = (ScreenHeight-VertSpace)/SquareHeight;

class MsgQ;

class World
{
public:
  World(int MaxX, int MaxY, int ContDist, int ChunkSize, int NumChunks,
	int LandPercent, int NumRivers, int RiverLength);
  World(MsgQ *); // get the world from the message Q
  World(); // read from save file
  ~World();

  void Save(); // save to save file

  void AllocColors();

  int What(int x, int y) { return world(x,y); }
  void Set(int x, int y, int what) { world(x,y) = what; }

  int Terrain(int x, int y) { return What(x, y) & TERRAIN; }

  int IsGoodie(int x, int y) { return world(x, y) & GOODIE; }

  // if there are any units then the first will be drawn
  // if there is a city it will be drawn
  // otherwise the terrain is drawn
  void Draw(int x, int y);
  void Draw(int x, int y, int cols, int rows, int atx, int aty);

  // same as Draw except that we only draw one square and units
  // are ignored
  void DrawBase(int x, int y, int atx, int aty);

  int MaxX() { return maxX; }
  int MaxY() { return maxY; }

  void Send(); // send the world over the socket

  void MakeVisible(int x, int y)
    { visible(x,y) = visible(x,y) == 0 ? 2 : visible(x,y)+1; }
  void LoseVisible(int x, int y)
    { --visible(x,y); }

  // how much around you is visible
  void SeeSquare(int x, int y, int side = 1);
  void HideSquare(int x, int y, int side = 1);

  double SquareDefense(int x, int y, int attacker);

  // returns true if you can see enemy units in that square
  int Visible(int x, int y) { return visible(x,y) > 1; }
  // returns real visibility
  int RealVisible(int x, int y) { return visible(x, y); }

  List<ulong> &Units(int x, int y) { return units(x,y); }

  ulong &WhichCity(int x, int y) { return cities(x,y); }

  // returns the id of the city who is working on this square
  // is 0 if no city is using it
  ulong &Working(int x, int y) { return working(x, y); }

  // color the main map
  void MarkSquare(int x, int y, int c);

  void DrawMainMap(); // redraws the main map

  int WaterColor() { return waterColor; }
  int LandColor() { return landColor; }

  int Food(int x, int y, int player);
  int Prod(int x, int y, int player);
  int Trade(int x, int y, int player);

  int IsRoad(int x, int y) { return What(x, y) & ROAD; }
  void MakeRoad(int x, int y);

  int IsRailRoad(int x, int y) { return world(x, y) & RAILROAD; }
  void MakeRailRoad(int x, int y);

  int Irrigated(int x, int y) { return What(x, y) & IRRIG; }
  void Irrigate(int x, int y);

  int Mined(int x, int y) { return world(x, y) & MINED; }
  void Mine(int x, int y);

  void DrawSquare(int wx, int wy, int xc, int yc);

  int FixX(int x) { return (x+maxX)%maxX; }
  int FixY(int y) { return (y+maxY)%maxY; }

  int IrrigTime(int x, int y);
  int RoadTime(int x, int y);
  int RailRoadTime(int x, int y);
  int MineTime(int x, int y);

private:
  typedef List<ulong> LongList;

  Array<ushort> world;
  Array<LongList> units;
  Array<ulong> cities;
  Array<ulong> working;

  Rivers *rivers;

  // if 0 then black, if it is 1 we draw the terrain, bigger than
  // that we draw any units there too
  Array<uchar> visible;

  int maxX, maxY;

  static char **pictures[LAST_TERRAIN_TYPE];
  static char **goodiePics[LAST_TERRAIN_TYPE];
  static long compiledPictures[LAST_TERRAIN_TYPE];
  static long compiledGoodies[LAST_TERRAIN_TYPE];

  static double terrainDefense[LAST_TERRAIN_TYPE];

  static long minedPic;

  static int waterColor, landColor, blackColor;

  void GrowChunk(int &xp, int &yp, int type);
  int Verify(int x, int y, int dist, int type, int where);
  void NewChunk(int &xp, int &yp, int dist, int type, int where);
  int MakeChunk(int x, int y, int dist, int type);
  int MakeTerrain(int max, int percent, int NumChunks, int ChunkSize,
		   int ContDist, int type, int where);

  int GivesWater(int x, int y)
    { return Irrigated(x, y) || Terrain(x, y) == WATER; }

};

#endif
