/* This file is part of node-sword-interface.

   Copyright (C) 2019 - 2026 Tobias Klein <contact@tklein.info>

   node-sword-interface 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 2 of the License, or
   (at your option) any later version.

   node-sword-interface 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 node-sword-interface. See the file COPYING.
   If not, see <http://www.gnu.org/licenses/>. */


#include <swmodule.h>
#include "napi_sword_helper.hpp"
#include "string_helper.hpp"
#include "module_helper.hpp"
#include "module_store.hpp"
#include "common_defs.hpp"

using namespace std;
using namespace sword;

Napi::Array NapiSwordHelper::getNapiArrayFromStringVector(const Napi::Env& env, vector<string>& stringVector)
{
    Napi::Array napiArray = Napi::Array::New(env, stringVector.size());

    for (unsigned int i = 0; i < stringVector.size(); i++) {
        napiArray.Set(i, stringVector[i]);
    }

    return napiArray;
}

Napi::Array NapiSwordHelper::getNapiVerseObjectsFromRawList(const Napi::Env& env, string moduleCode, vector<Verse>& verses)
{
    Napi::Array versesArray = Napi::Array::New(env, verses.size());

    for (unsigned int i = 0; i < verses.size(); i++) {
        Verse currentRawVerse = verses[i];
        Napi::Object verseObject = Napi::Object::New(env);
        this->verseTextToNapiObject(moduleCode, currentRawVerse, verseObject);
        versesArray.Set(i, verseObject);
    }

    return versesArray;
}

void NapiSwordHelper::swordModuleToNapiObject(const Napi::Env& env, SWModule* swModule, Napi::Object& object)
{
    if (swModule == 0) {
        cerr << "swModule is 0! Cannot run conversion to napi object!" << endl;
        return;
    }

    object["name"] = swModule->getName();
    object["type"] = swModule->getType();

    if (swModule->getConfigEntry("Category")) {
        object["category"] = string(swModule->getConfigEntry("Category"));
    } else {
        object["category"] = "";
    }

    object["description"] = swModule->getDescription();
    object["language"] = swModule->getLanguage();
    object["location"] = this->_moduleStore.getModuleDataPath(swModule);
  
    object["abbreviation"] = this->getConfigEntry(swModule, "Abbreviation", env);
    object["about"] = this->getConfigEntry(swModule, "About", env);
    object["distributionLicense"] = this->getConfigEntry(swModule, "DistributionLicense", env);
    object["shortCopyright"] = this->getConfigEntry(swModule, "ShortCopyright", env);
    object["version"] = this->getConfigEntry(swModule, "Version", env);

    if (swModule->getConfigEntry("SwordVersionDate")) {
        object["lastUpdate"] = this->getConfigEntry(swModule, "SwordVersionDate", env);
    } else {
        object["lastUpdate"] = "";
    }

    if (swModule->getConfigEntry("Direction")) {
        string direction = string(swModule->getConfigEntry("Direction"));
        object["isRightToLeft"] = Napi::Boolean::New(env, (direction == "RtoL"));
    } else {
        object["isRightToLeft"] = Napi::Boolean::New(env, false);
    }

    if (swModule->getConfigEntry("InstallSourceCaption")) {
        object["repository"] = string(swModule->getConfigEntry("InstallSourceCaption"));
    } else {
        object["repository"] = "";
    }

    if (swModule->getConfigEntry("InstallSize")) {
        std::string configInstallSize = string(swModule->getConfigEntry("InstallSize"));

        int moduleSize = -1;
        if (configInstallSize.length() > 0 &&
            configInstallSize.find_first_not_of("0123456789") == std::string::npos) {
            moduleSize = std::stoi(configInstallSize);
        }

        object["size"] = Napi::Number::New(env, moduleSize);
    } else {
        object["size"] = Napi::Number::New(env, -1);
    }

    bool moduleIsLocked = swModule->getConfigEntry("CipherKey");
    
    object["locked"] = Napi::Boolean::New(env, moduleIsLocked);
    if (moduleIsLocked && swModule->getConfigEntry("UnlockInfo")) {
        object["unlockInfo"] = swModule->getConfigEntry("UnlockInfo");
    } else {
        object["unlockInfo"] = "";
    }

    object["inUserDir"] = Napi::Boolean::New(env, this->_moduleStore.isModuleInUserDir(swModule));
    object["hasStrongs"] = Napi::Boolean::New(env, this->_moduleHelper.moduleHasGlobalOption(swModule, "Strongs"));
    object["hasFootnotes"] = Napi::Boolean::New(env, this->_moduleHelper.moduleHasGlobalOption(swModule, "Footnotes"));
    object["hasHeadings"] = Napi::Boolean::New(env, this->_moduleHelper.moduleHasGlobalOption(swModule, "Headings"));
    object["hasRedLetterWords"] = Napi::Boolean::New(env, this->_moduleHelper.moduleHasGlobalOption(swModule, "RedLetter"));
    object["hasCrossReferences"] = Napi::Boolean::New(env, this->_moduleHelper.moduleHasGlobalOption(swModule, "Scripref"));

    object["hasGreekStrongsKeys"] = Napi::Boolean::New(env, this->_moduleHelper.moduleHasFeature(swModule, "GreekDef"));
    object["hasHebrewStrongsKeys"] = Napi::Boolean::New(env, this->_moduleHelper.moduleHasFeature(swModule, "HebrewDef"));
    
    // Add module history entries
    std::vector<std::string> historyEntries = this->_moduleHelper.getModuleHistoryEntries(swModule);
    object["history"] = this->getNapiArrayFromStringVector(env, historyEntries);
}

Napi::String NapiSwordHelper::getConfigEntry(sword::SWModule* swModule, std::string key, const Napi::Env& env)
{
    std::string configEntry = "";

    if (swModule == 0) {
        cerr << "swModule is 0! Cannot get config entry!" << endl;
    } else {
        if (swModule->getConfigEntry(key.c_str())) {
            configEntry = swModule->getConfigEntry(key.c_str());
        }
    }

    Napi::String napiConfigEntry = Napi::String::New(env, configEntry);
    return napiConfigEntry;
}

void NapiSwordHelper::verseTextToNapiObject(string moduleCode, Verse rawVerse, Napi::Object& object)
{
    string reference = rawVerse.reference;
    string verseText = rawVerse.content;

    vector<string> splittedReference = StringHelper::split(reference, " ");
    string book = splittedReference.size() > 0 ? splittedReference[0] : "";
    string chapterVerseReference = splittedReference.size() > 1 ? splittedReference[1] : "";

    vector<string> splittedChapterVerseReference = StringHelper::split(chapterVerseReference, ":");
    int chapter = (splittedChapterVerseReference.size() > 0 &&
                   splittedChapterVerseReference[0].find_first_not_of("0123456789") == std::string::npos &&
                   !splittedChapterVerseReference[0].empty())
                  ? std::stoi(splittedChapterVerseReference[0]) : 0;
    int verseNr = (splittedChapterVerseReference.size() > 1 &&
                   splittedChapterVerseReference[1].find_first_not_of("0123456789") == std::string::npos &&
                   !splittedChapterVerseReference[1].empty())
                  ? std::stoi(splittedChapterVerseReference[1]) : 0;

    object["moduleCode"] = moduleCode;
    object["bibleBookShortTitle"] = book;
    object["chapter"] = chapter;
    object["verseNr"] = verseNr;
    object["absoluteVerseNr"] = rawVerse.absoluteVerseNumber;
    object["content"] = verseText;
}

void NapiSwordHelper::strongsEntryToNapiObject(const Napi::Env& env, StrongsEntry* strongsEntry, Napi::Object& object)
{
    if (strongsEntry == 0) {
        cerr << "strongsEntry is 0! Cannot run conversion to napi object!" << endl;
        return;
    }

    object["rawEntry"] = strongsEntry->rawEntry;
    object["key"] = strongsEntry->key;
    object["transcription"] = strongsEntry->transcription;
    object["phoneticTranscription"] = strongsEntry->phoneticTranscription;
    object["definition"] = strongsEntry->definition;

    Napi::Array referencesArray = Napi::Array::New(env, strongsEntry->references.size());
    for (unsigned int i = 0; i < strongsEntry->references.size(); i++) {
        StrongsReference currentReference = strongsEntry->references[i];
        Napi::Object referenceObject = Napi::Object::New(env);
        
        referenceObject["text"] = currentReference.text;
        referenceObject["key"] = currentReference.key;

        referencesArray.Set(i, referenceObject);
    }

    object["references"] = referencesArray;
}
