// -*- c++ -*-
#ifndef _WORLD_H
#define _WORLD_H

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

class Rivers;
class MapDisplay;
class WDisplay;
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
typedef enum { WATER = 0, LAND, MOUNTAINS, HILLS, DESERT,
       SWAMP, PLAINS, GRASS, FOREST, JUNGLE, RIVER,
       LAST_TERRAIN_TYPE } TerrainType;
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 RAILROADED = 0x80;
const ushort MINED = 0x100;
const ushort POLLUTED = 0x200;

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 Distance(MapCoord c1, MapCoord c2);
  int What(MapCoord c) { return world(c.x, c.y); }
  void Set(MapCoord c, int what);

  TerrainType Terrain(MapCoord c) {return TerrainType(What(c) & TERRAIN);}

  int IsGoodie(MapCoord c) { return What(c) & 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(WDisplay *d, MapCoord c);
  void Draw(WDisplay *d, MapCoord c, int cols, int rows);

  // same as Draw except that we only draw one square and units
  // are ignored
  void DrawBase(WDisplay *d, MapCoord c); //, int atx, int aty);

  void DrawSquare(WDisplay *d, MapCoord c);

  // color the main map
  void MarkSquare(MapDisplay *w, MapCoord c, int c);
  void DrawMainMap(MapDisplay *w); // redraws the main map

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

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

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

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

  // zone-of-control stuff
  int OpponentPresent(int player, MapCoord c);
  int NextToOpponent(int player, MapCoord c);

  // how much around you is visible
  void SeeSquare(MapDisplay *w, MapCoord c, int side = 1);
  void HideSquare(MapDisplay *w, MapCoord c, int side = 1);

  double SquareDefense(MapCoord c, int attacker);

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

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

  ulong &WhichCity(MapCoord c) { return cities(c.x, c.y); }

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

  int Food(MapCoord c, int player, int LoveTheRuler);
  int Prod(MapCoord c, int player, int LoveTheRuler);
  int Trade(MapCoord c, int player, int LoveTheRuler);

  int IsRoad(MapCoord c) { return What(c) & ROAD; }
  void MakeRoad(MapCoord c);

  int IsRailRoad(MapCoord c) { return What(c) & RAILROADED; }
  void MakeRailRoad(MapCoord c);

  int Irrigated(MapCoord c) { return What(c) & IRRIG; }
  void Irrigate(MapCoord c);

  int Mined(MapCoord c) { return What(c) & MINED; }
  void Mine(MapCoord c);

  int Polluted(MapCoord c) { return What(c) & POLLUTED; }
  void Pollute(MapCoord c);
  void PolluteNear(MapCoord c);
  void UnPollute(MapCoord c);

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

  int IrrigTime(MapCoord c);
  int RoadTime(MapCoord c);
  int RailRoadTime(MapCoord c);
  int MineTime(MapCoord c);
  int UnPolluteTime(MapCoord c);

private:
  typedef List<ulong> LongList;

  Array<ushort> world;
  Array<ushort> lastSeen;
  Array<LongList> units;
  Array<ulong> cities;
  Array<ulong> working;
  ushort& what(MapCoord c) {return world(c.x,c.y);}

  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 int waterColor, landColor, blackColor;

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

// MarkM changed GivesWater to not give water from city hexes, because
// he also made city hexes on clearish terrain automatically irrigated.
  int GivesWater(MapCoord c)
    { return (Irrigated(c) && !WhichCity(c))
		|| Terrain(c) == WATER || Terrain(c) == RIVER;
    }

};

#endif
