#pragma once

#include <SFML/Graphics.hpp>
#include "IEntity.hpp"
#include "Collider.hpp"
#include "Log.hpp"
#include "Sand.hpp"

class Grid: public sf::Drawable, public IEntity {
public:
	Grid() = delete;
	Grid(Collider* collider, sf::RenderWindow* window, std::vector<Sand>* sand, std::vector<Spike>* spikes, Renderer* renderer):
	m_collider(collider),
	m_window(window),
	m_visible(false),
    m_sand(sand),
	m_spikes(spikes),
	m_renderer(renderer),
	m_mouseRelease(false),
	m_csand(nullptr),
	m_cspike(nullptr) {
		m_sprite.setTexture(Asset::texture("data/arrows.png"));
		setType(-1);
	}
	void reset() {
		setType(-1);
		m_csand = nullptr;
		m_cspike = nullptr;
	}
	void sendReleaseSignal() {
		m_mouseRelease = true;
	}
	void update(float delta) override {
		static sf::Vector2i prev_mouse(0, 0);
		
		sf::Vector2i pos;
		sf::Vector2i curr_mouse = sf::Mouse::getPosition(*m_window);
		pos.x = curr_mouse.x / 64.f;
		pos.y = curr_mouse.y / 64.f;
		
		int mode = m_type;

		if(m_cspike && !sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) {
			m_cspike = nullptr;
		}

		if(pos.x >= 0 && pos.x < 20 && pos.y >= 0 && pos.y < 12) {
			int block = m_collider->getBlock(pos.x, pos.y);

			if(m_cspike) {
				
			} else if(m_csand) {
                if(block == Collider::AIR) {
                    mode = 2;
                } else {
                    mode = -1;
                }
            } else if(block == Collider::SAND) {
				mode = 1;
			} else if(block == Collider::SPIKE 
			&& (m_collider->getBlock(pos.x, pos.y - 1) == Collider::LEVER 
			|| mode == 0)) { // not checking if y>1, not needed rn
				mode = 0;
			} else {
				mode = -1;
			}
		} else {
			mode = -1;
		}
		
		if(mode == 0 && sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) {
			float mouse_w = m_renderer->getPosition() - 320 + curr_mouse.x / 2;
			if(!m_cspike) {
				auto res = std::find_if(m_spikes->begin(), m_spikes->end(), [mouse_w](Spike& s) {
					return s.getPosition().x <= mouse_w && s.getPosition().x + 32.f >= mouse_w;
				});
				m_cspike = &(*res);
			}
			if(m_cspike) {
				float delta = (curr_mouse.y - prev_mouse.y) / 2.f;
				int x = (m_cspike->getPosition().x - m_renderer->getPosition() + 320.f) / 32;
				if(delta < 0) {
					if(m_collider->getBlock(x, (m_cspike->getPosition().y + delta) / 32.f) == Collider::LEVER
					|| m_collider->getBlock(x, (m_cspike->getPosition().y + delta) / 32.f) == Collider::SPIKE) {
						m_cspike->move(delta);
						m_sprite.move(0.f, delta);
					} else {
						mode = 0;
					}
				} else {
					if(m_collider->getBlock(x, (m_cspike->getPosition().y + 32.f + delta) / 32.f) == Collider::LEVER
					|| m_collider->getBlock(x, (m_cspike->getPosition().y + 32.f + delta) / 32.f) == Collider::SPIKE
                    || m_collider->getBlock(x, (m_cspike->getPosition().y + 32.f + delta) / 32.f) == Collider::AIR) {
						m_cspike->move(delta);
						m_sprite.move(0.f, delta);
					} else {
						mode = 0;
					}

                }
			}
		} else {
			for(Spike& s: *m_spikes) {
				s.release();
			}
			if(mode >= 0) {
				m_sprite.setPosition(pos.x * 32.f, pos.y * 32.f);
			}
			setType(mode);
		}
		
		if(m_mouseRelease) {
			if(mode == 1) {
				float mouse_w = m_renderer->getPosition() - 320.f + curr_mouse.x / 2.f;
				float mouse_h = curr_mouse.y / 2.f;
				auto res = std::find_if(m_sand->begin(), m_sand->end(), [mouse_w, mouse_h](Sand& s) {
					return s.getPosition().y <= mouse_h && s.getPosition().y + 32.f >= mouse_h
					&& s.getPosition().x <= mouse_w && s.getPosition().x + 32.f >= mouse_w;
				});
				if(res != m_sand->end()) {
					res->setPosition(-32.f, -32.f);
					m_csand = &(*res);
					mode = 2;
				}
			} else if(mode == 2) {
				m_csand->setPosition(m_renderer->getPosition() - 320.f + pos.x * 32.f, pos.y * 32.f);
				mode = -1;
				m_csand = nullptr;
			}
			m_mouseRelease = false;
		}

		prev_mouse = curr_mouse;
	}
private:
	void setType(int type) {
		if(type >= 0) {
			m_sprite.setTextureRect(sf::IntRect(
				type * 32,
				0,
				32,
				32
			));
			m_visible = true;
		} else {
			m_visible = false;
		}
		m_type = type;
	}
	void draw(sf::RenderTarget& rt, sf::RenderStates states) const override {
		if(m_visible) {
			rt.draw(m_sprite, states);
		}
    }
	
	Collider* m_collider;
	sf::RenderWindow* m_window;
	bool m_visible;
	std::vector<Sand>* m_sand;
	std::vector<Spike>* m_spikes;
	Renderer* m_renderer;
	sf::Sprite m_sprite;
	int m_type;
	bool m_mouseRelease;
	Sand* m_csand;
	Spike* m_cspike;
};
