/*
	Copyright 2016, 2017 Michele "King_DuckZ" Santullo

	This file is part of MyCurry.

	MyCurry is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	MyCurry is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with MyCurry.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "character.hpp"
#include "inputbag.hpp"
#include "gameactions.hpp"
#include "worldgrid.hpp"
#include "grid_raytrace.hpp"
#include <ciso646>
#include <cassert>
#include <cstdint>

namespace curry {
	Character::Character (cloonel::InputBag* parInputBag, const WorldSizeNotifiable::DeferredRegister& parDeferredRegister) :
		WorldSizeNotifiable(parDeferredRegister),
		m_position(vec2f(0.0f), vec2f(0.0f), vec2f(0.0f)),
		m_width_height(0.0f),
		m_input_bag(parInputBag),
		m_world(parDeferredRegister.world()),
		m_speed(1.0f)
	{
		assert(m_input_bag);
		assert(m_world);
	}

	void Character::load (const char* parTexturePath, cloonel::SDLMain& parSDLMain) {
		const auto old_wh = m_width_height;
		m_texture.load(parTexturePath, parSDLMain);
		m_width_height = vector_cast<vec2f>(m_texture.width_height());
		const auto& new_wh = m_width_height;
		if (m_position.get_min() != m_position.get_max())
			m_position.change_minmax(m_position.get_min(), m_position.get_max() + old_wh - new_wh);
	}

	void Character::unload() {
		m_texture.unload();
	}

	void Character::set_position (const vec2f& parPos) {
		m_position = parPos;
	}

	const vec2f& Character::position() const {
		return m_position.get();
	}

	const vec2f& Character::width_height() const {
		return m_width_height;
	}

	Texture& Character::texture() {
		return m_texture;
	}

	Rect<float> Character::destination_rect (const vec2f& parWorldPos) const {
		return Rect<float>(
			m_position.get() - parWorldPos,
			m_position.get() - parWorldPos + width_height()
		);
	}

	Rect<float> Character::source_rect() const {
		return Rect<float>(
			vec2f(0.0f),
			width_height()
		);
	}

	void Character::world_size_changed (const vec2us&, const vec2i& parPixelSize) {
		m_position.change_minmax(m_position.get_min(), vector_cast<vec2f>(parPixelSize));
	}

	void Character::do_movement (float parDeltaT) {
		const float speed = parDeltaT * m_speed;
		vec2f offset(0.0f);
		if (cloonel::IsPressed(*m_input_bag, ActionRight))
			offset.x() += speed;
		if (cloonel::IsPressed(*m_input_bag, ActionLeft))
			offset.x() -= speed;
		if (cloonel::IsPressed(*m_input_bag, ActionUp))
			offset.y() -= speed;
		if (cloonel::IsPressed(*m_input_bag, ActionDown))
			offset.y() += speed;

		const auto old_pos = this->position();
		const auto new_pos = old_pos + offset;

		this->set_position(new_pos);
		if (position() == old_pos)
			return;

		vec2us last_valid_pos(0xFFFF);

		auto is_walkable = [](const WorldTileProperty& wtp) {
			return wtp.property->walkable;
		};

		for_each_voxel_under_segment(old_pos, this->position(), m_texture.width_height(), *m_world, is_walkable);
		//for (auto tile_vec : crossed_tiles(old_pos, this->position(), m_world->tile_size())) {
		//	const TileIndex* const tile = m_world->tile(tile_vec);
		//	for (uint16_t z = 0; z < m_world->layer_count(); ++z) {
		//		const auto& tile_prop = m_world->tile_property(tile + z);
		//		if (not tile_prop.walkable) {
		//			assert(last_valid_pos != vec2us(0xFFFF));
		//			this->set_position(vector_cast<vec2f>(last_valid_pos));
		//		}
		//		else {
		//			last_valid_pos = tile_vec;
		//		}
		//	}
		//}
	}

	void Character::set_speed (float parSpeed) {
		assert(parSpeed > 0.0f);
		m_speed = parSpeed;
	}
} //namespace curry
