/*
	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 "ingamescene.hpp"
#include "sdlmain.hpp"
#include "mycurry_toplevelConfig.h"
#include "key.hpp"
#include "inputbag.hpp"
#include "movingobject.hpp"
#include "worldgrid.hpp"
#include "worldviewport.hpp"
#include "texture.hpp"
#include "rect.hpp"
#include "character.hpp"
#include "csvloader.hpp"
#include "worlditems.hpp"
#include "gameactions.hpp"
#include <SDL2/SDL.h>
#include <algorithm>
#include <vector>
#include <fstream>
#include <boost/utility/string_ref.hpp>
#include <boost/lexical_cast.hpp>
#include <cassert>

namespace curry {
	namespace {
	} //unnamed namespace

	struct IngameScene::LocalData {
		LocalData (const vec2us& parTileSize, cloonel::SDLMain* parSDLMain, cloonel::InputBag* parInputBag) :
			world(parTileSize),
			viewport(&world, vec2f(parSDLMain->WidthHeight()), WorldSizeNotifiable::DeferredRegister(&world, &viewport)),
			player(parTileSize),
			character(parInputBag, WorldSizeNotifiable::DeferredRegister(&world, &character))
		{
		}

		WorldGrid world;
		WorldViewport viewport;
		Texture worldtiles;
		MovingObject player;
		Character character;
		WorldItems moveable_items;
	};

	IngameScene::IngameScene (cloonel::SDLMain* parSDLMain) :
		GameSceneBase(parSDLMain),
		m_local_data(std::make_unique<LocalData>(vec2us(32), parSDLMain, &this->input_bag()))
	{
		using cloonel::Key;
		using cloonel::InputDevice_Keyboard;

		auto& inp = this->input_bag();
		inp.AddAction(ActionLeft, Key(InputDevice_Keyboard, SDL_SCANCODE_LEFT), "Move left");
		inp.AddAction(ActionUp, Key(InputDevice_Keyboard, SDL_SCANCODE_UP), "Move up");
		inp.AddAction(ActionRight, Key(InputDevice_Keyboard, SDL_SCANCODE_RIGHT), "Move right");
		inp.AddAction(ActionDown, Key(InputDevice_Keyboard, SDL_SCANCODE_DOWN), "Move down");
	}

	IngameScene::~IngameScene() noexcept = default;

	void IngameScene::on_prepare() {
		m_local_data->worldtiles.load(RESOURCES_PATH "nonfree_texture.png", *this->sdl_main(), RGBA{255, 255, 255, 0});
		CSVJointData tiles = load_csv({
			RESOURCES_PATH "nonfree_map_ground.csv",
			RESOURCES_PATH "nonfree_map_transparent.csv"
		});

		vec2us world_size(tiles.width, tiles.height);
		m_local_data->world.set_layers(world_size, tiles.tables);
		m_local_data->world.set_tile_properties(tiles.tile_properties);
#if !defined(NDEBUG)
		std::cout << "World size set to " << world_size << '\n';
#endif

		m_local_data->viewport.allow_overscan(false);
		m_local_data->character.load(RESOURCES_PATH "LPC_Sara_Preview.png", *this->sdl_main());
		const float speed_per_sec =
			static_cast<float>(
				std::max(m_local_data->world.tile_size().x(), m_local_data->world.tile_size().y())
			) * 4.5f;
		m_local_data->character.set_speed(speed_per_sec);

		assert(m_local_data->moveable_items.no_items_registered());
		m_local_data->moveable_items.register_observer(&m_local_data->character);
	}

	void IngameScene::on_update (float parDeltaT) {
		const auto& world = m_local_data->world;

		auto& viewport = m_local_data->viewport;
		m_local_data->moveable_items.move_items(parDeltaT);
		viewport.set_position(m_local_data->character.position() - (viewport.size() - m_local_data->character.width_height()) / 2.0f);

		const auto tilesize(static_cast<vec2f>(world.tile_size()));
		const auto tilecount(m_local_data->worldtiles.width_height() / world.tile_size());
		for (auto tile : viewport) {
			for (uint16_t z = 0; z < m_local_data->world.layer_count(); ++z) {
				if (tile.index[z] < 0)
					continue;
				vec2f idx(static_cast<float>(tile.index[z] % tilecount.x()), static_cast<float>(tile.index[z] / tilecount.x()));
				vec2f src_rect_xy = idx * tilesize;
				vec2f pixel_pos = vector_cast<vec2f>(tile.pixel_pos);

				Rect<float> src_rect(src_rect_xy, src_rect_xy + tilesize);
				Rect<float> dst_rect(pixel_pos, pixel_pos + tilesize);
				this->draw_clipped(m_local_data->worldtiles, src_rect, dst_rect);
			}
		}
		this->draw_clipped(m_local_data->character.texture(), m_local_data->character.source_rect(), m_local_data->character.destination_rect(viewport.position()));
	}

	void IngameScene::on_destroy() noexcept {
		m_local_data->moveable_items.unregister_all();
		m_local_data->worldtiles.unload();
		m_local_data->character.unload();
	}
} //namespace curry
