/**********************************************************************************************
    Copyright (C) 2014-2015 Oliver Eichler <oliver.eichler@gmx.de>

    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 "gis/db/IDBFolderSql.h"

#include <QtNetwork>
#include <QtSql>

#include "CMainWindow.h"
#include "gis/CGisListDB.h"
#include "gis/db/CDBFolderLostFound.h"
#include "gis/db/macros.h"
#include "helpers/CSettings.h"

IDBFolderSql::IDBFolderSql(QSqlDatabase& db, QTreeWidget* parent) : IDBFolder(false, db, eTypeDatabase, 1, parent) {
  socket = new QUdpSocket(this);
}

void IDBFolderSql::expanding() {
  IDBFolder::expanding();

  if (showLostFound()) {
    folderLostFound = new CDBFolderLostFound(db, nullptr);
    insertChild(0, folderLostFound);
  }
}

void IDBFolderSql::updateLostFound() {
  if (folderLostFound) {
    folderLostFound->update();
  }
}

bool IDBFolderSql::update() {
  QSqlQuery query(db);
  QList<IDBFolder*> dbFoldersDel;

  /* Database folders are a bit special as there are no items. But a lost & found folder.
   *
   * As there can be folders removed from the database and new folders this is a bit tricky.
   *
   * dbFoldersAdd is filled with all folders IDs attached with the database folder. Now we
   * iterate over all existing items and remove their ID from dbFoldersAdd. Additionally
   * the folder item is updated. If the update returns false, the folders was removed from
   * the database or an error occurred. In both cases the item is registered for removal in
   * dbFoldersDel.
   *
   * When done with the iteration all folders registered for removal are deleted and the
   * new ones are created. Finally lost & found is updated.
   */

  // get all folder IDs attached to this folder
  QList<quint64> dbFoldersAdd;
  query.prepare("SELECT child FROM folder2folder WHERE parent=:parent");
  query.bindValue(":parent", id);
  QUERY_EXEC(return false);
  while (query.next()) {
    dbFoldersAdd << query.value(0).toULongLong();
  }

  const int N = childCount();
  for (int i = 1; i < N; i++) {
    IDBFolder* folder = dynamic_cast<IDBFolder*>(child(i));
    if (folder) {
      dbFoldersAdd.removeAll(folder->getId());
      if (!folder->update()) {
        dbFoldersDel << folder;
      }
    }
  }

  qDeleteAll(dbFoldersDel);

  // add folders
  query.prepare(
      "SELECT t1.child, t2.type FROM folder2folder AS t1, folders AS t2 WHERE t1.parent = :id AND t2.id = t1.child "
      "ORDER BY t2.id");
  query.bindValue(":id", id);
  QUERY_EXEC(return false);
  while (query.next()) {
    quint64 idChild = query.value(0).toULongLong();
    quint32 typeChild = query.value(1).toInt();
    if (dbFoldersAdd.contains(idChild)) {
      createFolderByType(db, typeChild, idChild, this);
    }
  }

  takeChild(0);
  sortChildren(eColumn, Qt::AscendingOrder);
  insertChild(0, folderLostFound);

  updateLostFound();
  return true;
}

void IDBFolderSql::announceChange() const {
  SETTINGS;
  bool enabled = cfg.value("Database/listenUpdate", false).toBool();
  if (!enabled) {
    return;
  }
  quint16 port = cfg.value("Database/port", UDP_PORT).toUInt();

  QByteArray msg;
  QDataStream stream(&msg, QIODevice::WriteOnly);
  stream.setByteOrder(QDataStream::LittleEndian);
  stream.setVersion(QDataStream::Qt_5_2);

  quint32 tan = QRandomGenerator::global()->generate();

  stream << quint32(0);
  stream << tan;
  stream << CMainWindow::self().id;
  stream << db.driverName();
  stream << getDBName();
  stream << getDBHost();

  const QList<QNetworkInterface>& netdevices = QNetworkInterface::allInterfaces();
  for (const QNetworkInterface& netdevice : netdevices) {
    const QList<QNetworkAddressEntry>& networks = netdevice.addressEntries();
    for (const QNetworkAddressEntry& network : networks) {
      socket->writeDatagram(msg, network.broadcast(), port);
      socket->writeDatagram(msg, network.broadcast(), port);
      socket->writeDatagram(msg, network.broadcast(), port);
    }
  }
}
