/***************************************************************************
                          world.cpp  -  description
                             -------------------
    begin                : sept 2011
    copyright            : (C) 2011 by Jaime Robles
    email                : jaime@robles.es
 ***************************************************************************/

/*****************************************************************************
 * This file is part of KLog.                                             *
 *                                                                           *
 *    KLog 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.                                    *
 *                                                                           *
 *    KLog 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 KLog.  If not, see <https://www.gnu.org/licenses/>.       *
 *                                                                           *
 *****************************************************************************/

#include "world.h"
#include "callsign.h"
/*
 QHash<QString, int> worldPrefixes;
To insert a (key, value) pair into the hash, you can use operator[]():
 hash["EA"] = 130;
 hash["EA6"] = 131;
 hash["EA8"] = 132;

*/
World::World(DataProxy_SQLite *dp, const QString &_parentFunction)
{
    Q_UNUSED(_parentFunction);
   //qDebug() << Q_FUNC_INFO << " - Start from: " << _parentFunction;
    worldPrefixes.clear();
    cqz = -1;
    ituz = -1;
    ret = false;
    continentId = -1;
    lat = 0.0;
    lon = 0.0;
    utc = 0.0;
    locator = new Locator();
    created = false;    
    dataProxy = dp;
    read = readWorld();
    util = new Utilities(Q_FUNC_INFO);
   //qDebug() << Q_FUNC_INFO << " - END";
}

World::~World()
{
   //qDebug() << Q_FUNC_INFO;
    delete(locator);
    delete(util);
}

bool World::readEntities()
{
    //qDebug() << Q_FUNC_INFO << " - Start";
    entities.clear();
    entities = dataProxy->getAllEntiNameISOAndPrefix();
    if (entities.count()<1)
        return false;

    // qMapIterator<EntityData, int> i(entities);  // Just test, to see if we are doing ok
    //while (i.hasNext()) {
    //    i.next();
    //  //qDebug() << " - " << i.key().name;
    //}
    //qDebug() << Q_FUNC_INFO << " - END" ;
    return true;
}

// Function to get EntityData based on dxcc ID
EntityData World::getEntityDataFromDXCC(const int _dxcc)
{
   //qDebug() << Q_FUNC_INFO << ": " << QString::number(_dxcc);
    for (auto it = entities.constBegin(); it != entities.constEnd(); ++it) {
        if (it.value() == _dxcc) {
            return it.key();
        }
    }
    // If not found, return a default EntityData object
    return EntityData();
}

QString World::getEntityMainPrefix(int _dxcc)
{
   //qDebug() << Q_FUNC_INFO << ": " << _dxcc;

    EntityData entity = getEntityDataFromDXCC(_dxcc);
   //qDebug() << Q_FUNC_INFO << " - 10";
    Callsign prefix(entity.mainprefix);
   //qDebug() << Q_FUNC_INFO << " - 11";
    QString aux = QString();
   //qDebug() << Q_FUNC_INFO << " - 11";
   //qDebug() << Q_FUNC_INFO << ": prefix: " << prefix.getHostFullPrefix() ;
    if (prefix.isValidPrefix())
        aux = entity.mainprefix;
    return aux;
}

bool World::readWorld()
{ // Used to link a prefix with an Entity quickly, without quering the DB.
   //qDebug() << Q_FUNC_INFO << " - Start";
    specialCalls.clear();
    longPrefixes.clear();
    worldPrefixes.clear();

    worldPrefixes = dataProxy->getWorldData();
    //worldPrefixes = dataProxy->getHashTableData(WorldData);
    specialCalls << dataProxy->getSpecialCallsigns();
    longPrefixes << dataProxy->getLongPrefixes();

   //qDebug() << Q_FUNC_INFO << " - worldPrefixes: " << worldPrefixes.count();
   //qDebug() << Q_FUNC_INFO << " - specialCalls : " << specialCalls.count();
   //qDebug() << Q_FUNC_INFO << " - longPrefixes : " << longPrefixes.count();
    if (!readEntities())
        return false;
    if (worldPrefixes.isEmpty())
        return false;
    if (specialCalls.isEmpty())
        return false;
    return !longPrefixes.isEmpty();
}

bool World::recreate(const QString &_worldFile)
{
   //qDebug() << Q_FUNC_INFO << ": " << _worldFile;
    QSqlQuery query;
      // Delete from entity table
      if (!executeQuery(query, "DELETE FROM entity")) {
        emitQueryError(query);
        return false;
      }

      // Delete from prefixesofentity table
      if (!executeQuery(query, "DELETE FROM prefixesofentity")) {
        emitQueryError(query);
        return false;
      }

    // Create the world file
    //qDebug() << Q_FUNC_INFO << " - END-4";
    return create(_worldFile);
}

bool World::create(const QString &_worldFile)
{
   //qDebug() << Q_FUNC_INFO << "  " << _worldFile;

    created = readCTYCSV(_worldFile);
   //qDebug() << Q_FUNC_INFO << " - created: " << util->boolToQString(created) ;
   //qDebug() << Q_FUNC_INFO << " - 30" ;
    if (created)
    {
       //qDebug() << Q_FUNC_INFO << " - 40" ;
        created = insertSpecialEntities();
       //qDebug() << Q_FUNC_INFO << " - created: " << util->boolToQString(created) ;
    }
    if (created)
    {
       //qDebug() << Q_FUNC_INFO << " - 50" ;
        if (dataProxy->updateISONames())
        {            
       //qDebug() << Q_FUNC_INFO << " - 60" ;
          //qDebug() << Q_FUNC_INFO << "  updateISONames TRUE" ;
        }
        else
        {
          //qDebug() << Q_FUNC_INFO << " - 70" ;
          //qDebug() << Q_FUNC_INFO << "  updateISONames FALSE" ;
        }
      //qDebug() << Q_FUNC_INFO << " - 80" ;
    }
    if (created)
    { // Let's add the Primary Subdivisions to the DB
     //qDebug() << Q_FUNC_INFO << " - 81" ;
        created = dataProxy->addPrimarySubdivisions();
    //qDebug() << Q_FUNC_INFO << " - created: " << util->boolToQString(created) ;
    }
  //qDebug() << Q_FUNC_INFO << " - 90" ;
    read = readWorld ();
  //qDebug() << Q_FUNC_INFO << " - read   : " << util->boolToQString(read) ;
  //qDebug() << Q_FUNC_INFO << " - created: " << util->boolToQString(created) ;
  //qDebug() << Q_FUNC_INFO << " - END" ;
    return created;
}


QStringList World::readZones (const QString &pref, const int _cq, const int _itu)
{
    //Returns a QStringList: prefix, CQz, ITUz
   //qDebug() << Q_FUNC_INFO << ":  (" << pref << "/" << QString::number(_cq) <<"/" << QString::number(_itu)<< ")";
    QStringList result;
    int cq = _cq;
    int itu = _itu;
    QString azone; // Special zones start with a [
    QString aux = pref;

    if(aux.count('[')==1) // Check if has special CQz
    {
         //qDebug() << Q_FUNC_INFO << ": DETECTED [ !!!!";
        qsizetype pos = aux.indexOf('[');
        azone = aux.sliced(pos, 1);
        //Following line was migrated from qt5
        //azone = (aux.midRef(aux.indexOf('[')+1)).toString();

         //qDebug() << Q_FUNC_INFO << ": (ITU)-1: " << aux << " right of " << QString::number(aux.indexOf('[')) << " = " << azone;
        itu = (azone.left(azone.indexOf(']'))).toInt();

         //qDebug() << Q_FUNC_INFO << ": (ITU)-2: " << azone.left(azone.indexOf(']'));
        aux = aux.left(aux.indexOf('['));
         //qDebug() << Q_FUNC_INFO << ": (ITU): "  << pref << "/" << QString::number(itu) << "/" << aux;
    }

    if(aux.count('(')==1) // Check if has special CQz
    {
         //qDebug() << Q_FUNC_INFO << ": DETECTED ( !!!!";
        qsizetype pos = aux.indexOf('(');
        azone = aux.sliced(pos, 1);
         //Following line was migrated from qt5
        //azone = (aux.midRef(aux.indexOf('(')+1)).toString();
        cq = (azone.left(azone.indexOf(')'))).toInt();
        aux = aux.left(aux.indexOf('('));
         //qDebug() << Q_FUNC_INFO << ": (CQ): "  << pref << "/" << QString::number(cq) << "/" << aux;
    }
     //qDebug() << Q_FUNC_INFO << ": (Pref/CQ/ITU): "  << pref << "= " << aux <<"/" << QString::number(cq) << "/" << QString::number(itu);

    result << aux << QString::number(cq) << QString::number(itu);
     //qDebug() << Q_FUNC_INFO << ": (Pref/CQ/ITU): " << result;
    return result;
}



QString World::getQRZEntityName(const QString &_qrz)
{
   //qDebug() << Q_FUNC_INFO << ": " << _qrz;
    if (_qrz.length() < 1 )
    {
        return QString();
    }
    int prefixIDNumber = getQRZARRLId(_qrz);
    //qDebug() << Q_FUNC_INFO << " - " << prefixIDNumber;
    return getEntityName(prefixIDNumber);
}

QString World::getEntityName(const int _entityN)
{
   //qDebug() << Q_FUNC_INFO << ": " << _entityN;
    int prefixIDNumber = _entityN;
    if (prefixIDNumber<=0)
    {
        return QString();
    }
    return dataProxy->getEntityNameFromId(prefixIDNumber);
}

int World::getQRZCqz(const QString &_qrz)
{
    //qDebug() << Q_FUNC_INFO << ": " << _qrz;
     if (_qrz.length() < 1 )
     {
         return -1;
     }

    Callsign callsign(_qrz);
    QString aux = callsign.getHostPrefix();
   // QString aux = util->getPrefixFromCall(_qrz);
    return dataProxy->getCQzFromPrefix(aux);
}

int World::getQRZItuz(const QString &_qrz)
{
   //qDebug() << Q_FUNC_INFO << ": " << _qrz;
    if (_qrz.length() < 1 )
    {
        return -1;
    }
    Callsign callsign(_qrz);
    QString aux = callsign.getHostPrefix();
    // qString aux = util->getPrefixFromCall(_qrz);
    return dataProxy->getITUzFromPrefix(aux);
}

int World::getEntityCqz(const int _enti)
{
   //qDebug() << Q_FUNC_INFO << ": " <<  _enti;
    if (_enti < 1 )
    {
        return -1;
    }
    return dataProxy->getCQzFromEntity(_enti);
}

int World::getEntityItuz(const int _enti)
{
   //qDebug() << Q_FUNC_INFO << ": " <<  _enti;
    if (_enti < 1 )
    {
        return -1;
    }
    return dataProxy->getITUzFromEntity(_enti);
}

int World::getQRZARRLId(const QString &_qrz)
{
   //qDebug() << Q_FUNC_INFO << ": " << _qrz;
    QString  call = _qrz.toUpper();
    Callsign callsign(call);
    if (!callsign.isValidPrefix())
        return -1;
    // Lets look for the full call to identify special callsigns
    int entID = worldPrefixes.value(call, -1);
    if (entID > 0)
        return entID;

    QString prefix = callsign.getHostFullPrefix();
   //qDebug() << Q_FUNC_INFO << " - prefix: " << prefix;
    entID = worldPrefixes.value(prefix, -2);
    while ((prefix.length()>1) && (entID<=0))
    {
       //qDebug() << Q_FUNC_INFO << " - " << QString("Pref: %1 / EntID: %2").arg(prefix).arg(entID);
        prefix.chop(1);
        entID = worldPrefixes.value(prefix, -3);
       //qDebug() << Q_FUNC_INFO << " - " << QString("New Pref: %1 / New EntID: %2").arg(prefix).arg(entID);
    }
   //qDebug() << Q_FUNC_INFO << " - " << QString("Callsign: %1 / Final EntID: %2").arg(_qrz).arg(entID);
    return entID;
}

QString World::getQRZEntityMainPrefix(const QString &_qrz)
{
   //qDebug() << Q_FUNC_INFO << ": " << _qrz;
    if (_qrz.length() < 1 )
    {
        return "";
    }
    int i = getQRZARRLId(_qrz);

    return getEntityMainPrefix(i);
}

bool World::isNewCQz(const int _cqz)
{
   //qDebug() << Q_FUNC_INFO << ": " <<  _cqz;
    return dataProxy->isNewCQz(_cqz);
}

bool World::isNewEntity(const int _entityN)
{
   //qDebug() << Q_FUNC_INFO << ": " <<  _entityN;
    if (_entityN <= 0)
    {
        return false;
    }
    return dataProxy->isNewEntity(_entityN);
}

QString World::getQRZContinentShortName(const QString &_qrz)
{
   //qDebug() << Q_FUNC_INFO << ": " << _qrz;
    return getContinentShortName (getQRZARRLId(_qrz));
}

QString World::getContinentShortName(const int _enti)
{
   //qDebug() << Q_FUNC_INFO << ": " <<  _enti;
    if ( _enti < 0 )
    {
        return "--";
    }
    QString a = dataProxy->getContinentShortNameFromEntity(_enti);
    if (a.length()!=2)
    {
        return "--";
    }
    else
    {
        return a;
    }
}

QString World::getQRZContinentNumber(const QString &_qrz)
{
   //qDebug() << Q_FUNC_INFO << ": " << _qrz;
    int i = getQRZARRLId(_qrz);
    return QString::number(getContinentNumber(i));
}

int World::getContinentNumber(const int _enti)
{
   //qDebug() << Q_FUNC_INFO << ": " <<  _enti;
    if (_enti <= 0)
    {
        return -1;
    }
    return dataProxy->getContinentIdFromEntity(_enti);
}

double World::getLongitude(const int _enti)
{
   //qDebug() << Q_FUNC_INFO << ": " <<  _enti;
    if (_enti <= 0)
    {
        return 0.0;
    }
    return dataProxy->getLongitudeFromEntity(_enti);
}

double World::getLatitude(const int _enti)
{
   //qDebug() << Q_FUNC_INFO << ": " <<  _enti;
    if (_enti <= 0)
    {
        return 0.0;
    }
    return dataProxy->getLatitudeFromEntity(_enti);
}

int World::selectEntity(const int _ent1, const int _ent2)
{ // Check for I(248) vs IT9(2248) cases
   //qDebug() << QString("Entity-1: %1, Entity-2: %2").arg(_ent1).arg(_ent2);
    // Easy case: Same igual, no issue
    if (_ent1 == _ent2 && _ent1 > 0)
        return _ent1;

    // Case I(248) vs IT9(2248)
    int higher = std::max(_ent1, _ent2);
    int lower  = std::min(_ent1, _ent2);
    if (higher % 1000 == lower)
        return lower;

    return -1;
}


QString World::getQRZLocator(const QString &_qrz)
{
   //qDebug() << Q_FUNC_INFO << " - Start: " << _qrz;
    Callsign callsign(_qrz);
    if (!callsign.isValidPrefix())
        return QString();

    int entity = getQRZARRLId (_qrz);
   //qDebug() << Q_FUNC_INFO << " - Entity: " << entity;
    return locator->getLocator(getLongitude(entity), getLatitude (entity));
}

QString World::getLocator(const int _entityN)
{
   //qDebug() << Q_FUNC_INFO << ": " <<  _entityN;
    if (_entityN <= 0)
    {
        return "";
    }
    return locator->getLocator(getLongitude(_entityN), getLatitude(_entityN));
}

bool World::addEntity(const QString &_name, const int _cq, const int _itu, const int _contId, const double _lat, const double _lon, const double _utc, const int _dxcc, const QString &_mainpref)
{
   //qDebug() << Q_FUNC_INFO << " - Start";

    QSqlQuery query;

    query.prepare(QStringLiteral("INSERT INTO entity (name, cqz, ituz, continent, latitude, longitude, utc, dxcc, mainprefix) "
                  "VALUES (:name, :cqz, :ituz, :continent, :latitude, :longitude, :utc, :dxcc, :mainprefix)"));

    query.bindValue(QStringLiteral(":name"), _name); // name
    query.bindValue(QStringLiteral(":cqz"), _cq); // CQ
    query.bindValue(QStringLiteral(":ituz"), _itu); // ITU
    query.bindValue(QStringLiteral(":continent"), _contId); // Cont
    query.bindValue(QStringLiteral(":latitude"), _lat); // Lat
    query.bindValue(QStringLiteral(":longitude"), _lon); // Lon
    query.bindValue(QStringLiteral(":utc"), _utc); // UTC
    query.bindValue(QStringLiteral(":dxcc"), _dxcc); // dxcc
    query.bindValue(QStringLiteral(":mainprefix"), _mainpref); // Mainprefix

     //qDebug()  << Q_FUNC_INFO << " Entity name: " << _name;
     //qDebug()  << Q_FUNC_INFO << " Entity cqz:  " << QString::number(_cq);
     //qDebug()  << Q_FUNC_INFO << " Entity ituz: " << QString::number(_itu);
     //qDebug()  << Q_FUNC_INFO << " Entity cont: " << QString::number(_contId);
     //qDebug()  << Q_FUNC_INFO << " Entity lat:  " << QString::number(_lat);
     //qDebug()  << Q_FUNC_INFO << " Entity lon:  " << QString::number(_lon);
     //qDebug()  << Q_FUNC_INFO << " Entity UTC:  " << QString::number(_utc);
     //qDebug()  << Q_FUNC_INFO << " Entity ARRL: " << QString::number(_dxcc);
     //qDebug()  << Q_FUNC_INFO << " Entity Pref: " << _mainpref;

    if (query.exec())
    {
         //qDebug() << Q_FUNC_INFO << "  Entity data added OK: " <<  _name ;
        return true;
    }
    //else if (query.lastError().text() == QString::number(19))
    //{
         //qDebug() << Q_FUNC_INFO << "  Entity data NOT added: error19:  " <<  _name ;
    //}
    //else
    //{
         //qDebug() << Q_FUNC_INFO << "  Entity data NOT added: error else:  " <<  _name ;
        //emit queryError(Q_FUNC_INFO, query.lastError().databaseText(), query.lastError().text(), query.lastQuery());
         //qDebug() << Q_FUNC_INFO << " Entity data NOT added" ;
         //qDebug() << Q_FUNC_INFO << " LastQuery: " << query.lastQuery() ;
         //qDebug() << Q_FUNC_INFO << " LastError-data: " << query.lastError().databaseText() ;
         //qDebug() << Q_FUNC_INFO << " LastError-driver: " << query.lastError().driverText() ;
         //qDebug() << Q_FUNC_INFO << " LastError-n: " << query.lastError().text();
    //}
    emit queryError(Q_FUNC_INFO, query.lastError().databaseText(), query.lastError().text(), query.lastQuery());
    return false;
}

bool World::addPrefix(const QString &_pref, const int _dxcc, const int _cqz, const int _ituz)
{
   //qDebug() << Q_FUNC_INFO << ": _pref: " << _pref;
     //qDebug() << Q_FUNC_INFO << ": _dxcc: " << QString::number(_dxcc);
     //qDebug() << Q_FUNC_INFO << ": _cqz: " << QString::number(_cqz);
     //qDebug() << Q_FUNC_INFO << ": _ituz: " << QString::number(_ituz);
    QSqlQuery query;
    query.prepare(QStringLiteral("INSERT INTO prefixesofentity (prefix, dxcc, cqz, ituz) VALUES (:pref, :dxcc, :cqz, :ituz)"));

    query.bindValue(QStringLiteral(":pref"), _pref); // Cont
    query.bindValue(QStringLiteral(":dxcc"), _dxcc); // Cont
    query.bindValue(QStringLiteral(":cqz"), _cqz); // CQ
    query.bindValue(QStringLiteral(":ituz"), _ituz); // ITU
    if (query.exec())
    {
         //qDebug() << Q_FUNC_INFO << "  Prefix data added OK: " <<  _pref ;
         //qDebug() << Q_FUNC_INFO << "  query: " <<  query.executedQuery();
         //qDebug() << Q_FUNC_INFO << "  query: " <<  query.lastQuery();
        return true;
    }
    else if ((query.lastError().nativeErrorCode()).toInt() == 2067)
    //else if (query.lastError().text() == QString::number(19))
    {
        //#pragma "ERROR code 19 should be 2067 to indicate duplicate"
         //qDebug() << Q_FUNC_INFO << "  :Prefix data NOT added: error19:  " <<  _pref ;
    }
    else
    {
         //qDebug() << Q_FUNC_INFO << "  Pref data NOT added: error else:  " <<  _pref ;
         //qDebug() << Q_FUNC_INFO << " LastQuery: " << query.lastQuery() ;
         //qDebug() << Q_FUNC_INFO << " LastError-data: " << query.lastError().databaseText() ;
         //qDebug() << Q_FUNC_INFO << " LastError-driver: " << query.lastError().driverText() ;
         //qDebug() << Q_FUNC_INFO << " LastError-n: " << query.lastError().text();
    }
    emit queryError(Q_FUNC_INFO, query.lastError().databaseText(), query.lastError().text(), query.lastQuery());
    return false;
}

bool World::readCTYCSV(const QString &_worldFile)
{
   //qDebug() << Q_FUNC_INFO << ": " <<  _worldFile;
    QFile file(_worldFile);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
      //qDebug() << Q_FUNC_INFO << ": File not found: END FALSE" << _worldFile;
        return false;
    }
    QStringList existingPrefixes;
    QList<int> existingDXCCs;

  //qDebug() << Q_FUNC_INFO << "File found:" << _worldFile;

    QTextStream in(&file);
    int numberOfLines = 0;

    // First pass: count lines
    while (!in.atEnd()) {
        in.readLine();
        numberOfLines++;
    }

  //qDebug() << Q_FUNC_INFO << "Number of lines:" << numberOfLines;

    // Reset the file position
    file.seek(0);
    in.seek(0);

// Initialize progress dialog
#ifndef KLOG_TESTING
    QProgressDialog progress(tr("Reading cty.csv..."), tr("Abort reading"), 0, numberOfLines);
    progress.setWindowModality(Qt::ApplicationModal);
#endif

    int progressBarPosition = 0;
    QStringList stringList, stringListPrefixes;

    while (!in.atEnd()) {
#ifndef KLOG_TESTING
        progress.setValue(progressBarPosition);
        if (progress.wasCanceled())
            break;
#endif

        progressBarPosition++;
        stringList.clear();
        stringListPrefixes.clear();

        QString line = in.readLine().simplified().trimmed();
        line.remove(QChar(';'), Qt::CaseInsensitive);
        stringList = line.split(',');

        if (stringList.size() != 10) {
            continue;
        }
        // Si el indicativo ya existe, no se debe nuscar más
        //    Aqui se añaden de forma indefinida... no se valida el indicativo, solo el numero y entonces se va añadiendo varias veces el mismo indicativo
        //    habría que, además del numero, verificar el prefijo primero


        QString mPrefix = stringList.at(0);
        if (existingPrefixes.contains(mPrefix))
        {
           //qDebug() << Q_FUNC_INFO << ": Existing prefix: " << mPrefix;
            continue;
        }


        int _entityNumber = stringList.at(2).toInt();

        if (mPrefix.contains(QChar('*'), Qt::CaseInsensitive))
        {
            _entityNumber += 1000;
            // Loop until we find an unused entity number with an empty main prefix
            while (existingDXCCs.contains(_entityNumber))
            {
                _entityNumber += 1000;
            }
            existingDXCCs.append(_entityNumber);
        }

        QString prefixesToAdd = QString(); // To capture 3Y/b, FT/t prefixes
        if (mPrefix.contains(QChar('/'), Qt::CaseInsensitive))
        {
            prefixesToAdd = mPrefix.toUpper();
        }

        QString entName = stringList.at(1);
        int contId = dataProxy->getContinentIdFromContinentShortName(stringList.at(3));
        int cqz = stringList.at(4).toInt();
        int ituz = stringList.at(5).toInt();
        double lat = stringList.at(6).toDouble();
        double lon = stringList.at(7).toDouble();
        double utc = stringList.at(8).toDouble();

       //qDebug() << Q_FUNC_INFO << ": NEW prefix: " << mPrefix;
        existingPrefixes.append(mPrefix);

        prefixesToAdd = prefixesToAdd + " " + stringList.at(9);
        if (addEntity(entName, cqz, ituz, contId, lat, lon, utc, _entityNumber, mPrefix)) {
          //qDebug() << Q_FUNC_INFO << "Entity added:" << entName;
            // stringList.at(9) contains an space separated list of prefixes for that entity
            addPrefixes(prefixesToAdd.trimmed(), _entityNumber, cqz, ituz);             //TODO: Handle the error
        } else {
          //qDebug() << Q_FUNC_INFO << "Entity not added:" << entName;
        }

#ifndef KLOG_TESTING
        progress.setLabelText("Reading cty.csv ... \nNow reading " + mPrefix + " data");
#endif
      //qDebug() << Q_FUNC_INFO << "Progress bar position:" << progressBarPosition;
    }

#ifndef KLOG_TESTING
    progress.setValue(numberOfLines);
#endif

    if (created) {
        dataProxy->updateISONames();
    }

    return true;
}


bool World::addPrefixes(const QString &prefixes, int entityNumber, int cqz, int ituz)
{
   //qDebug() << Q_FUNC_INFO << " - Start";
    QStringList stringListPrefixes = prefixes.split(' ');

    QList<QPair<QString, QPair<int, QPair<int, int>>>> pairPrefixes;
    //pairPrefixes.append(qMakePair(QString("EA"), qMakePair(130, qMakePair(10, 20))));


    for (const QString &prefix : stringListPrefixes) {
        QStringList stringListProcessedPrefix = readZones(prefix, cqz, ituz);
        if (stringListProcessedPrefix.size() == 3)
        {
            //TODO: Add some checks to ensure we are adding a prefix and 3 numbers

            //Returns a QStringList: prefix, CQz, ITUz
            pairPrefixes.append(qMakePair(stringListProcessedPrefix.at(0), qMakePair(entityNumber, qMakePair(stringListProcessedPrefix.at(1).toInt(), stringListProcessedPrefix.at(2).toInt()))));
            //if (addPrefix(stringListProcessedPrefix.at(0), entityNumber, stringListProcessedPrefix.at(1).toInt(), stringListProcessedPrefix.at(2).toInt())) {
            //  //qDebug() << Q_FUNC_INFO << "Prefix added:" << stringListProcessedPrefix.at(0);
            //} else {
            //  //qDebug() << Q_FUNC_INFO << "Prefix not added:" << stringListProcessedPrefix.at(0);
            //}
        }
    }
    return insertPrefixes(pairPrefixes);
}

bool World::insertPrefixes(const QList<QPair<QString, QPair<int, QPair<int, int>>>> &pairPrefixes)
{
   //qDebug() << Q_FUNC_INFO << " - Start ";
    QSqlQuery query;
    query.prepare(
        "INSERT INTO prefixesofentity (prefix, dxcc, cqz, ituz) "
        "VALUES (:pref, :dxcc, :cqz, :ituz)");
    dataProxy->beginTransaction();
    // qSqlDatabase::database().transaction();  // Start the transaction

    foreach (const auto &prefix, pairPrefixes)
    {
        query.bindValue(":pref", prefix.first);
        query.bindValue(":dxcc", prefix.second.first);
        query.bindValue(":cqz", prefix.second.second.first);
        query.bindValue(":ituz", prefix.second.second.second);

        if (!query.exec())
        {
            emit queryError(Q_FUNC_INFO, query.lastError().databaseText(), query.lastError().text(), query.lastQuery());
            dataProxy->rollbackTransaction();
            // qSqlDatabase::database().rollback();  // Rollback transaction on error
            return false;
        }
    }
    dataProxy->commitTransaction();
    // qSqlDatabase::database().commit();  // Commit the transaction
    return true;
}


int World::extractEntityNumber(const QStringList &stringList)
{
    if (stringList.size() < 3) {
        qWarning() << Q_FUNC_INFO << "Insufficient elements in stringList!";
        return -1; // or another error code
    }

    int _entityNumber = stringList.at(2).toInt();
    QString _dirtyPrefix = stringList.at(0);
    QString _prefix;
    if (_prefix.contains(QChar('*'), Qt::CaseInsensitive))
    {

    }

    if (_prefix.contains(QChar('*'), Qt::CaseInsensitive)) {
        _entityNumber += 1000;
        // Loop until we find an unused entity number with an empty main prefix
        while ((!getEntityMainPrefix(_entityNumber).isEmpty()) || existingDXCCId(_entityNumber)) {
            _entityNumber += 1000;
        }
    }

   //qDebug() << Q_FUNC_INFO << "Final extracted entity number:" << _entityNumber;
    return _entityNumber;
}

bool World::existingDXCCId(const int _id) const
{
   //qDebug() << Q_FUNC_INFO << " - Start ";
    QSqlQuery query;
    query.prepare("SELECT 1 FROM entity WHERE dxcc = :id LIMIT 1");
    query.bindValue(":id", _id);
    if (!query.exec())
    {
        emit queryError(Q_FUNC_INFO, query.lastError().databaseText(), query.lastError().text(), query.lastQuery());
        return false;
    }
    return query.next(); // Returns true if a row exists, false otherwise
}

int World::getHowManyEntities()
{
   //qDebug() << Q_FUNC_INFO << " - Start ";
    return dataProxy->getHowManyEntities();
}

bool World::insertSpecialEntities()
{ //https://en.wikipedia.org/wiki/Non-ITU_prefix
   //qDebug() << Q_FUNC_INFO << " - Start ";
    // Define the list of prefixes to insert and their target main prefixes
    const QList<QPair<QString, QString>> prefixesToInsert = {
        {"T9", "E7"},  // T9 prefix moved to E7 entity
        {"4N", "4O"},  // 4N prefix moved to 4O entity
        {"YZ", "4O"}   // YZ prefix moved to 4O entity
    };

    QSqlQuery query;
    // Prepare the reusable INSERT query
    if (!query.prepare("INSERT INTO prefixesofentity (prefix, dxcc, cqz, ituz) VALUES (?, ?, ?, ?)"))
    {
       //qDebug() << Q_FUNC_INFO << " - Failed to prepare query: " << query.lastError().text();
        return false;
    }

    for (const auto &pair : prefixesToInsert)
    {
        const QString prefix = pair.first;
        const QString mainPrefix = pair.second;

        // 1. Get target entity properties (DXCC, CQZ, ITUZ)
        int entityID = dataProxy->getEntityIdFromMainPrefix(mainPrefix);
        if (entityID <= 0) {
           //qDebug() << Q_FUNC_INFO << " - ERROR: Failed to find entity ID for: " << mainPrefix;
            return false;
        }
        int cqz = dataProxy->getCQzFromEntity(entityID);
        int ituz = dataProxy->getITUzFromEntity(entityID);

        // 2. Bind values to the prepared statement
        query.bindValue(0, prefix);
        query.bindValue(1, entityID);
        query.bindValue(2, cqz);
        query.bindValue(3, ituz);

       //qDebug() << Q_FUNC_INFO << " - Attempting to insert: " << prefix;

        // 3. Attempt insertion and check for errors
        if (!query.exec())
        {
            // Check for the SQLite specific error code (or text) for a unique constraint violation.
            // SQLite error code 19 is 'SQLITE_CONSTRAINT'.
            const QString dbError = query.lastError().databaseText();
            const bool isConstraintViolation = dbError.contains("UNIQUE constraint failed", Qt::CaseInsensitive);

            if (isConstraintViolation)
            {
                // This means the prefix already exists, so the goal is met (value is present).
               //qDebug() << Q_FUNC_INFO << " - SUCCESS: Prefix '" << prefix << "' already exists.";
            }
            else
            {
                // Unhandled error (e.g., table missing, syntax error), so fail the function.
               //qDebug() << Q_FUNC_INFO << " - FAILURE: Unhandled SQL error for prefix '" << prefix << "': " << query.lastError().text();
                query.finish();
                return false;
            }
        } else {
            // Insertion successful, meaning the data was added.
           //qDebug() << Q_FUNC_INFO << " - SUCCESS: Prefix '" << prefix << "' inserted.";
        }
    }

    query.finish();
   //qDebug() << Q_FUNC_INFO << " - END - All prefixes processed successfully.";
    return true;
}

bool World::hasSpecialEntities()
{ // Checks if T9 is added to the list of prefixes to validate if special prefixes have been added.
   //qDebug() << Q_FUNC_INFO << " - Start ";
    QString queryString = QString("SELECT dxcc from prefixesofentity WHERE prefix='T9'");
    QSqlQuery query;
    bool sqlOK = query.exec(queryString);

    if (sqlOK)
    {
        query.next();
        if (query.isValid())
        {
            if ((query.value(0)).toInt()>1)
            {
                query.finish();
                return true;
            }
        }
    }
    else
    {
        emit queryError(Q_FUNC_INFO, query.lastError().databaseText(), query.lastError().text(), query.lastQuery());
    }
    query.finish();
    return false;
}

bool World::isAKnownCall(const QString &_callsign)
{
   //qDebug() << Q_FUNC_INFO << ": " << _callsign;
    return specialCalls.contains(_callsign);
}

bool World::isAKnownPrefix(const QString &_prefix)
{
   //qDebug() << Q_FUNC_INFO << ": " << _prefix;
    return longPrefixes.contains(_prefix);
}

bool World::executeQuery(QSqlQuery &query, const QString &queryString) const
{
    if (!query.exec(queryString)) {
        return false;
    }
    return true;
}

void World::emitQueryError(const QSqlQuery &query) const
{
    emit queryError(Q_FUNC_INFO, query.lastError().databaseText(), query.lastError().text(), query.lastQuery());
}


int World::getHowManyEmptyDXCCorCont()
{   // Refactored from DataProxy_SQLite::fillEmptyDXCCInTheLog()
    QSqlQuery query;
    QString queryString = QString("SELECT COUNT (id) FROM log WHERE dxcc IS NULL OR dxcc<'1' OR cont IS NULL");
    bool sqlOK = query.exec(queryString);
    int qsos = -1;
    if (sqlOK)
    {
        query.next();
        qsos = (query.value(0)).toInt();
    }
    else
    {
        emit queryError(Q_FUNC_INFO, query.lastError().databaseText(), query.lastError().text(), query.lastQuery());
    }
    query.finish();
    return qsos;
}

bool World::fillEmptyDXCCInTheLog()
{
    //qDebug() << Q_FUNC_INFO << " - Start";
    int qsos = getHowManyEmptyDXCCorCont();
    if (qsos < 1)
    {
        return true;
    }
    int step = util->getProgresStepForDialog(qsos);

    QProgressDialog progress(QObject::tr("Updating information..."), QObject::tr("Abort updating"), 0, qsos);
    progress.setMaximum(qsos);
    progress.setWindowModality(Qt::WindowModal);

    QString queryString = QString("SELECT id, call FROM log WHERE dxcc IS NULL OR dxcc<1 OR cont IS NULL");
    QSqlQuery query;
    bool sqlOK = query.exec(queryString);

    if (sqlOK)
    {
        int nameCol = -1;

        QSqlRecord rec = query.record();
        QString _call = QString();
        int _id = -1;
        int _dxcc = -1;
        QString _aux = QString();
        QString _continent = QString();
        int j = 0;

        while (query.next())
        {
            if (query.isValid())
            {
                nameCol = rec.indexOf("id");
                _id = (query.value(nameCol)).toInt();
                nameCol = rec.indexOf("call");
                _call = (query.value(nameCol)).toString();
                _dxcc = getQRZARRLId(_call);
                //_dxcc = QString::number(getPrefixId(_call));
                _continent = dataProxy->getContinentShortNameFromEntity(_dxcc);
                    //qDebug() << Q_FUNC_INFO << ":   DXCC: " << _dxcc;
                    //qDebug() << Q_FUNC_INFO << ":   Cont: " << _continent;
                // UPDATE THE ID WITH THE DXCC
                sqlOK = updateDXCCAndContinent(_id, _dxcc, _continent);
                if (!sqlOK)
                {
                    query.finish();
                    return false;
                }
                if (( (j % step )== 0) )
                { // To update the speed I will only show the progress once each X QSOs
                    _aux = QObject::tr("Updating DXCC and Continent information...") + "\n" + QObject::tr("QSO: ")  + QString::number(j) + "/" + QString::number(qsos);
                    //_aux = "Updating ...";
                    progress.setLabelText(_aux);
                    progress.setValue(j);
                }
                if ( progress.wasCanceled() )
                {
                    //qDebug() << Q_FUNC_INFO << ":   progress canceled";
                    query.finish();
                    return true;
                }
                j++;
            }
        }
        query.finish();
        progress.setValue(qsos);
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Information);
        msgBox.setWindowTitle(tr("KLog DXCC"));
        msgBox.setText(tr("All QSOs have been updated with a DXCC and the Continent.") );
        msgBox.exec();
    }
    else
    {
        emit queryError(Q_FUNC_INFO, query.lastError().databaseText(), query.lastError().text(), query.lastQuery());
        query.finish();
        return false;
    }
    query.finish();
    return true;
}

bool World::updateDXCCAndContinent(const int _id, const int _dxcc, const QString &_cont)
{   // Refactored from DataProxy_SQLite::fillEmptyDXCCInTheLog()
    QString queryString = QString("UPDATE log SET dxcc = :dxcc', cont = :cont WHERE id = :id");
    QSqlQuery query;
    query.prepare(queryString);
    query.bindValue(":dxcc", _dxcc);
    query.bindValue(":cont", _cont);
    query.bindValue(":id", _id);

    if (query.exec(queryString))
    {
        emit queryError(Q_FUNC_INFO, query.lastError().databaseText(), query.lastError().text(), query.lastQuery());
        query.finish();
        return false;
    }
    query.finish();
    return true;
}
