#include "Snake.h"
#include "Fruit.h"
#include "World.h"
#include <SDL3/SDL.h>

TSnake::TSnake()
	: m_Size(TileSize)
{
	m_SnakeBody.emplace_back(5, 5);
	m_Direction = EDirection::Right;

	m_DestinationRect.w = static_cast<float>(m_Size);
	m_DestinationRect.h = static_cast<float>(m_Size);
}

void TSnake::Update(float DeltaTime)
{
	if (m_SnakeBody.empty() || m_Direction == EDirection::None)
	{
		return;
	}

	m_MoveDistance += DeltaTime * m_Speed;
	if (m_MoveDistance >= 1.0f)
	{
		Move();
		m_MoveDistance -= 1.0f;
	}
}

void TSnake::Move()
{
	TSnakeSegment Head = m_SnakeBody.front();

	if (m_GrowBy > 0)
	{
		m_SnakeBody.emplace_back(0, 0);
		m_GrowBy -= 1;
	}


	TSnakeSegment Tail = m_SnakeBody.back();
	Tail.m_Position.X = Head.m_Position.X;
	Tail.m_Position.Y = Head.m_Position.Y;
	m_SnakeBody.pop_back();
	m_SnakeBody.push_front(Tail);

	auto HeadIter = m_SnakeBody.begin();

	switch (m_Direction)
	{
	case EDirection::Left:
		--HeadIter->m_Position.X;
		break;
	case EDirection::Right:
		++HeadIter->m_Position.X;
		break;
	case EDirection::Up:
		--HeadIter->m_Position.Y;
		break;
	case EDirection::Down:
		++HeadIter->m_Position.Y;
		break;
	default:
		break;
	}

	if (TFruit* Fruit = GameObject.World->GetFruitAtPosition(HeadIter->m_Position.X, HeadIter->m_Position.Y))
	{
		GrowBy(Fruit->GetGrowByValue());
		Fruit->SetRandomPosition();
	}
}

void TSnake::Render(SDL_Renderer* Renderer)
{
	if (m_SnakeBody.empty())
	{
		return;
	}
	EDirection PrevDirection, NextSegmentDirection;
	SDL_FRect SourceRect;

	for (auto SegmentIter = m_SnakeBody.begin(); SegmentIter != m_SnakeBody.end(); ++SegmentIter)
	{
		m_DestinationRect.x = static_cast<float>(SegmentIter->m_Position.X * m_Size);
		m_DestinationRect.y = static_cast<float>(SegmentIter->m_Position.Y * m_Size);
		m_DestinationRect.w = TileSize;
		m_DestinationRect.h = TileSize;

		NextSegmentDirection = GetNextSegmentDirection(SegmentIter);
		if (SegmentIter == m_SnakeBody.begin())
		{
			EDirection PhysicalDirection = GetSnakePhysicalDirection();
			switch (PhysicalDirection)
			{
			case EDirection::Left:
				SourceRect = HeadLeftRect;
				break;
			case EDirection::Right:
				SourceRect = HeadRightRect;
				break;
			case EDirection::Up:
				SourceRect = HeadUpRect;
				break;
			case EDirection::Down:
				SourceRect = HeadDownRect;
				break;
			}
			SDL_RenderTexture(Renderer, GameObject.TileMap, &SourceRect, &m_DestinationRect);
			PrevDirection = GetOppositeDirection(PhysicalDirection);
		}
		else if (NextSegmentDirection == EDirection::None)
		{
			switch (GetOppositeDirection(PrevDirection))
			{
			case EDirection::Left:
				SourceRect = SegmentTailLeftRect;
				break;
			case EDirection::Right:
				SourceRect = SegmentTailRightRect;
				break;
			case EDirection::Up:
				SourceRect = SegmentTailUpRect;
				break;
			case EDirection::Down:
				SourceRect = SegmentTailDownRect;
				break;
			}
			SDL_RenderTexture(Renderer, GameObject.TileMap, &SourceRect, &m_DestinationRect);
		}
		else
		{
			EDirection DirectionTowardHead = GetOppositeDirection(PrevDirection);

			if ((DirectionTowardHead == EDirection::Up && NextSegmentDirection == EDirection::Down) || (DirectionTowardHead == EDirection::Down && NextSegmentDirection == EDirection::Up))
			{
				SourceRect = SegmentUpDownRect;
			}
			else if ((DirectionTowardHead == EDirection::Left && NextSegmentDirection == EDirection::Right) || (DirectionTowardHead == EDirection::Right && NextSegmentDirection == EDirection::Left))
			{
				SourceRect = SegmentLeftRightRect;
			}
			else if ((DirectionTowardHead == EDirection::Left && NextSegmentDirection == EDirection::Up) || (DirectionTowardHead == EDirection::Up && NextSegmentDirection == EDirection::Left))
			{
				SourceRect = SegmentLeftUpRect;
			}
			else if ((DirectionTowardHead == EDirection::Right && NextSegmentDirection == EDirection::Up) || (DirectionTowardHead == EDirection::Up && NextSegmentDirection == EDirection::Right))
			{
				SourceRect = SegmentRightUpRect;
			}
			else if ((DirectionTowardHead == EDirection::Left && NextSegmentDirection == EDirection::Down) || (DirectionTowardHead == EDirection::Down && NextSegmentDirection == EDirection::Left))
			{
				SourceRect = SegmentLeftDownRect;
			}
			else if ((DirectionTowardHead == EDirection::Right && NextSegmentDirection == EDirection::Down) || (DirectionTowardHead == EDirection::Down && NextSegmentDirection == EDirection::Right))
			{
				SourceRect = SegmentRightDownRect;
			}
			SDL_RenderTexture(Renderer, GameObject.TileMap, &SourceRect, &m_DestinationRect);
			PrevDirection = NextSegmentDirection;
		}
	}
}

void TSnake::SetDirection(EDirection Direction)
{
	EDirection PhysicalDirection = GetSnakePhysicalDirection();
	if (Direction == EDirection::Right && PhysicalDirection == EDirection::Left) return;
	if (Direction == EDirection::Left && PhysicalDirection == EDirection::Right) return;
	if (Direction == EDirection::Up && PhysicalDirection == EDirection::Down) return;
	if (Direction == EDirection::Down && PhysicalDirection == EDirection::Up) return;
	m_Direction = Direction;
}

EDirection TSnake::GetOppositeDirection(EDirection Direction)
{
	switch (Direction)
	{
	case EDirection::Left:
		return EDirection::Right;
	case EDirection::Right:
		return EDirection::Left;
	case EDirection::Up:
		return EDirection::Down;
	case EDirection::Down:
		return EDirection::Up;
	case EDirection::None:
		return EDirection::None;
	}
	return EDirection::None;
}

EDirection TSnake::GetNextSegmentDirection(std::list<TSnakeSegment>::iterator CurrentSegment)
{
	std::list<TSnakeSegment>::iterator NextSegment = CurrentSegment;
	++NextSegment;
	if (NextSegment == m_SnakeBody.end())
	{
		return EDirection::None;
	}

	if (CurrentSegment->m_Position.X == NextSegment->m_Position.X)
	{
		if (NextSegment->m_Position.Y > CurrentSegment->m_Position.Y) return EDirection::Down;
		else  return EDirection::Up;
	}
	else if (CurrentSegment->m_Position.Y == NextSegment->m_Position.Y)
	{
		if (NextSegment->m_Position.X > CurrentSegment->m_Position.X) return EDirection::Right;
		else  return EDirection::Left;
	}

	return EDirection::None;
}

SDL_AppResult TSnake::HandleEvent(SDL_Event* event)
{
	switch (event->type)
	{
	case SDL_EVENT_QUIT:
		return SDL_APP_SUCCESS;
	case SDL_EVENT_FINGER_DOWN:
	{
		int x = static_cast<int>(event->tfinger.x * GameObject.WindowWidth);
		int y = static_cast<int>(event->tfinger.y * GameObject.WindowHeight);
		const int Margin = 400;
		if ((x < Margin && y < Margin) || (x >= (GameObject.WindowWidth - Margin) && y < Margin))
		{
			SetDirection(EDirection::Up);
			return SDL_APP_CONTINUE;
		}
		else if ((x < Margin && y >= GameObject.WindowHeight - Margin) || (x >= (GameObject.WindowWidth - Margin) && y >= GameObject.WindowHeight - Margin))
		{
			SetDirection(EDirection::Down);
			return SDL_APP_CONTINUE;
		}
		else if (x < Margin)
		{
			SetDirection(EDirection::Left);
			return SDL_APP_CONTINUE;
		}
		else if (x >= (GameObject.WindowWidth - Margin))
		{
			SetDirection(EDirection::Right);
			return SDL_APP_CONTINUE;
		}
		return SDL_APP_SUCCESS;
	}
	case SDL_EVENT_KEY_DOWN:
		switch (event->key.key)
		{
		case SDLK_UP:
			SetDirection(EDirection::Up);
			break;
		case SDLK_DOWN:
			SetDirection(EDirection::Down);
			break;
		case SDLK_LEFT:
			SetDirection(EDirection::Left);
			break;
		case SDLK_RIGHT:
			SetDirection(EDirection::Right);
			break;
		}
	}

	return SDL_APP_CONTINUE;
}

bool TSnake::IsOccupiedBySegment(int X, int Y)
{
	for (auto SegmentIter = m_SnakeBody.begin(); SegmentIter != m_SnakeBody.end(); ++SegmentIter)
	{
		if (SegmentIter->m_Position.X == X && SegmentIter->m_Position.Y == Y)
		{
			return true;
		}
	}

	return false;
}
