mxArray*
ParticleSimulator::SaveConvexity()
{
	ParticleFactory* factory = ParticleFactory::getInstance();
	const int dims[] = { factory->particles.size(), 3 };
	vector<float> F(dims[0] * dims[1]);
	for (int i = 0; i < dims[0]; ++i)
	{
		MovingParticle* p = factory->particles[i];
		CParticleF pr = p->project(p->created + 0.1);
		SetData2(F, i, 0, dims[0], dims[1], pr.m_X);
		SetData2(F, i, 1, dims[0], dims[1], pr.m_Y);
		SetData2(F, i, 2, dims[0], dims[1], p->reflexive<=0 ? 0.0f: 1.0f);
	}
	return StoreData(F, mxSINGLE_CLASS, 2, dims);
}
StraightOffsetPolygon::StraightOffsetPolygon(const vector<MovingParticle*>& vertices, float time)
{
	particles = vertices;
	this->created = time;
	//set the neighborhood
	for (int i = 0; i < particles.size(); ++i)
	{
		MovingParticle* p = particles[i];
		MovingParticle* q = particles[i == particles.size() - 1 ? 0 : i + 1];
		MovingParticle* r = particles[i == 0 ? particles.size() - 1 : i - 1];
		MovingParticle::setNeighbors(p, r, q);
		pset.insert(p);
	}
	//calculate velocity if necessary
	for (int i = 0; i < particles.size(); ++i)
	{
		MovingParticle* p = particles[i];
		if (p->isInitialized()) continue;
		CParticleF o = p->getP();
		MovingParticle* q = particles[i == particles.size() - 1 ? 0 : i + 1];
		MovingParticle* r = particles[i == 0 ? particles.size() - 1 : i - 1];
		if (o == a) //leaf node
		{
			float ang = GetVisualDirection(o.m_X, o.m_Y, b.m_X, b.m_Y) + PI / 2.0;
			a.m_X = o.m_X + cos(ang);
			a.m_Y = o.m_Y + sin(ang);
		}
		else if (o == b) //leaf node
		{
			float ang = GetVisualDirection(o.m_X, o.m_Y, a.m_X, a.m_Y) - PI / 2.0;
			b.m_X = o.m_X + cos(ang);
			b.m_Y = o.m_Y + sin(ang);
		}
		float vx, vy;
		calculateBisectorVelocity(r->getP(), p->getP(), q->getP(), vx, vy);
		p->setVelocity(vx, vy);
	}
	//find the next event for each particle.
	//we only need to compute for those whose event has not been set of its participants no longer part of this polygon.
	for (int i = 0; i < particles.size(); ++i)
	{
		//MovingParticle* p = particles[i];
		//p->updateEvent();
		/*EventStruct event = p->getEvent();
		if (event.type == UnknownEvent ||
			pset.find(event.q) == pset.end() ||
			pset.find(event.r) == pset.end())
		{
			p->updateEvent(particles);
		}*/
	}
}
mxArray*
ParticleSimulator::SaveParticles()
{
	ParticleFactory* factory = ParticleFactory::getInstance();
	const int dims[] = { factory->particles.size(), ParticleDumpSize };
	vector<float> F(dims[0] * dims[1]);
	for (int i = 0; i < dims[0]; ++i)
	{
		MovingParticle* p = factory->particles[i];
		vector<float> v = p->dump2vector();
		for (int j = 0; j < dims[1]; ++j)
		{
			SetData2(F, i, j, dims[0], dims[1], v[j]);
		}
	}
	return StoreData(F, mxSINGLE_CLASS, 2, dims);
}
bool
ParticleSimulator::Simulate(float endtime, float delta, bool bdebug)
{
	ParticleFactory* factory = ParticleFactory::getInstance();
	float snapTime = delta;
	bool bSuccess = true;
	for (set<MovingParticle*>::iterator it = factory->activeSet.begin(); it != factory->activeSet.end(); ++it)
	{
		(*it)->updateEvent();
	}
	{
		vector<Snapshot> shots = Snapshot::TakeSnapshot(time);
		snapshots.insert(snapshots.end(), shots.begin(), shots.end());
	}
	int iter = 0;
	while (time < endtime)
	{
		iter++;
		vector<Snapshot> shots = Snapshot::TakeSnapshot(time); //temporary
		MovingParticle* p = MovingParticle::getNextEvent();
		if (p == NULL) break;
		if (p->getEvent().t > endtime) break;
		if (p->id == 255)
			p->id += 0;

		for (set<MovingParticle*>::iterator it = factory->activeSet.begin(); it != factory->activeSet.end(); ++it)
		{
			(*it)->update(p->getEvent().t - time);
		}
		time = p->getEvent().t;
		if (p->applyEvent() == false) break;
		p->getEvent().print();

		MovingParticle::removeUnstable();
		if (time >= snapTime)
		{
			vector<Snapshot> shots = Snapshot::TakeSnapshot(time);
			snapshots.insert(snapshots.end(), shots.begin(), shots.end());
			snapTime += delta;
		}
		MovingParticle::quickFinish();
		if (bdebug && MovingParticle::sanityCheck() == false)
		{
			printf("Violation of sanity check found at %f.\n", time);
			vector<Snapshot> shots = Snapshot::TakeSnapshot(time);
			snapshots.insert(snapshots.end(), shots.begin(), shots.end());
			bSuccess = false;
			break;
		}

		//find closed regions
		vector<vector<MovingParticle*>> regions = MovingParticle::clusterParticles();
		for (int i = 0; i < regions.size(); ++i)
		{
			//if (clockWise(regions[i]) > 0)
			{
				vector<MovingParticle*> tr = MovingParticle::traceBackPolygon(regions[i]);
				vector<vector<MovingParticle*>> areas = MovingParticle::closedRegions(tr);
				for (int j = 0; j < areas.size(); ++j)
				{
					Snapshot shot(0.0f, areas[j]);
					if (find(closedRegions.begin(), closedRegions.end(), shot) == closedRegions.end())
					{
						closedRegions.push_back(shot);
					}
				}
				//if (tr.size() > 10) //forget small ones
				{
					Snapshot shot(0.0f, tr);
					if (find(traces.begin(), traces.end(), shot) == traces.end())
					{
						traces.push_back(Snapshot(0, tr));
						polygons.push_back(Snapshot(time, regions[i]));
					}
				}
			}
		}

		doneEvents.push_back(p->getEvent());
		//for (set<MovingParticle*>::iterator it = factory->updateQueue.begin(); it != factory->updateQueue.end(); ++it)
		for (set<MovingParticle*>::iterator it = factory->activeSet.begin(); it != factory->activeSet.end(); ++it)
		{
			(*it)->updateEvent();
		}
		factory->updateQueue.clear();
	}
	for (int i = 0; i < factory->particles.size(); ++i)
	{
		MovingParticle* p = factory->particles[i];
		if (p->bActive == false && p->time > p->init_event_time && p->init_event_time >= 0)
		{
			printf("%d %f %f %f %d\n", p->id, p->created, p->time, p->init_event_time, p->event.type);
		}
	}
	return bSuccess;
}
/*
a snapshot provides neighborhood information at a particular point in time. We try to reproduce the configuration of the system 
as accurately as possible. We need to consider the following steps.
1. all particles that are created after the time point need to be removed from the system.
2. all particles that are still active at the time point need to be activated.
3. children of a particle need to be erased if they are created after the time point.
*/
bool
ParticleSimulator::Restore(vector<Snapshot>& snapshots)
{
	ParticleFactory* factory = ParticleFactory::getInstance();
	float time0 = 0;
	for (int i = 0; i < snapshots.size(); ++i)
	{
		if (snapshots[i].getTime() > time0)
		{
			time0 = snapshots[i].getTime();
		}
	}
	time = time0;
	for (int i = 0; i < factory->particles.size(); ++i)
	{
		MovingParticle* p = factory->particles[i];
		for (int j = 0; j < 2; ++j)
		{
			if (p->children[j] != NULL && p->children[j]->created > time)
			{
				p->children[j] = NULL;
			}
		}
	}

	vector<MovingParticle*> toremove;
	for (int i = 0; i < factory->particles.size(); ++i)
	{
		MovingParticle* p = factory->particles[i];
		if (p->created > time)
		{
			toremove.push_back(p);
		}
	}
	//activate those that are still active.
	vector<MovingParticle*> toactivate;
	for (int i = 0; i < factory->particles.size(); ++i)
	{
		MovingParticle* p = factory->particles[i];
		if (p->bActive == false && p->created < time && p->time > time)
		{
			toactivate.push_back(p);
		}
	}

	//set neighbors
	for (int i = 0; i < snapshots.size(); ++i)
	{
		float t = snapshots[i].getTime();
		for (int j = 0; j < snapshots[i].size(); ++j)
		{
			int id = snapshots[i].get(j);
			int id0 = snapshots[i].get(j == 0 ? snapshots[i].size() - 1 : j - 1);
			int id2 = snapshots[i].get(j == snapshots[i].size() - 1 ? 0 : j + 1);
			MovingParticle* p = factory->get(id);
			if (p == NULL)
			{
				return false;
			}
			p->setNeighbors(p, factory->get(id0), factory->get(id2));
		}
	}

	//delete future particles from memory
	for (int i = 0; i < toremove.size(); ++i)
	{
		factory->remove(toremove[i]);
	}
	//factory->activeSet.clear();
	for (int i = 0; i < toactivate.size(); ++i)
	{
		factory->activeSet.insert(toactivate[i]);
	}
	//recalculate positions
	for (set<MovingParticle*>::iterator it = factory->activeSet.begin(); it != factory->activeSet.end(); ++it)
	{
		MovingParticle* p = *it;
		p->p = p->project(time);
		p->time = time;
	}
	//recalculate events
	for (set<MovingParticle*>::iterator it = factory->activeSet.begin(); it != factory->activeSet.end(); ++it)
	{
		(*it)->event.type == UnknownEvent;
		(*it)->updateEvent();
	}
	return true;
}
bool
ParticleSimulator::Simulate(float endtime, float delta, bool bdebug)
{
	ParticleFactory& factory = ParticleFactory::getInstance();
	PolygonFactory& pfactory = PolygonFactory::getInstance();
	float snapTime = delta;
	bool bSuccess = true;
	for (set<MovingParticle*>::iterator it = factory.activeSet.begin(); it != factory.activeSet.end(); ++it)
	{
		(*it)->updateEvent();
	}
	{
		vector<Snapshot> shots = Snapshot::TakeSnapshot(time);
		snapshots.insert(snapshots.end(), shots.begin(), shots.end());
	}
	int iter = 0;
	while (time < endtime)
	{
		iter++;
		MovingParticle* p = MovingParticle::getNextEvent();
		if (p == NULL) break;
		if (p->getEvent().t > endtime) break;
		if (p->id == 19 && p->event.q->id == 22)
			iter += 0;

		for (set<MovingParticle*>::iterator it = factory.activeSet.begin(); it != factory.activeSet.end(); ++it)
		{
			(*it)->update(p->getEvent().t - time);
		}
		time = p->getEvent().t;
		pair<MovingParticle*, MovingParticle*> pnew = p->applyEvent();
		/*if (p->event.type == SplitEvent || p->event.type == CollisionEvent)
		{
			if (p->polygon->getId() == p->event.q->polygon->getId())
			{
				//split the same polygon into two
				dag.add(p->polygon, pnew.first->polygon);
				dag.add(p->polygon, pnew.second->polygon);
			}
			else {
				//merge two polygons into one
				dag.add(pnew.first->polygon, p->polygon);
				dag.add(pnew.first->polygon, p->event.q->polygon);
			}
		}*/
		p->getEvent().print();

		//MovingParticle::removeUnstable();
		if (time >= snapTime)
		{
			vector<Snapshot> shots = Snapshot::TakeSnapshot(time);
			snapshots.insert(snapshots.end(), shots.begin(), shots.end());
			snapTime += delta;
		}
		MovingParticle::quickFinish();
		if (bdebug && MovingParticle::sanityCheck() == false)
		{
			printf("Violation of sanity check found at %f.\n", time);
			vector<Snapshot> shots = Snapshot::TakeSnapshot(time);
			snapshots.insert(snapshots.end(), shots.begin(), shots.end());
			bSuccess = false;
			break;
		}

		//find closed regions
		vector<vector<MovingParticle*>> regions = MovingParticle::clusterParticles();
		for (int i = 0; i < regions.size(); ++i)
		{
			vector<MovingParticle*> tr = MovingParticle::traceBackPolygon(regions[i]);
			vector<vector<MovingParticle*>> areas = MovingParticle::closedRegions(tr);

			Snapshot shot0(time, time, regions[i]);
			for (int j = 0; j < areas.size(); ++j)
			{
				Snapshot shot(time, 0.0f, areas[j]);
				if (find(closedRegions.begin(), closedRegions.end(), shot) == closedRegions.end())
				{
					closedRegions.push_back(shot);
					//Polygon* poly = pfactory.makePolygon(areas[j], time);
					traces.push_back(shot0);
				}
			}
			
			/*if (find(traces.begin(), traces.end(), shot0) == traces.end())
			{
				polygons.push_back(Snapshot(time, time, regions[i]));
			}*/
		}

		doneEvents.push_back(p->getEvent());
		//for (set<MovingParticle*>::iterator it = factory->updateQueue.begin(); it != factory->updateQueue.end(); ++it)
		for (set<MovingParticle*>::iterator it = factory.activeSet.begin(); it != factory.activeSet.end(); ++it)
		{
			(*it)->updateEvent();
		}
		factory.updateQueue.clear();
	}
	for (int i = 0; i < factory.particles.size(); ++i)
	{
		//factory.particles[i]->printParentTree("\t");
	}
	return bSuccess;
}