#pragma once

#include <SFML/Graphics.hpp>
#include <imgui-SFML.h>
#include <imgui.h>

#include "Random.h"



template<typename T, typename U, typename V>
inline T getRandomNumber(U min, V max)
{
	return static_cast<T>(Random::get(min, max));
}

template<typename T, typename U, typename V, typename W>
inline sf::Color createColor(T r, U g, V b, W a)
{
	return sf::Color(static_cast<std::uint8_t>(r), static_cast<std::uint8_t>(g), static_cast<std::uint8_t>(b), static_cast<std::uint8_t>(a));
}

template<typename T, typename U, typename V>
inline sf::Color createColor(T r, U g, V b, std::uint8_t a = 255u)
{
	return sf::Color(static_cast<std::uint8_t>(r), static_cast<std::uint8_t>(g), static_cast<std::uint8_t>(b), a);
}

inline sf::Color getRandomColor()
{
	return sf::Color(getRandomNumber<std::uint8_t>(0u, 255u), getRandomNumber<std::uint8_t>(0u, 255u), getRandomNumber<std::uint8_t>(0u, 255u));
}

template<typename T>
inline T signage(T in)
{
	T zero{};
	if (in > zero) return ++zero;
	if (in < zero) return --zero;
	return zero;
}

struct LineSegment
{
  sf::Vector2f startPoint{};

  sf::Vector2f endPoint{};
};

struct ImguiColor
{
	ImguiColor() = default;
	ImguiColor(float r_in, float g_in, float b_in, float a_in)
		: r(r_in)
		, g(g_in)
		, b(b_in)
		, a(a_in)
	{ }

  float r{};
  float g{};
  float b{};
  float a{ 1.f };

  sf::Color asSfColor()
  {
    return sf::Color(std::uint8_t(r * 255.f), std::uint8_t(g * 255.f), std::uint8_t(b * 255.f), std::uint8_t(a * 255.f));
  }
};

inline ImguiColor constructImguiColor(sf::Color color)
{
  return ImguiColor((float)color.r / 255.f, (float)color.g / 255.f, (float)color.b / 255.f, (float)color.a / 255.f);
}

inline std::optional<sf::Vector2f> lineIntersect(LineSegment first, LineSegment second)
{
  sf::Vector2f firstVector = (first.endPoint - first.startPoint);
  sf::Vector2f secondVector = (second.endPoint - second.startPoint);

  float lengthCrossProduct = firstVector.cross(secondVector);

  sf::Vector2f fms = second.startPoint - first.startPoint; //first line start point minus second

  float firstScalar = fms.cross(secondVector) / lengthCrossProduct;
  float secondScalar = fms.cross(firstVector) / lengthCrossProduct;

  if ((firstScalar >= 0 and firstScalar <= 1) and (secondScalar >= 0 and secondScalar <= 1))
    return sf::Vector2f(first.startPoint.x + (firstScalar * firstVector.x), first.startPoint.y + (firstScalar * firstVector.y));
  else
    return std::nullopt;
}

inline sf::VertexArray constructRectangle(sf::Vector2f center, sf::Vector2f size, sf::Color color = sf::Color::Black)
{
  sf::VertexArray rectangle(sf::PrimitiveType::LineStrip, 5);
  rectangle[0].color = color;
  rectangle[1].color = color;
  rectangle[2].color = color;
  rectangle[3].color = color;
  rectangle[4].color = color;

  rectangle[0].position = { center.x - size.x, center.y - size.y };
  rectangle[1].position = { center.x + size.x, center.y - size.y };
  rectangle[2].position = { center.x + size.x, center.y + size.y };
  rectangle[3].position = { center.x - size.x, center.y + size.y };
  rectangle[4].position = rectangle[0].position;

  return rectangle;
}

inline sf::Vector2f constructVector(sf::Vector2f startPoint, sf::Angle angle, float length)
{
  return { startPoint.x + (length * std::cos(angle.asRadians())), startPoint.y + (length * std::sin(angle.asRadians())) };
}

inline sf::VertexArray constructRandomPolygon(sf::Vector2f center, int vertexLowerBound, int vertexUpperBound, int averageSize, sf::Color color = sf::Color::Black)
{
  const std::size_t vertexCount = getRandomNumber<std::size_t>(vertexLowerBound, vertexUpperBound);
  sf::VertexArray polygon(sf::PrimitiveType::LineStrip, vertexCount);

  const float angleIncrement = 360.f / (float)(vertexCount - 1);
  const float angleStart = getRandomNumber<float>(0, 360);

  for (int i = 0; i < vertexCount - 1; i++)
  {
    float angleOffset = getRandomNumber<float>(-(90 / (int)vertexCount), (90 / (int)vertexCount));
    polygon[i].color = color;
    polygon[i].position = constructVector(center, sf::degrees(angleStart + (angleIncrement * i) + angleOffset), getRandomNumber<float>((averageSize - 50), (averageSize + 50)));
  }
  polygon[vertexCount - 1].position = polygon[0].position;
  polygon[vertexCount - 1].color = color;
  return polygon;
}

inline float distanceBetween(sf::Vector2f startPoint, sf::Vector2f endPoint)
{
  return sqrtf(powf(endPoint.x - startPoint.x, 2) + powf(endPoint.y - startPoint.y, 2));
}

inline sf::VertexArray constructVertexArray(sf::Shape& shape)
{
  sf::VertexArray result(sf::PrimitiveType::LineStrip);
  auto& transform = shape.getTransform();

  for (int i = 0; i < shape.getPointCount(); i++)
  {
    result.append(sf::Vertex{ transform.transformPoint(shape.getPoint(i)), sf::Color::Black });
  }

  result.append(result[0]);
  return result;
}