#include <math.h>
#include "citydisplay.h"
#include "displays.h"
#include "graph.h"
#include "city.h"
#include "world.h"
#include "trans.h"
#include "units.h"
#include "pixmaps.h"

// FIXME: are these decls really clean ?

int WDisplay::white, WDisplay::black, WDisplay::gray;
int CityDisplay::back, CityDisplay::red;

void AllocColors(Graphics *screen)
{
  WDisplay::white = screen->AllocColor((char*)whiteColor);
  WDisplay::black = screen->AllocColor((char*)blackColor);
  WDisplay::gray = screen->AllocColor((char*)grayColor);
  CityDisplay::red = screen->AllocColor((char*)redColor);
  CityDisplay::back = screen->AllocColor((char*)backGroundColor);
}

CityDisplay::CityDisplay(City *cityp, Player *owner) : WDisplay(owner) {
  city = cityp;
  city->SetDisplay(this);
}

CityDisplay::~CityDisplay() {
  city->SetDisplay(NULL);
}

void CityDisplay::Setup() {
  CreateButtons();
  CreateDrawingArea(CityScreenWidth, CityScreenHeight, this);
}

void CityDisplay::Update() {
  city->InfoScreen();
}

void CityDisplay::GetEvent(EventType event, int x, int y, char */*data*/)
{
  if (state == IN_SELECT)
    return;
  if (event == MOUSE_LEFT) {
    if (y >= StationedUnitsY && x >= StationedUnitsX)
      {
	// find unit
	int unit_no = int(1+(x-StationedUnitsX)/SquareWidth)+7*int((y-StationedUnitsY)/SquareHeight);
	if (unit_no >= world->Units(city->coord).Count())
	  return;
	Lister<ulong> unitl = world->Units(city->coord);
	if (unit_no)
	  for(int i = 0;i<unit_no-1;i++)
	    unitl.Next();
	
	// wake unit
	player->WakeUnit(trans->TransUnit(unitl.Elem()));
	city->ShowStationedUnits();
      }
    else {
      if (y >= PeopleOffsetY && y <= PeopleOffsetY+PeopleHeight) {
	// changing elvi/scientists/taxmen
	int n = (int)ceil((x-3*PeopleGap)/(double)PeopleWidth-city->size);
	if ((n <= 0) || (n > city->elvi+city->taxmen+city->scientists))
	  return;
	if (n <= city->elvi) { // changing elvi to scientists
	  city->elvi--;
	  city->scientists++;
	} else if (n <= city->elvi+city->scientists) { // scientists to taxmen
	  city->scientists--;
	  city->taxmen++;
	} else { // taxmen to elvi
	  city->taxmen--;
	  city->elvi++;
	}
      } else {
	x = (x-CityMapOffsetX)/SquareWidth;
	y = (y-CityMapOffsetY)/SquareHeight;
	if (x < 0 || x > 4 || y < 0 || y > 4) return;
	x -= 2;
	y -= 2;
	Debug('c', "Clicked on square %d %d\n", x, y);
	if (x == 0 && y == 0) return;
	MapCoord wc = city->coord.down(y).right(x);
	if (world->RealVisible(wc) == 0) return;
	if (world->Working(wc) == city->id) { // take off
	  world->Working(wc) = 0;
	}
	else if (world->Working(wc) == 0) {
	  city->ComputeEcon();
	  if (city->elvi+city->taxmen+city->scientists <= 0) return;
	  world->Working(wc) = city->id;
	}
      }
      city->ComputeEcon();
      city->DrawCityMap();
      city->ShowPeople();
      city->ShowEcon();
    }
  }
  else if (event == KEYBOARD)
    {
      switch(x) {
      case 'b':
	city->ChooseBuy(); // buy what we're working on
	break;
      case 'c':
	city->ChooseBuild(); // change what we're working on
	break;
      case 'm':
	player->ShowMessages();
	delete this;
	break;
      case 'r':
	Update(); // refresh screen
	break;
      }
    }
}

static void closeHandler(CityDisplay *disp) {
  disp->GetEvent(KEYBOARD, 'm', 0, NULL);
}

static void refreshHandler(CityDisplay *disp) {
  disp->GetEvent(KEYBOARD, 'r', 0, NULL);
}

void CityDisplay::CreateButtons() {
  AddButton("Close", closeHandler, this);
  AddButton("Refresh", refreshHandler, this);
}

void 
CityDisplay::ShowPeople(int happy, int content, int goondas, int elvi, int taxmen, int scientists)
{
  int x = PeopleOffsetX, y = PeopleOffsetY, i;
  FillRect(PeopleOffsetX, PeopleOffsetY, ScreenWidth, PeopleHeight, back);
  for (i = 0; i < happy; ++i, x += PeopleWidth)
    DrawPixmap(x, y, &happy_pic);
  x += PeopleGap;
  for (i = 0; i < content; ++i, x += PeopleWidth)
    DrawPixmap(x, y, &worker_pic);
  x += PeopleGap;
  for (i = 0; i < goondas; ++i, x += PeopleWidth)
    DrawPixmap(x, y, &goonda_pic);
  x += PeopleGap;
  for (i = 0; i < elvi; ++i, x += PeopleWidth)
    DrawPixmap(x, y, &elvis_pic);
  for (i = 0; i < taxmen; ++i, x += PeopleWidth)
    DrawPixmap(x, y, &taxman_pic);
  for (i = 0; i < scientists; ++i, x += PeopleWidth)
    DrawPixmap(x, y, &scientist_pic);
}

void CityDisplay::ShowEcon(int food, int prod, int trade, int corruption, int lux, int gold, int sci)
{
  int x = CityEconOffsetX, y = CityEconOffsetY;
  FillRect(x, y, 3*ResourceIconWidth+6*FontWidth, 4*LineHeight, back);
  DrawPixmap(x, y, &food_pic);
  DrawPixmap(x, y+LineHeight, &shield_pic);
  DrawPixmap(x, y+2*LineHeight, &trade_pic);
  DrawPixmap(x+4*FontWidth+ResourceIconWidth,y+2*LineHeight, &corruption_pic);
  WriteInt(x+ResourceIconWidth+2, y, food, 4, white);
  WriteInt(x+ResourceIconWidth+2, y+LineHeight, prod, 4, white);
  WriteInt(x+ResourceIconWidth+2, y+2*LineHeight, trade, 4, white);
  WriteInt(x+4*FontWidth+2*ResourceIconWidth, y+2*LineHeight, corruption, 3, white);

  DrawPixmap(x, y+3*LineHeight, &lux_pic);
  DrawPixmap(x+ResourceIconWidth+2*FontWidth, y+3*LineHeight, &money_pic);
  DrawPixmap(x+2*ResourceIconWidth+4*FontWidth, y+3*LineHeight, &bulb_pic);
  WriteInt(x+ResourceIconWidth, y+3*LineHeight, lux, 2, white);
  WriteInt(x+2*ResourceIconWidth+2*FontWidth, y+3*LineHeight, gold, 2, white);
  WriteInt(x+3*ResourceIconWidth+4*FontWidth, y+3*LineHeight, sci, 2, white);
}

void CityDisplay::ShowOwnUnit(int n, Unit* unit, int food, int prod, int unhappy)
{
  if (n > MaxOwnUnitsRows*OwnUnitsPerRow) {
    WriteChar(OwnUnitsX+SquareWidth*OwnUnitsPerRow,
	      int(OwnUnitsY+SquareHeight*(MaxOwnUnitsRows-0.5)), '+', white);
    return;
  }

  int y = OwnUnitsY + SquareHeight*int(n/(double)OwnUnitsPerRow);
  int x = OwnUnitsX+SquareWidth*(n % OwnUnitsPerRow);

  unit->Draw(this, ScreenCoord(x, y));

  y+=SquareHeight-2;
  int xinc = ResourceIconWidth;
  if ((food+prod+unhappy)*ResourceIconWidth > SquareWidth)
    xinc = SquareWidth/ResourceIconWidth/(food+prod+unhappy);

  while (food) {
    DrawPixmap(x, y, &food_pic);
    x += xinc;
    food--;
  }
  while (prod) {
    DrawPixmap(x, y, &shield_pic);
    x += xinc;
    prod--;
  }
  while (unhappy) {
    DrawPixmap(x, y, &unhappy_pic);
    x += xinc;
    unhappy--;
  }
}

void CityDisplay::ShowStationedUnits(Lister<ulong> unitl) 
{
  int x = StationedUnitsX, y = StationedUnitsY, n = 0;
  while (unitl && n < 10) {
    Unit *unit = trans->TransUnit(unitl.Elem());
    unit->Draw(this, ScreenCoord(x, y));
    unitl.Next();
    x += SquareWidth;
    ++n;
    if (n%5 == 0) {
      x = StationedUnitsX;
      y += SquareHeight;
    }
  }
}

void CityDisplay::ShowFood(char *text) {
  FillRect(CityFoodOffsetX, CityFoodOffsetY, 12*FontWidth,2*LineHeight+2,back);
  WriteStr(CityFoodOffsetX+2, CityFoodOffsetY+2, "Food Stored", white);
  DrawPixmap(CityFoodOffsetX+2, CityFoodOffsetY+2+LineHeight, &food_pic);
  WriteStr(CityFoodOffsetX+ResourceIconWidth+4, CityFoodOffsetY+2+LineHeight, text, white);
}

void CityDisplay::ShowSmokeStacks(int number)
{
  int dx = SmokeStackWidth+2;
  
  while ((dx * number) > 5*SquareWidth)
    dx = int(double(dx)/1.5);
  for (int i = 0; i < number; i++)
    DrawPixmap(SmokeStacksX+ i*dx,SmokeStacksY, &smokestack_pic);
}

void CityDisplay::PutIcon(CPixmap * handle, int n, int &xc, int &yc,
		   int inc, int xlim, int xl)
{
  while (n-- > 0) {
    DrawPixmap(xc, yc, handle);
    xc += inc;
    if (xc >= xlim) { xc = xl; yc += ResourceIconHeight; }
  }
}

void CityDisplay::ShowResources(MapCoord coord, int food, int prod, int trade)
{
  ScreenCoord scoord = TranslateScreen(coord);
  int tot = food+prod+trade;
  int inc = tot < 3 ? ResourceIconWidth : ResourceIconWidth/(int(tot/2.0+0.5)-1);

  int x = scoord.x, y = scoord.y;
  PutIcon(&food_pic, food, x, y, inc, scoord.x+SquareWidth, scoord.x);
  PutIcon(&shield_pic, prod, x, y, inc, scoord.x+SquareWidth, scoord.x);
  PutIcon(&trade_pic, trade, x, y, inc, scoord.x+SquareWidth, scoord.x);
}

void CityDisplay::ShowBuildings(List<charp> buildings)
{
  int x = BuildingListX, y = BuildingListY, n = 0;
  FillRect(x, y,FontWidth*LongestBuildingName,BuildingsInList*LineHeight,back);

  Lister<charp> buildl(buildings);
  while (buildl && n < BuildingsInList)
    {
      WriteStr(x, y, buildl.Elem(), white);
      buildl.Next();
      y += LineHeight;
      ++n;
    }
}

void CityDisplay::ShowWorkInProgress(char *text)
{
  FillRect(CurrentProjectX, CurrentProjectY, FontWidth*LongestBuildingName, 2*LineHeight, back);
  int l = strlen(text);
  WriteStr(CurrentProjectX+(LongestBuildingName-l)*FontWidth/2, CurrentProjectY, text, white);
}

void CityDisplay::ShowBuildingProgress(char *text)
{
  DrawPixmap(CurrentProjectX,CurrentProjectY+LineHeight, &shield_pic);
  WriteStr(CurrentProjectX+ResourceIconWidth, CurrentProjectY+LineHeight, text, white);
}

ScreenCoord CityDisplay::TranslateScreen(MapCoord coord)
{
  Offset off = coord-city->coord;
  return ScreenCoord(CityMapOffsetX+(off.x+2)*SquareWidth,
		     CityMapOffsetY+(off.y+2)*SquareHeight);
}
