#include "Gen2.h"

void Creature::Step()
{
	for(int i = 0; i < 8192; i++) {
		Gen& g = gen[i];
		switch(gen[i].op & 15) {
		case 1:
			g.value = Op1(g) + Op2(g);
			break;
		case 2:
			g.value = Op1(g) * Op2(g);
			break;
		case 3:
			g.value = Op1(g) ^ Op2(g);
			break;
		case 4:
			g.value = ~Op1(g);
			break;
		case 5:
			g.value = Op1(g) << Op2(g);
			break;
		case 6:
			g.value = Op1(g) >> Op2(g);
			break;
		case 7:
			g.value = !Op1(g);
			break;
		case 8:
			g.value = g.op1;
			break;
		case 9:
			g.value = Op1(g) & Op2(g);
			break;
		case 10:
			g.value = Op1(g) | Op2(g);
			break;
		case 11:
			g.value = Op1(g) < Op2(g);
			break;
		default:
			g.value = Op1(g);
			break;
		}
	}
}

void Creature::Random()
{
	color = Color(rand() & 255, rand() & 255, rand() & 255);
	energy = 20;
	pos.x = rand() & PMSK;
	pos.y = rand() & PMSK;
	ppos = pos;
	for(int i = 0; i < 8192; i++) {
		Gen& g = gen[i];
		g.op = rand();
		g.op1 = rand();
		g.op2 = rand();
	}
}

void World::Paint(Draw& w)
{
	ImageBuffer ib(WSZ, WSZ);
	RGBA *t = ~ib;
	for(int y = 0; y < WSZ; y++) {
		for(int x = 0; x < WSZ; x++) {
			Spot& p = spot[y][x];
			RGBA c;
			if(!p.creature) {
				c.a = 255;
				c.r = c.g = c.b = 0;
				switch(p.kind) {
				case ROCK:
					c.b = 255;
					break;
				case TRAP:
					c.r = 255;
					break;
				case GRASS:
					c.g = p.energy;
					break;
				}
			}
			*t++ = c;
		}
	}
	for(int i = 0; i < creature.GetCount(); i++) {
		Point p = creature[i].pos;
		RGBA c;
		c.a = 255;
		c.b = c.r = c.g = minmax(creature[i].energy, 0, 255);
		ib[p.y][p.x] = c;
	}
	ib.SetKind(IMAGE_OPAQUE);
	w.DrawImage(0, 0, ib);
	w.DrawRect(WSZ + 1, 0, 99999, 99999, SColorFace());
	w.DrawRect(0, WSZ + 1, 99999, 99999, SColorFace());
}

void World::Die(int i, const char *cause)
{
	LOG(cause);
	creature.Remove(i);
	died++;
}

void World::Step()
{
	step++;
	int gen = 0;
	int best = 0;
	int n = creature.GetCount();
	if((step & 31) == 0 && creature.GetCount() < 1200)
		for(int y = 0; y < WSZ; y++)
			for(int x = 0; x < WSZ; x++) {
				Spot& p = spot[y][x];
				if(p.kind == GRASS && p.energy < 255)
					p.energy++;
			}
	if(n > 500) {
		Spot& p = spot[(rand() & 255) + 400][(rand() & 255) + 400];
		if(p.kind == EMPTY) {
			p.kind = TRAP;
			p.energy = 0;
		}
		Spot& tp = spot[rand() & 1023][rand() & 1023];
		if(tp.kind == EMPTY) {
			tp.kind = TRAP;
			tp.energy = 0;
		}
	}
	for(int i = 0; i < creature.GetCount(); i++) {
		Creature& c = creature[i];
		int ie = 7500;
		int ik = 7000;
		for(int x = -6; x <= 6; x++)
			for(int y = -6; y <= 6; y++) {
				Spot& p = spot[(c.pos.y + y) & PMSK][(c.pos.x + x) & PMSK];
				c.gen[ie++].value = p.kind;
				c.gen[ik++].value = p.energy;
			}
	}
	{
		CoWork co;
		for(int i = 0; i < creature.GetCount(); i++)
			co & callback(&creature[i], &Creature::Step);
	}
	for(int i = 0; i < creature.GetCount(); i++) {
		Creature& c = creature[i];
		if(c.generation > gen)
			gen = c.generation;
		if(c.collected > best)
			best = c.collected;
		Point np = c.pos;
		if(c.gen[8190].value < 0x7000) {
			c.gen[8190].value = 0x8000;
			np.x = (np.x - 1) & PMSK;
		}
		if(c.gen[8190].value > 0x9000) {
			c.gen[8190].value = 0x8000;
			np.x = (np.x + 1) & PMSK;
		}
		if(c.gen[8191].value < 0x7000) {
			c.gen[8191].value = 0x8000;
			np.y = (np.y - 1) & PMSK;
		}
		if(c.gen[8191].value > 0x9000) {
			c.gen[8191].value = 0x8000;
			np.y = (np.y + 1) & PMSK;
		}
		Spot& p = spot[np.y][np.x];
		if(np != c.pos)
			c.energy--;
		switch(p.kind) {
		case GRASS:
			LOG("Eaten " << (int)p.energy);
			for(int i = 5200; i < 5300; i++)
				c.gen[i].value += p.energy;
			c.energy += p.energy;
			c.collected += 16 * p.energy;
			p.energy = 0;
			p.kind = EMPTY;
			for(;;) {
				Spot& p = spot[rand() & PMSK][rand() & PMSK];
				if(p.kind == EMPTY) {
					p.kind = GRASS;
					p.energy = 0;
					break;
				}
			}
			break;
		case ROCK:
			np = c.pos;
			for(int i = 5000; i < 5100; i++)
				c.gen[i].value = 8000;
			break;
		}
		for(int q = 5400; q < 5500; q++)
			c.gen[q].value = c.energy;
		spot[c.pos.y][c.pos.x].creature = NULL;
		if(c.pos != np) {
			c.ppos = c.pos;
			c.pos = np;
		}
		spot[c.pos.y][c.pos.x].creature = &c;
		c.energy--;
		if(p.kind == TRAP) {
			if(!(c.pos.x == 0 || c.pos.y == 0 || c.pos.x == PMSK || c.pos.y == PMSK))
				p.kind = EMPTY;
			Die(i, "Fallen in trap");
		}
		else
		if(c.energy < 0)
			Die(i, "Starved to death");
		else
		if(c.energy > 400) {
			int mi = 0;
			int mind = INT_MAX;
			for(int i = 1; i < creature.GetCount(); i++) {
				Point p = creature[i].pos;
				int d = (np.x - p.x) * (np.x - p.x) + (np.y - p.y) * (np.y - p.y);
				if(d < mind) {
					mi = i;
					mind = d;
				}
			}
			Creature& pc = creature[mi];
			Creature& nc = creature.Add();
			nc.pos = c.ppos;
			nc.energy = 200;
			nc.generation = c.generation + 1;
			c.energy -= 200;
			for(int i = 0; i < 8192; i++)
				nc.gen[i] = (rand() & 1) ? c.gen[i] : pc.gen[i];
			while((rand() & 5) == 0) {
				nc.color = Color(127 + (((rand() & 15) + c.color.GetR() - 7) & 127),
				                 127 + (((rand() & 15) + c.color.GetG() - 7) & 127),
				                 127 + (((rand() & 15) + c.color.GetB() - 7) & 127));
				for(int p = rand() & 255; p < 8192; p++) {
					if((rand() & 3) == 0)
						nc.gen[p].op = rand();
					if((rand() & 3) == 0)
						nc.gen[p].op1 = rand();
					if((rand() & 3) == 0)
						nc.gen[p].op2 = rand();
					p += rand() & 255;
				}
			}
			born++;
		}
	}
/*	for(int i = 0; i < 1024; i++) {
		spot[i][0].kind = TRAP;
		spot[i][PMSK].kind = TRAP;
		spot[0][i].kind = TRAP;
		spot[PMSK][i].kind = TRAP;
	}*/
	Title(String().Cat() << "Step: " << step << " Gen: " << gen << ", pop: " << creature.GetCount()
	      << ", born: " << born << ", died: " << died << " best: " << best);
	Refresh();
}

World::World()
{
	spot = new Spot[WSZ][WSZ];
	for(int y = 0; y < WSZ; y++)
		for(int x = 0; x < WSZ; x++) {
			Spot& p = spot[x][y];
			p.kind = 0;
			if((rand() & 15) == 0)
				p.kind = ROCK;
			if((rand() & 127) == 0)
				p.kind = TRAP;
			if(rand() % 20 == 0) {
				p.kind = GRASS;
				p.energy = rand();
			}
//			if(x == 0 || y == 0 || x == PMSK || y == PMSK) {
//				p.kind = TRAP;
//				p.energy = 0;
//			}
			p.creature = NULL;
		}
	if(PromptYesNo("Restore creatures from the previous run?")) {
		FileIn in(ConfigFile("creatures"));
		for(;;) {
			Creature r;
			if(!in.GetAll(&r, sizeof(Creature)))
				break;
			creature.Add(r);
		}
	}
	else
		for(int i = 0; i < 15000; i++)
			creature.Add().Random();
	SetTimeCallback(-2, THISBACK(Step));
	step = 0;
	born = died = 0;
	Sizeable().Zoomable();
}

World::~World()
{
	if(PromptYesNo("Store creatures?")) {
		FileOut out(ConfigFile("creatures"));
		for(int i = 0; i < creature.GetCount(); i++)
			out.Put(&creature[i], sizeof(Creature));
	}
	delete[] spot;
}

GUI_APP_MAIN {
	World().Run();
}
