/* g_map.c
 *
 * Micropolis, Unix Version.  This game was released for the Unix platform
 * in or about 1990 and has been modified for inclusion in the One Laptop
 * Per Child program.  Copyright (C) 1989 - 2007 Electronic Arts Inc.  If
 * you need assistance with this program, you may contact:
 *   http://wiki.laptop.org/go/Micropolis  or email  micropolis@laptop.org.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.  You should have received a
 * copy of the GNU General Public License along with this program.  If
 * not, see <http://www.gnu.org/licenses/>.
 * 
 *             ADDITIONAL TERMS per GNU GPL Section 7
 * 
 * No trademark or publicity rights are granted.  This license does NOT
 * give you any right, title or interest in the trademark SimCity or any
 * other Electronic Arts trademark.  You may not distribute any
 * modification of this program using the trademark SimCity or claim any
 * affliation or association with Electronic Arts Inc. or its employees.
 * 
 * Any propagation or conveyance of this program must include this
 * copyright notice and these terms.
 * 
 * If you convey this program (or any modifications of it) and assume
 * contractual liability for the program to recipients of it, you agree
 * to indemnify Electronic Arts for any liability that those contractual
 * assumptions impose on Electronic Arts.
 * 
 * You may not misrepresent the origins of this program; modified
 * versions of the program must be marked as such and not identified as
 * the original program.
 * 
 * This disclaimer supplements the one included in the General Public
 * License.  TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS
 * PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY
 * OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK.  THE ENTIRE RISK OF
 * SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU.  ELECTRONIC ARTS
 * DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES,
 * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY
 * RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING,
 * USAGE, OR TRADE PRACTICE.  ELECTRONIC ARTS DOES NOT WARRANT AGAINST
 * INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL
 * MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE
 * UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE
 * WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE
 * CORRECTED.  NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR
 * ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY.  SOME
 * JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED
 * WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
 * CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY
 * NOT APPLY TO YOU.
 */
#include "sim.h"


#define VAL_NONE	0
#define VAL_LOW		1
#define VAL_MEDIUM	2
#define VAL_HIGH	3
#define VAL_VERYHIGH	4
#define VAL_PLUS	5
#define VAL_VERYPLUS	6
#define VAL_MINUS	7
#define VAL_VERYMINUS	8

short valMap[] = { 
  -1, COLOR_LIGHTGRAY, COLOR_YELLOW, COLOR_ORANGE, COLOR_RED,
  COLOR_DARKGREEN, COLOR_LIGHTGREEN, COLOR_ORANGE, COLOR_YELLOW
};

short valGrayMap[] = { 
  -1, 31, 127, 191, 255,
  223, 255, 31, 0
};


void (*mapProcs[NMAPS])(SimView *);

void drawPopDensity(SimView *view);
void drawRateOfGrowth(SimView *view);
void drawTrafMap(SimView *view);
void drawPolMap(SimView *view);
void drawCrimeMap(SimView *view);
void drawLandMap(SimView *view);
void drawFireRadius(SimView *view);
void drawPoliceRadius(SimView *view);
void ditherMap(SimView *view);
void drawRect(SimView *view, int pixel, int solid,
	 int x, int y, int w, int h);
void maybeDrawRect(SimView *view, int val,
	    int x, int y, int w, int h);


short GetCI(short x)
{
  if (x < 50)	return(VAL_NONE);
  if (x < 100)	return(VAL_LOW);
  if (x < 150)	return(VAL_MEDIUM);
  if (x < 200)	return(VAL_HIGH);
  return(VAL_VERYHIGH);
}


void
drawPopDensity(SimView *view)
{
  short x, y;

  drawAll(view);
  for (x = 0; x < HWLDX; x++) {
    for (y = 0; y < HWLDY; y++) {
      maybeDrawRect(view, GetCI(PopDensity[x][y]),
		    x * 6, y * 6, 6, 6);
    }
  }
}


void
drawRateOfGrowth(SimView *view)
{
  short x, y;

  drawAll(view);
  for (x = 0; x < SmX; x++) {
    for (y = 0; y < SmY; y++) {
      short val, z = RateOGMem[x][y];

      if (z > 100) val = VAL_VERYPLUS;
      else {
	if (z > 20)  val = VAL_PLUS;
	else {
	  if (z < -100) val = VAL_VERYMINUS;
	  else {
	    if (z < -20) val = VAL_MINUS;
	    else val = VAL_NONE;
	  }
	}
      }
      maybeDrawRect(view, val,
		    x * 24, y * 24, 24, 24);
    }
  }
}


void
drawTrafMap(SimView *view)
{
  short x;
  short y;

  drawLilTransMap(view);

  for (x = 0; x < HWLDX; x++) {
    for (y = 0; y < HWLDY; y++) {
      maybeDrawRect(view, GetCI(TrfDensity[x][y]),
		    x * 6, y * 6, 6, 6);
    }
  }
}


void
drawPolMap(SimView *view)
{
  short x, y;

  drawAll(view);

  for (x = 0; x < HWLDX; x++) {
    for (y = 0; y < HWLDY; y++) {
      maybeDrawRect(view, GetCI(10 + PollutionMem[x][y]),
		    x * 6, y * 6, 6, 6);
    }
  } 
}


void
drawCrimeMap(SimView *view)
{
  short x, y;

  drawAll(view);

  for (x = 0; x < HWLDX; x++) {
    for (y = 0; y < HWLDY; y++) {
      maybeDrawRect(view, GetCI(CrimeMem[x][y]),
		    x * 6, y * 6, 6, 6);
    }
  }
}


void
drawLandMap(SimView *view)
{
  short x, y;

  drawAll(view);

  for (x = 0; x < HWLDX; x++) {
    for (y = 0; y < HWLDY; y++) {
      maybeDrawRect(view, GetCI(LandValueMem[x][y]),
		    x * 6, y * 6, 6, 6);
    }
  }
}


void
drawFireRadius(SimView *view)
{
  short x, y;

  drawAll(view);
  for (x = 0; x < SmX; x++) {
    for (y = 0; y < SmY; y++) {
      maybeDrawRect(view, GetCI(FireRate[x][y]),
		    x * 24, y * 24, 24, 24);
    }
  }
}


void
drawPoliceRadius(SimView *view)
{
  short x, y;

  drawAll(view);
  for (x = 0; x < SmX; x++) {
    for (y = 0; y < SmY; y++) {
      maybeDrawRect(view, GetCI(PoliceMapEffect[x][y]),
		    x * 24, y * 24, 24, 24);
    }
  }
}


void
setUpMapProcs(void)
{
  mapProcs[ALMAP] = drawAll;
  mapProcs[REMAP] = drawRes;
  mapProcs[COMAP] = drawCom;
  mapProcs[INMAP] = drawInd;
  mapProcs[PRMAP] = drawPower;
  mapProcs[RDMAP] = drawLilTransMap;
  mapProcs[PDMAP] = drawPopDensity;
  mapProcs[RGMAP] = drawRateOfGrowth;
  mapProcs[TDMAP] = drawTrafMap;
  mapProcs[PLMAP] = drawPolMap;
  mapProcs[CRMAP] = drawCrimeMap;
  mapProcs[LVMAP] = drawLandMap;
  mapProcs[FIMAP] = drawFireRadius;
  mapProcs[POMAP] = drawPoliceRadius;
  mapProcs[DYMAP] = drawDynamic;
}


void
MemDrawMap(SimView *view)
{
  (*mapProcs[view->map_state])(view);
  if (!view->x->color) {
    ditherMap(view);
    XSetForeground(view->x->dpy, view->x->gc, view->pixels[COLOR_BLACK]);
    XSetBackground(view->x->dpy, view->x->gc, view->pixels[COLOR_WHITE]);
    XPutImage(view->x->dpy, view->pixmap, view->x->gc, view->image, 
	      0, 0, 0, 0, view->m_width, view->m_height);
  }
}


void
ditherMap(SimView *view)
{
  int i, x, y, width, height;
  int err, pixel1, pixel8;
  int line_bytes1 = view->line_bytes;
  int line_bytes8 = view->line_bytes8;
  unsigned char *image1 = view->data;
  unsigned char *image8 = view->data8;
  int *errors;

  width = view->m_width; height = view->m_height;

  errors = (int *)malloc(sizeof(int) * (width));

  for (i = 0; i < width; i++)
    errors[i] = (Rand16() & 15) - 7;

  err = (Rand16() & 15) - 7;

  for (y = 0; y < height; y += 2) {
    unsigned char *i1 = image1; 
    unsigned char *i8 = image8;

    image1 += line_bytes1;
    image8 += line_bytes8;

    for (x = 0; x < width; x += 8) {
      pixel1 = 0;
      for (i = 0; i < 8; i++) {
	pixel1 <<= 1;
	pixel8 = *(i8++) + err + errors[x + i];
	if (pixel8 > 127) {
	  err = pixel8 - 255;
	} else {
	  pixel1 |= 1;
	  err = pixel8;
	}
	errors[x + i] = err/2;
	err = err/2;
      }
      *(i1++) = pixel1;
    }

    i1 = image1 + (width / 8) - 1;
    i8 = image8 + width - 1;

    image1 += line_bytes1;
    image8 += line_bytes8;

    for (x = width - 8; x >= 0; x -= 8) {
      pixel1 = 0;
      for (i = 7; i >= 0; i--) {
	pixel1 >>= 1;
	pixel8 = *(i8--) + err + errors[x + i];
	if (pixel8 > 127) {
	  err = pixel8 - 255;
	} else {
	  pixel1 |= 128;
	  err = pixel8;
	}
	errors[x + i] = err/2;
	err = err/2;
      }
      *(i1--) = pixel1;
    }
  }

  free(errors);
}


void
maybeDrawRect(SimView *view, int val,
	    int x, int y, int w, int h)
{
  if (val == VAL_NONE) return;

  if (view->x->color) {
    drawRect(view, view->pixels[valMap[val]], 0, x, y, w, h);
  } else {
    drawRect(view, valGrayMap[val], 1, x, y, w, h);
  }
}


void
drawRect(SimView *view, int pixel, int solid,
	 int x, int y, int w, int h)
{
  int W = view->m_width, H = view->m_height;

  if (x < 0) {
    if ((w += x) < 0) w = 0;
    x = 0;
  } else if (x > W) {
    x = 0; w = 0;
  }
  if (x + w > W) {
    w = W - x;
  }
  if (y < 0) {
    if ((h += y) < 0) h = 0;
    y = 0;
  } else if (y > H) {
    y = 0; h = 0;
  }
  if (y + h > H) {
    h = H - y;
  }

  if (w && h) {
    int i, j, stipple = (x ^ y) & 1;
#if 0
    unsigned char *data =
      view->x->color ? view->data : view->data8;
#endif

    /* In the case of black and white, we use an 8 bit buffer and dither it. */
    int pixelBytes = 
      view->x->color ? view->pixel_bytes : 1;
    QUAD line = 
      view->x->color ? view->line_bytes : view->line_bytes8;

#if 0
    unsigned char *image =
      &(data[(line * y) + (x * pixelBytes)]);
#endif

    switch (pixelBytes) {

    case 1:
      {
	unsigned char *data = 
	  view->data8;
	unsigned char *image = 
	  &data[(line * y) + (x * pixelBytes)];

	for (i = h; i > 0; i--) {
	  for (j = w; j > 0; j--) {
	    if (solid || stipple++ & 1)
	      *image = pixel;
	    image++;
	  }
	  if (!(w & 1))
	    stipple++;
	  image += line - w;
	}
      }
      break;

    case 2:
      {
	unsigned short *data = 
	  (unsigned short *)view->data;
	unsigned short *image;
	line >>= 1; /* Convert from byte offset to short offset */
	image = 
	  &data[(line * y) + x];

	for (i = h; i > 0; i--) {
	  for (j = w; j > 0; j--) {
	    if (solid || stipple++ & 1) {
	      if (view->x->x_big_endian) {
	        *image = ((pixel & 0xff) << 8) | ((pixel & 0xff00) >> 8);
	      } else {
	        *image = pixel;
	      }
	    }
	    image++;
	  }
	  if (!(w & 1))
	    stipple++;
	  image += line - w;
	}

      }
      break;

    case 3:
    case 4:
      {
	unsigned char *data = 
	  (unsigned char *)view->data;
	unsigned char *image;
	image = 
	  &data[(line * y) + x * pixelBytes];

	for (i = h; i > 0; i--) {
	  for (j = w; j > 0; j--) {
	    if (view->x->x_big_endian) {
	      if (pixelBytes == 4) {
	        image++;
	      }
	      if (solid || stipple++ & 1) {
	        *(image++) = (pixel >> 16) & 0xff;
	        *(image++) = (pixel >> 8) & 0xff;
	        *(image++) = (pixel >> 0) & 0xff;
	      } else {
	        image += 3;
	      }
	    } else {
	      if (solid || stipple++ & 1) {
	        *(image++) = (pixel >> 0) & 0xff;
	        *(image++) = (pixel >> 8) & 0xff;
	        *(image++) = (pixel >> 16) & 0xff;
	      } else {
	        image += 3;
	      }
	      if (pixelBytes == 4) {
	        image++;
	      }
	    }
	  }
	  if (!(w & 1)) {
	    stipple++;
	  }
	  image += line - w * pixelBytes;
	}
      }
      break;

    default:
      assert(0); /* Undefined depth */
      break;
    }

  }
}



