#include "Core.hpp"
#include "Log.hpp"
#include "Animation.hpp"
#include "Asset.hpp"

Core::Core(RenderWindow* window):
m_state(SPLASH),
m_score(0),
m_running(true),  
m_paused(false), 
m_collider(),
m_renderer(window),
m_window(window),   
m_player(&m_collider),
m_blockLayer("data/map.json", "data/tileset.png"),
m_grid(&m_collider, window, &m_sand, &m_spikes, &m_renderer),
m_victory(false) {
	m_map.load("data/map.tmx"); 
 
	m_background.setTexture(Asset::texture("data/basic_background.png"));
	m_renderer.registerDrawable(&m_background, 0, Renderer::Mode::BG);
      
	for(unsigned i = 0; i < LAYER_COUNT; ++i) {
    	m_layer[i].create(m_map, i);
		m_renderer.registerDrawable(&m_layer[i], i, Renderer::Mode::FG);
	}  
	m_renderer.registerDrawable(&m_blockLayer, 10, Renderer::Mode::FG);
	m_renderer.registerDrawable(&m_player, 1000, Renderer::Mode::FG);
	m_renderer.registerDrawable(&m_grid, 100000, Renderer::Mode::UI);
	
	m_splash.setTexture(Asset::texture("data/splash3.png"));
	m_renderer.registerDrawable(&m_splash, 101000, Renderer::Mode::UI);
	
	m_renderer.setFocus(19); 
	m_renderer.sort(); 
	m_fruit.reserve(100);

    reset();
	//m_collider.populate(m_blockLayer.rebuildCollider(m_renderer.getFocus()));
	//m_collider.setLocalX(m_renderer.getPosition() - 320.f);
	 
	registerEntity(&m_renderer);
	registerEntity(&m_player);
	registerEntity(&m_grid);
	
	Asset::sound("data/intro1.ogg");
	Asset::sound("data/intro2.ogg");
	Asset::sound("data/loop1.ogg");
	Asset::sound("data/loop2.ogg");
	
	reset();
}
  
Core::~Core() {

} 

score_t Core::getScore() {
	return m_score;
}

bool Core::hasQuit() {
	return m_running;
}

void Core::run() { 
	sf::Clock clock; 
	float delta;
	float minimum = 1.f / 30.f;
	Log(INFO, "Launching core.");
	while(m_running) {
		events();
		delta = clock.restart().asSeconds();
		while(delta > minimum) {
			delta -= minimum;
			update(minimum);
		}
		update(delta);
		m_renderer.render();
	}
	Log(INFO, "Closing core.");
}

void Core::registerEntity(IEntity* entity) {
	m_entities.push_back(entity);
}

void Core::deregisterEntity(IEntity* entity) {
	m_entities.erase(std::remove_if(m_entities.begin(), m_entities.end(), [entity](IEntity* elem){ 
		return elem == entity;
	}), m_entities.end());
}

void Core::events() {
    sf::Event event;
	static sf::Clock headpat_clock;
	static int headpats = 0; 
    while(m_window->pollEvent(event)) {
		if(event.type == sf::Event::MouseButtonPressed) {//Released) {
			if(m_state == SPLASH) {
				static bool sploosh = false;
				if(sploosh) {
					m_state = INGAME;
					m_music.setBuffer(Asset::sound("data/intro1.ogg"));
					m_music.setLoop(false);
					m_music.setRelativeToListener(true);
					m_music.setVolume(40.f);
					m_music.play();
					m_global_clock.restart();
				} else {
					sploosh = true;
					m_splash.setTexture(Asset::texture("data/splash.png"));
				}
			} else {
				m_grid.sendReleaseSignal();
			}
		}
        if(event.type == sf::Event::Closed || (event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::Escape)) {
            m_running = false;
		} else if(event.type == sf::Event::GainedFocus) {
			m_paused = false;
		} else if(event.type == sf::Event::LostFocus) {
			m_paused = true;
		} else if(event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::P) {
			m_paused = !m_paused;
		} else if(event.type == sf::Event::MouseButtonReleased && m_player.isDead()) {
			sf::Vector2i pos1 = sf::Mouse::getPosition(*m_window);
			pos1.x /= 2;
			pos1.x += m_renderer.getPosition() - 320;
			pos1.y /= 2;
			sf::Vector2i pos2 = (sf::Vector2i)m_player.getPosition();
			pos2.x += 21;
			pos2.y += 13;
			if(std::abs(pos1.x - pos2.x) < 10 && std::abs(pos1.y - pos2.y) < 8) {
				headpat_clock.restart();
				headpats++;
			}
			if(!(headpats <3)) {
				/*m_collider.populate(m_blockLayer.rebuildCollider(m_renderer.getFocus()));
				m_collider.setLocalX(m_renderer.getPosition() - 320.f);
				m_grid.reset();
				m_player.setPosition(((m_renderer.getFocus() + 1) * 20) * 32.f - 32.f, 32.f);
				m_player.revive();*/
                reset();
			}
		}
    }
	if(headpat_clock.getElapsedTime().asSeconds() > 1.f) {
		headpat_clock.restart();
		headpats = 0;
	}
}

void Core::update(float delta) {
	if(!m_victory && !m_paused && m_state == INGAME) {
		if(m_interlude_clock.getElapsedTime().asSeconds() > 1.f) {
			m_collider.update(delta);
			for(Sand& s: m_sand) {
				s.update(delta);
			}
			for(Spike& s: m_spikes) {
				s.update(delta);
			}
			for(Fruit& s: m_fruit) {
				s.update(delta);
			}
			for(IEntity* ie: m_entities) {
				ie->update(delta);
			}
		} else {
			m_grid.update(delta);
			m_renderer.update(delta);
		}  
		   
		if(m_renderer.getPosition() - m_player.getPosition().x > 320.f) {
            m_renderer.setFocus(m_renderer.getFocus() - 1);
			if(m_renderer.getFocus() == 7) {
				m_music.setBuffer(Asset::sound("data/intro2.ogg"));
				m_music.setLoop(false);
				m_music.play();
				m_player.turboBoost();
			}
			reset();
		}
		
		int room = m_renderer.getFocus();
		float delay = room > 8 ? 1.f : room == 3 ? 0.33f : 0.5f;
		if(room != 1 && room != 2 && room != 9 && room != 8 && room != 17 && room != 19 && m_fruit_clock.getElapsedTime().asSeconds() > delay) {
			for(int i = 0; i < 18; ++i) {
				if(m_fruit.size() < 100 && m_collider.getBlock(i, 0) == Collider::AIR && rand() % 20 == 0) {
					m_fruit.emplace_back(&m_collider);
					m_fruit.back().setPosition(m_renderer.getPosition() - 320.f + i * 32.f, 0.f);
					m_fruit.back().setTexture(Asset::texture("data/owocek.png"));
					m_renderer.registerDrawable(&(m_fruit.back()), 1000, Renderer::Mode::FG);
				}
			}
			m_renderer.sort();
			m_fruit_clock.restart();
		}
		
		static float color = 255.f;
		if(room == 1) {
			if(color == -100.f) {
				color = 0.f;
				std::ostringstream oss;
				m_victory_text.setFont(Asset::font("data/Scratch.ttf"));
				m_victory_text.setCharacterSize(62);
				oss << "You win! IT is safe\nTime: ";
				oss << m_global_clock.getElapsedTime().asSeconds() << "s\n";
				oss << "Thanks for playing";
				m_victory_text.setString(oss.str());
				m_victory_text.setOrigin(m_victory_text.getLocalBounds().width / 2.f, m_victory_text.getLocalBounds().height / 2.f);
				m_victory_text.setPosition(320.f, 180.f);
				m_splash.setTexture(Asset::texture("data/splash2.png"));
				m_renderer.registerDrawable(&m_splash, 101000, Renderer::Mode::UI);
				m_renderer.registerDrawable(&m_victory_text, 101001, Renderer::Mode::UI);
				m_renderer.sort();
			}
			color += delta * 255.f / 3.f;
			m_victory_text.setFillColor(sf::Color(255, 255, 255, color));
			m_splash.setColor(sf::Color(255, 255, 255, color));
			if(color >= 255.f) {
				m_splash.setColor(sf::Color::White);
				m_victory = true;
			}
		} else if(color != -100.f) {
			color -= delta * 255.f;
			if(color > 0.f) {
				m_splash.setColor(sf::Color(255, 255, 255, color));
			} else if(color != -100.f) {
				m_renderer.deregisterDrawable(&m_splash);
				color = -100.f;
			}
		}
	}
	
	if(m_renderer.getFocus() > 9) {
		if(m_music.getBuffer() && m_music.getStatus() == sf::Sound::Stopped) {
			m_music.setBuffer(Asset::sound("data/loop1.ogg"));
			m_music.setLoop(true);
			m_music.play();
		}
	} else if(m_renderer.getFocus() > 7) {
		float vol = m_music.getVolume();
		if(vol > 0.f) {
			if(vol - delta * 5.f > 0.f) {
				m_music.setVolume(vol - delta * 5.f);
			} else {
				m_music.stop();
			}
		}
	} else {
		float vol = m_music.getVolume();
		if(vol < 40.f) {
			if(vol + delta * 20.f < 40.f) {
				m_music.setVolume(vol + delta * 20.f);
			} else {
				m_music.setVolume(40.f);
			}
		}
		if(m_music.getBuffer() && m_music.getStatus() == sf::Sound::Stopped) {
			m_music.setBuffer(Asset::sound("data/loop2.ogg"));
			m_music.setLoop(true);
			m_music.play();
		}
	}
}

void Core::reset() {
    m_collider.populate(m_blockLayer.rebuildCollider(m_renderer.getFocus()));
    for(Spike& s: m_spikes) {
        m_renderer.deregisterDrawable(&s); 
    }  
    for(Sand& s: m_sand) { 
        m_renderer.deregisterDrawable(&s);
    }
	for(Fruit& s: m_fruit) { 
        m_renderer.deregisterDrawable(&s);
    }
    m_spikes.clear();
    m_sand.clear();
	m_fruit.clear();
    m_spikes = m_blockLayer.reloadSpikes(m_renderer.getFocus(), &m_collider);
    m_sand = m_blockLayer.reloadSand(m_renderer.getFocus(), &m_collider);
	m_collider.setLocalX(m_renderer.getPosition() - 320.f);
	for(Sand& s: m_sand) {
        m_renderer.registerDrawable(&s, 500, Renderer::Mode::FG);
		s.update(0.f);
    }
    for(Spike& s: m_spikes) {
        m_renderer.registerDrawable(&s, 500, Renderer::Mode::FG);
		s.registerColl();
    }
    m_renderer.sort();
    m_grid.reset();
    m_player.setPosition(((m_renderer.getFocus() + 1) * 20) * 32.f - 32.f, 0.f);
    m_player.revive();
	m_interlude_clock.restart();
}
