text: #include "App.h"
  #include "Window.h"
  #include "Render.h"
  #include "Textures.h"
  #include "Player.h"
  #include "Map.h"
  #include "Input.h"
  
  #include "Defs.h"
  #include "Log.h"
  
  //#define VSYNC true
  #ifdef OPTICKPROFILE
  #include "optick.h"
  #endif // OPTICKPROFILE
  
  
  
  Render::Render() : Module()
  {
  	name.create("renderer");
  	background.r = 0;
  	background.g = 0;
  	background.b = 0;
  	background.a = 0;
  }
  
  // Destructor
  Render::~Render()
  {}
  
  // Called before render is available
  bool Render::Awake(pugi::xml_node& config)
  {
  	LOG("Create SDL rendering context");
  	bool ret = true;
  
  	/*texturePath.create(rendererNode.child_value("texture"));*/
  
  	Uint32 flags = SDL_RENDERER_ACCELERATED;
  	vSync = config.child("vsync").attribute("value").as_bool(true);
  	if(vSync == true)
  	{
  		flags |= SDL_RENDERER_PRESENTVSYNC;
  		LOG("Using vsync");
  	}
  
  	renderer = SDL_CreateRenderer(app->win->window, -1, flags);
  
  	if(renderer == NULL)
  	{
  		LOG("Could not create the renderer! SDL_Error: %s\n", SDL_GetError());
  		ret = false;
  	}
  	else
  	{
  		camera.w = app->win->screenSurface->w;
  		camera.h = app->win->screenSurface->h;
  		camera.x = 0;
  		camera.y = 0;
  	}
  
  	return ret;
  }
  
  // Called before the first frame
  bool Render::Start()
  {
  	LOG("render start");
  	// back background
  	SDL_RenderGetViewport(renderer, &viewport);
  	mouseText = app->tex->Load("Assets/Textures/pointer.png");
  	return true;
  }
  
  // Called each loop iteration
  bool Render::PreUpdate()
  {
  	SDL_RenderClear(renderer);
  	return true;
  }
  
  bool Render::Update(float dt)
  {
  	return true;
  }
  
  bool Render::PostUpdate()
  {
  #ifdef OPTICKPROFILE
  	OPTICK_EVENT();
  #endif // OPTICKPROFILE
  
  	//SDL_SetRenderDrawColor(renderer, background.r, background.g, background.g, background.a);
  	SDL_RenderPresent(renderer);
  	return true;
  }
  
  // Called before quitting
  bool Render::CleanUp()
  {
  	LOG("Destroying SDL render");
  	SDL_DestroyRenderer(renderer);
  	return true;
  }
  
  bool Render::Load(pugi::xml_node& data)
  {
  	camera.x = data.child("camera").attribute("x").as_int();
  	camera.y = data.child("camera").attribute("y").as_int();
  	return true;
  }
  
  bool Render::Save(pugi::xml_node& data) const
  {
  	pugi::xml_node cam = data.append_child("camera");
  
  	cam.append_attribute("x") = camera.x;
  	cam.append_attribute("y") = camera.y;
  	return true;
  }
  
  // TODO 6: Create a method to load the state
  // for now it will be camera's x and y
  
  // TODO 8: Create a method to save the state of the renderer
  // using append_child and append_attribute
  
  void Render::SetBackgroundColor(SDL_Color color)
  {
  	background = color;
  }
  
  void Render::SetViewPort(const SDL_Rect& rect)
  {
  	SDL_RenderSetViewport(renderer, &rect);
  }
  
  void Render::ResetViewPort()
  {
  	SDL_RenderSetViewport(renderer, &viewport);
  }
  
  // Blit to screen
  bool Render::DrawTexture(SDL_Texture* texture, int x, int y, const SDL_Rect* section, float speed, SDL_RendererFlip flip, bool useCamera, double angle, int pivotX, int pivotY) const
  {
  	bool ret = true;
  	uint scale = app->win->GetScale();
  
  	SDL_Rect rect;
  	if (useCamera)
  	{
  		rect.x = (int)(camera.x * speed) + x * scale;
  		rect.y = (int)(camera.y * speed) + y * scale;
  	}
  	else
  	{
  		rect.x = x;
  		rect.y = y;
  	}
  
  	if(section != NULL)
  	{
  		rect.w = section->w;
  		rect.h = section->h;
  	}
  	else
  	{
  		SDL_QueryTexture(texture, NULL, NULL, &rect.w, &rect.h);
  	}
  
  	rect.w *= scale;
  	rect.h *= scale;
  
  	SDL_Point* p = NULL;
  	SDL_Point pivot;
  
  	if(pivotX != INT_MAX && pivotY != INT_MAX)
  	{
  		pivot.x = pivotX;
  		pivot.y = pivotY;
  		p = &pivot;
  	}
  
  	if(SDL_RenderCopyEx(renderer, texture, section, &rect, angle, p, flip) != 0)
  	{
  		LOG("Cannot blit to screen. SDL_RenderCopy error: %s", SDL_GetError());
  		ret = false;
  	}
  
  	return ret;
  }
  
  bool Render::DrawRectangle(const SDL_Rect& rect, Uint8 r, Uint8 g, Uint8 b, Uint8 a, bool filled, bool use_camera) const
  {
  	bool ret = true;
  	uint scale = app->win->GetScale();
  
  	SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
  	SDL_SetRenderDrawColor(renderer, r, g, b, a);
  
  	SDL_Rect rec(rect);
  	if(use_camera)
  	{
  		rec.x = (int)(camera.x + rect.x * scale);
  		rec.y = (int)(camera.y + rect.y * scale);
  		rec.w *= scale;
  		rec.h *= scale;
  	}
  
  	int result = (filled) ? SDL_RenderFillRect(renderer, &rec) : SDL_RenderDrawRect(renderer, &rec);
  
  	if(result != 0)
  	{
  		LOG("Cannot draw quad to screen. SDL_RenderFillRect error: %s", SDL_GetError());
  		ret = false;
  	}
  
  	return ret;
  }
  
  bool Render::DrawLine(int x1, int y1, int x2, int y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a, bool use_camera) const
  {
  	bool ret = true;
  	uint scale = app->win->GetScale();
  
  	SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
  	SDL_SetRenderDrawColor(renderer, r, g, b, a);
  
  	int result = -1;
  
  	if(use_camera)
  		result = SDL_RenderDrawLine(renderer, camera.x + x1 * scale, camera.y + y1 * scale, camera.x + x2 * scale, camera.y + y2 * scale);
  	else
  		result = SDL_RenderDrawLine(renderer, x1 * scale, y1 * scale, x2 * scale, y2 * scale);
  
  	if(result != 0)
  	{
  		LOG("Cannot draw quad to screen. SDL_RenderFillRect error: %s", SDL_GetError());
  		ret = false;
  	}
  
  	return ret;
  }
  
  bool Render::DrawCircle(int x, int y, int radius, Uint8 r, Uint8 g, Uint8 b, Uint8 a, bool use_camera) const
  {
  	bool ret = true;
  	uint scale = app->win->GetScale();
  
  	SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
  	SDL_SetRenderDrawColor(renderer, r, g, b, a);
  
  	int result = -1;
  	SDL_Point points[360];
  
  	float factor = (float)M_PI / 180.0f;
  
  	for(uint i = 0; i < 360; ++i)
  	{
  		points[i].x = (int)(x + radius * cos(i * factor));
  		points[i].y = (int)(y + radius * sin(i * factor));
  	}
  
  	result = SDL_RenderDrawPoints(renderer, points, 360);
  
  	if(result != 0)
  	{
  		LOG("Cannot draw quad to screen. SDL_RenderFillRect error: %s", SDL_GetError());
  		ret = false;
  	}
  
  	return ret;
  }
  
  void Render::CameraMovement()
  {
  	if (app->player->GetPosition().x <= (Map::GetMapWidth() * Map::GetTileWidth())- (app->win->width / 2))
  	{
  		camera.x = -(app->player->GetPosition().x - camera.w / 2);
  		
  	}
  	if (camera.x > 0)
  	{
  		camera.x = 0;
  		
  	}
  
  	camera.y = -(app->player->GetPosition().y - camera.h / 2);
  	
  
  	if (camera.y > 0)
  	{
  		camera.y = 0;
  		
  	}
  }
  
  void Render::RenderMouse()
  {
  	int x;
  	int y;
  	app->input->GetMousePosition(x, y);
  	if (app->input->GetMouseButtonDown(1) == KEY_REPEAT)
  	{
  		DrawTexture(mouseText, x, y, &mouseRect2, false);
  	}
  	else
  		DrawTexture(mouseText, x, y, &mouseRect, false);
  }