#include "sre/RenderPass.hpp"
#include "Scene.h"
#include "Application.h"
#include "Logger.hpp"
#include "SceneManager.h"
#include "PhysicsBody.hpp"
#include "Box2D/Dynamics/Contacts/b2Contact.h"

Scene::Scene():debugDraw(1) {}

Scene::~Scene() {
}

void Scene::Draw() {
	if (!Application::GetInstance()->isApplicationRunning) return;
	Logger::Log("Begin frame draw");
	auto renderPass = sre::RenderPass::create()
		.withCamera(*levelManager.currentLevel.camera)
		.build();
	auto spriteBatchBuilder = sre::SpriteBatch::create();
	DrawScene(spriteBatchBuilder);

	auto spriteBatch = spriteBatchBuilder.build();
	renderPass.draw(spriteBatch);
	doDebug = Application::GetInstance()->debugMode;
	if (doDebug)
	{
		physicsWorld->DrawDebugData();
		renderPass.drawLines(debugDraw.getLines());
		debugDraw.clear();
	}
	

	Logger::Log("End frame draw");
}

void Scene::PhysicsUpdate() {
	Logger::Log("Begin physics step");
	const int positionIterations = 4;
	const int velocityIterations = 12;
	physicsWorld->Step(SceneManager::GetInstance()->timeStep, velocityIterations, positionIterations);
	PhysicsStep();
	Logger::Log("End physics step");
}

void Scene::update(float deltaTime) {
	PhysicsUpdate();
	Logger::Log("Begin frame update");
	Update(deltaTime);
	Logger::Log("End frame update");
	
	if (CheckForWinState()){
		levelManager.DeleteLevel();
		levelManager.NextLevel(&Update, &DrawScene, &PhysicsStep, &KeyEvent, physicsWorld);
		for (auto gameObject : levelManager.currentLevel.levelObjects) {
			gameObject->InitialiseSelf();
		}
		Logger::Debug("Begin Initialise");
		for (auto gameObject : levelManager.currentLevel.levelObjects) {
			gameObject->Initialise();
		}
		Logger::Log("You Win");
		isLevelRunning = true;
	}
}

void Scene::End(){}

void Scene::LoadLevel(std::string LevelId) {
	Logger::Info("Loading level: " + LevelId);
	Update = Event<void(float)>();
	DrawScene = Event<void(sre::SpriteBatch::SpriteBatchBuilder&)>();
	PhysicsStep = Event<void()>();
	KeyEvent = Event<void(SDL_Event&)>();
	physicsWorld = std::make_shared<b2World>(b2Vec2(0,-9.8*10));
	physicsWorld->SetContactListener(this);
    //atlas = sre::SpriteAtlas::create("platformer-art-deluxe.json", "platformer-art-deluxe.png");
	//levelManager = *new LevelManager();
	levelManager.Start(&Update, &DrawScene, &PhysicsStep, &KeyEvent, physicsWorld,LevelId);
	//rootGameObjects = levelManager.currentLevel.levelObjects;
	physicsWorld->SetDebugDraw(&debugDraw);
	Logger::Debug("Begin InitialiseSelf");
	for (auto gameObject : levelManager.currentLevel.levelObjects) {
		gameObject->InitialiseSelf();
	}
	Logger::Debug("Begin Initialise");
	for (auto gameObject : levelManager.currentLevel.levelObjects) {
		gameObject->Initialise();
	}
	Logger::Info("Level load complete.");
}

std::shared_ptr<GameObject> Scene::Find(std::string name) {
	for (auto gameObject : levelManager.currentLevel.levelObjects) {
		if (gameObject->GetName() == name)return gameObject;
		auto childrenResult = findRecursive(gameObject, name);
		if (childrenResult != nullptr)return childrenResult;
	}
	return nullptr;
}
float Scene::GetPhysicsScale() {
	return physicsScale;
}

void Scene::BeginContact(b2Contact * contact) {
	Contact(contact, Enter);
}

void Scene::EndContact(b2Contact * contact) {
	Contact(contact, Exit);
}

void Scene::Contact(b2Contact* contact, CollisionPhase phase) {
	if (contact->GetFixtureA()->GetBody() == contact->GetFixtureB()->GetBody())return;
	auto a = contact->GetFixtureA()->GetBody()->GetUserData();
	auto b = contact->GetFixtureB()->GetBody()->GetUserData();
	auto BodyA = static_cast<PhysicsBody*>(a);
	auto BodyB = static_cast<PhysicsBody*>(b);
	BodyA->Collision(BodyB->GetOwner(), phase);
	BodyB->Collision(BodyA->GetOwner(), phase);
}

std::shared_ptr<GameObject> Scene::findRecursive(std::shared_ptr<GameObject> parent, std::string name) {
	auto children = parent->GetChildren();
	if (children.size() > 0) {
		for (auto child : children) {
			if (child->GetName() == name)return child;
			auto childrenResult = findRecursive(child, name);
			if (childrenResult != nullptr)return childrenResult;
		}
	}
	return nullptr;
}

bool Scene::CheckForWinState(){
	return !isLevelRunning;
}

void Scene::LevelOver() { isLevelRunning = false; }
