#include "WorldPrimitives.h"

#include <algorithm>
#include <cmath>

#include <QPainter>
#include <QPen>
#include <QPolygonF>
#include <QRectF>
#include <QVector2D>

namespace
{
    // Ship triangle proportions, as fractions of a tile: the nose reaches further
    // than the tail corners spread, so the facing direction reads at a glance.
    const float kShipForwardTileFactor = 0.45f;
    const float kShipSideTileFactor    = 0.25f;

    const float kDebrisRadiusTileFactor = 0.2f;

    // Health bar height as a fraction of a tile.
    const qreal kHealthBarHeightTileFactor = 0.12;

    const QColor kHealthBarTrack  (60, 60, 60);
    const QColor kHealthBarEnemy  (200, 60, 60);
    const QColor kHealthBarPlayer (60, 200, 60);

    const QColor kDebrisFill   (128, 110, 90);
    const QColor kDebrisOutline(50, 40, 30);

    // Sensor circles are drawn faint so a crowded field stays readable.
    const int kSensorRangeAlpha = 77;
}

float getShipForwardExtentPx(const WorldCoordinates& coordinates)
{
    return coordinates.getTilePx() * kShipForwardTileFactor;
}

float getDebrisRadiusPx(const WorldCoordinates& coordinates)
{
    return coordinates.getTilePx() * kDebrisRadiusTileFactor;
}

void drawShipBody(QPainter& painter, const WorldCoordinates& coordinates,
                  QPointF center, float facing_radians,
                  const QColor& fill, const QColor& outline)
{
    const QVector2D direction(std::cos(facing_radians), std::sin(facing_radians));
    const QVector2D perpendicular(-direction.y(), direction.x());

    const float forward = getShipForwardExtentPx(coordinates);
    const float side    = coordinates.getTilePx() * kShipSideTileFactor;

    QPolygonF triangle;
    triangle
        << QPointF(center.x() + static_cast<qreal>(direction.x() * forward),
                   center.y() + static_cast<qreal>(direction.y() * forward))
        << QPointF(center.x() + static_cast<qreal>(perpendicular.x() * side - direction.x() * side),
                   center.y() + static_cast<qreal>(perpendicular.y() * side - direction.y() * side))
        << QPointF(center.x() + static_cast<qreal>(-perpendicular.x() * side - direction.x() * side),
                   center.y() + static_cast<qreal>(-perpendicular.y() * side - direction.y() * side));

    painter.setPen(QPen(outline, 1));
    painter.setBrush(fill);
    painter.drawPolygon(triangle);
}

void drawHealthBar(QPainter& painter, const WorldCoordinates& coordinates,
                   qreal left, qreal top, qreal width, float fraction, bool isEnemy)
{
    const qreal height  = static_cast<qreal>(coordinates.getTilePx())
                        * kHealthBarHeightTileFactor;
    const float clamped = std::max(0.0f, fraction);

    painter.fillRect(QRectF(left, top, width, height), kHealthBarTrack);
    painter.fillRect(QRectF(left, top, width * static_cast<qreal>(clamped), height),
                     isEnemy ? kHealthBarEnemy : kHealthBarPlayer);
}

void drawDebrisMarker(QPainter& painter, const WorldCoordinates& coordinates,
                      QPointF center)
{
    const qreal radius = static_cast<qreal>(getDebrisRadiusPx(coordinates));
    painter.setBrush(kDebrisFill);
    painter.setPen(QPen(kDebrisOutline, 1));
    painter.drawEllipse(center, radius, radius);
}

void drawSensorRange(QPainter& painter, const WorldCoordinates& coordinates,
                     QPointF center, float range_tiles, const QColor& shipOutline)
{
    const qreal radius = static_cast<qreal>(range_tiles)
                       * static_cast<qreal>(coordinates.getTilePx());
    QColor circleColor = shipOutline;
    circleColor.setAlpha(kSensorRangeAlpha);

    painter.setPen(QPen(circleColor, 1));
    painter.setBrush(Qt::NoBrush);
    painter.drawEllipse(center, radius, radius);
}
