#include "drone_controller.hh"
#include "opengl_widget.hh"

#include <QJsonArray>
#include <QDebug>


DroneController::DroneController(const QJsonObject &json)
	:framerate(json["framerate"].toInt()) {

	QJsonArray ja = json["drones"].toArray();
	drones.reserve(ja.size());
	for (const QJsonValue &o : ja) {
		drones.append(Drone(o.toObject()));
	}
	for (const Drone &d : drones) {
		for (const Waypoint &wp : d.getWaypoints()) {
			if (wp.frame > duration) duration = wp.frame;
		}
	}

	OpenGLWidget::instance->makeCurrent();
	QOpenGLTexture *ground_tex = new QOpenGLTexture(QImage(":/img/ground.jpg").mirrored());
	ground_tex->setMagnificationFilter(QOpenGLTexture::LinearMipMapLinear);
	ground_tex->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
	ground_tex->setWrapMode(QOpenGLTexture::MirroredRepeat);
	OpenGLMesh *ground = new OpenGLMesh({
			-1000, 0, -1000, 0, 1000, 0, 0, 0,
			1000, 0, -1000, 0, 1000, 0, 1000, 0,
			-1000, 0, 1000, 0, 1000, 0, 0, 1000,
			1000, 0, -1000, 0, 1000, 0, 1000, 0,
			-1000, 0, 1000, 0, 1000, 0, 0, 1000,
			1000, 0, 1000, 0, 1000, 0, 1000, 1000,
		}, ground_tex);
	OpenGLWidget::instance->meshes.append(*ground);
	OpenGLWidget::instance->doneCurrent();

	connect(&timer, &QTimer::timeout, this, &DroneController::step);
}


int DroneController::getDuration() const {
	return duration;
}


void DroneController::step() {
	for (Drone d : drones) {
		d.setTo(frame);
	}
	OpenGLWidget::instance->update();
	emit frameChanged(frame);
	if (frame == duration) {
		pause();
	} else {
		frame++;
	}
}


void DroneController::play() {
	if (!paused) return;
	paused = false;
	timer.start(1000. / framerate);
	emit playing();
}


void DroneController::pause() {
	if (paused) return;
	paused = true;
	timer.stop();
	emit pausing();
}


void DroneController::suspend() {
	if (!paused) {
		pause();
		paused = false;
	}
}


void DroneController::resume() {
	if (!paused) {
		paused = true;
		play();
	}
}


void DroneController::seek(int frame) {
	if (this->frame == frame) return;
	this->frame = frame;
	step();
}


void DroneController::computeCollisions(double sphere_radius) {
	double sqDist = sphere_radius * sphere_radius * 2;
	for (int i = 0; i < duration; i++) {
		for (Drone &a : drones) {
			a.setTo(i);
			for (Drone &b : drones) {
				b.setTo(i);
				if (&a == &b) continue;
				if (collides(a, b, sqDist)) {
					emit collision(a.getId(), b.getId(), i);
				}
			}
		}
	}
	seek(frame);
}


bool DroneController::collides(const Drone &a, const Drone &b, double sqDist) {
	return (b.getPos() - a.getPos()).lengthSquared() < sqDist;
}
