/**********************************************************************************************
    Copyright (C) 2023 Gunnar Skjold <gunnar.skjold@gmail.com>

    This program 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.

    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

**********************************************************************************************/

#include "realtime/ais/CRtAis.h"

#include <QJsonDocument>
#include <QtNetwork>
#include <QtWidgets>

#include "helpers/CDraw.h"
#include "realtime/CRtDraw.h"
#include "realtime/ais/CRtAisInfo.h"

const QString CRtAis::strIcon("://icons/48x48/ActShip.png");

CRtAis::CRtAis(QTreeWidget* parent) : IRtSource(eTypeAis, true, parent) {
  setIcon(eColumnIcon, QIcon(strIcon));
  setText(eColumnName, "AIS");
  setCheckState(eColumnCheckBox, Qt::Checked);

  CRtAis::registerWithTreeWidget();
}

void CRtAis::registerWithTreeWidget() {
  QTreeWidget* tree = treeWidget();
  if (tree != nullptr) {
    QTreeWidgetItem* itemInfo = new QTreeWidgetItem(this);
    itemInfo->setFlags(Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
    info = new CRtAisInfo(*this, tree);
    connect(info, &CRtAisInfo::sigChanged, this, &CRtAis::sigChanged);
    tree->setItemWidget(itemInfo, eColumnWidget, info);
    emit sigChanged();
  }
}

void CRtAis::loadSettings(QSettings& cfg) {
  QMutexLocker lock(&IRtSource::mutex);

  IRtSource::loadSettings(cfg);
  showNames = cfg.value("showNames", showNames).toBool();

  if (info != nullptr) {
    info->loadSettings(cfg);
  }

  emit sigChanged();
}

void CRtAis::saveSettings(QSettings& cfg) const {
  QMutexLocker lock(&IRtSource::mutex);

  IRtSource::saveSettings(cfg);
  cfg.setValue("showNames", showNames);

  if (info != nullptr) {
    info->saveSettings(cfg);
  }
}

QString CRtAis::getDescription() const {
  return tr(
      "<b>AIS</b><br/>"
      "Get vessel positions via AIS over TCP/IP");
}

qint32 CRtAis::getNumberOfShips() const {
  QMutexLocker lock(&IRtSource::mutex);
  return ships.count();
}

bool CRtAis::getShowNames() const {
  QMutexLocker lock(&IRtSource::mutex);
  return showNames;
}

CRtAis::ship_t& CRtAis::getShipByMmsi(const QString& mmsi) {
  QMutexLocker lock(&IRtSource::mutex);
  if (!ships.contains(mmsi)) {
    ship_t ship;
    ship.mmsi = mmsi;
    ships[mmsi] = ship;
  }

  return ships[mmsi];
}

bool CRtAis::hasShip(const QString& key) { return ships.contains(key); }

void CRtAis::drawItem(QPainter& p, const QPolygonF& viewport, QList<QRectF>& blockedAreas, CRtDraw* rt) {
  if (checkState(eColumnCheckBox) != Qt::Checked) {
    return;
  }

  QPolygonF tmp2 = viewport;
  rt->convertRad2Px(tmp2);

  QFontMetrics fm(p.font());

  p.setPen(Qt::yellow);
  p.setBrush(Qt::yellow);
  QPixmap iconShip("://icons/16x16/Ship.png");
  QPixmap iconAid("://icons/16x16/Aid.png");
  QRect rectIconShip = iconShip.rect();
  rectIconShip.moveCenter(QPoint(0, 0));
  QRect rectIconAid = iconAid.rect();
  rectIconAid.moveCenter(QPoint(0, 0));

  QDateTime now = QDateTime::currentDateTime();

  const QStringList& keys = ships.keys();
  for (const QString& key : keys) {
    ship_t& ship = ships[key];

    if (!ship.aid) {
      QDateTime lim = QDateTime::fromSecsSinceEpoch(ship.timePosition).addSecs(900);
      if (lim < now) {
        ships.remove(key);
        continue;
      }
    }

    ship.point = ship.pos * DEG_TO_RAD;
    rt->convertRad2Px(ship.point);

    if (!tmp2.boundingRect().contains(ship.point)) {
      continue;
    }

    p.save();
    p.translate(ship.point);
    p.rotate(ship.heading);
    p.drawPixmap(ship.heading >= 0 ? rectIconShip : rectIconAid, ship.heading >= 0 ? iconShip : iconAid);
    p.restore();

    if (showNames) {
      QString name = ship.name.isEmpty() ? ship.mmsi.isEmpty() ? tr("unkn.") : ship.mmsi : ship.name;
      QRect rectLabel = fm.boundingRect(name);
      rectLabel.moveCenter(ship.point.toPoint() + QPoint(0, -8));
      rectLabel.adjust(-1, -1, 1, 1);
      if (!CDraw::doesOverlap(blockedAreas, rectLabel)) {
        CDraw::text(name, p, rectLabel.center(), Qt::darkBlue);
        blockedAreas << rectLabel;
      }
    }
  }

  if (info != nullptr) {
    info->draw(p, viewport, blockedAreas, rt);
  }
}

void CRtAis::fastDraw(QPainter& p, const QRectF& viewport, CRtDraw* rt) {
  if (!keyFocus.isEmpty() && ships.contains(keyFocus)) {
    p.save();

    const ship_t& ship = ships[keyFocus];
    p.setPen(Qt::red);
    p.setBrush(Qt::NoBrush);
    p.drawEllipse(ship.point, 10, 10);

    QString text;
    text += "<table>";
    if (!ship.name.isEmpty()) text += "<tr><td>" + tr("name:") + "</td><td>" + ship.name + "</td></tr>";
    if (!ship.type.isEmpty()) text += "<tr><td>" + tr("type:") + "</td><td>" + ship.type + "</td></tr>";
    if (!ship.callsign.isEmpty()) text += "<tr><td>" + tr("callsign:") + "</td><td>" + ship.callsign + "</td></tr>";
    if (!ship.imo.isEmpty()) text += "<tr><td>" + tr("imo:") + "</td><td>" + ship.imo + "</td></tr>";
    if (!ship.mmsi.isEmpty()) text += "<tr><td>" + tr("mmsi:") + "</td><td>" + ship.mmsi + "</td></tr>";
    text += "<tr><td>" + tr("last contact:") + "</td><td>" + QDateTime::fromSecsSinceEpoch(ship.timePosition).toString() +
            "</td></tr>";
    text += "<tr><td>" + tr("longitude:") + "</td><td>" + QString::number(ship.longitude) + "°</td></tr>";
    text += "<tr><td>" + tr("latitude:") + "</td><td>" + QString::number(ship.latitude) + "°</td></tr>";
    if (ship.velocity >= 0.1)
      text += "<tr><td>" + tr("velocity:") + "</td><td>" + QString::number(ship.velocity) + "knots</td></tr>";
    if (ship.heading >= 0 && ship.heading <= 360)
      text += "<tr><td>" + tr("heading:") + "</td><td>" + QString::number(ship.heading) + "°</td></tr>";
    if (!ship.destination.isEmpty())
      text += "<tr><td>" + tr("destination:") + "</td><td>" + ship.destination + "</td></tr>";
    if (ship.length > 0)
      text += "<tr><td>" + tr("length:") + "</td><td>" + QString::number(ship.length) + "m</td></tr>";
    if (ship.width > 0) text += "<tr><td>" + tr("width:") + "</td><td>" + QString::number(ship.width) + "m</td></tr>";
    if (ship.draught > 0)
      text += "<tr><td>" + tr("draught:") + "</td><td>" + QString::number(ship.draught) + "m</td></tr>";
    text += "</table>";

    QTextDocument doc;
    doc.setHtml(text);
    doc.setTextWidth(300);
    QRectF rectText(QPointF(0, 0), doc.size());

    rectText.moveTopLeft(ship.point + QPointF(32, 0));
    QRectF rectFrame = rectText.adjusted(-5, -5, 5, 5);

    p.setPen(CDraw::penBorderGray);
    p.setBrush(CDraw::brushBackWhite);
    PAINT_ROUNDED_RECT(p, rectFrame);

    p.translate(rectText.topLeft());
    p.setPen(Qt::black);
    doc.drawContents(&p);

    p.restore();
  }
}

void CRtAis::mouseMove(const QPointF& pos) {
  if (checkState(eColumnCheckBox) != Qt::Checked) {
    return;
  }

  QMutexLocker lock(&IRtSource::mutex);

  keyFocus.clear();
  const QStringList& keys = ships.keys();
  for (const QString& key : keys) {
    const ship_t& ship = ships[key];
    if ((ship.point - pos).manhattanLength() < 20) {
      keyFocus = key;
      break;
    }
  }
}

void CRtAis::slotSetShowNames(bool yes) {
  QMutexLocker lock(&IRtSource::mutex);
  showNames = yes;
  emit sigChanged();
}
