/*
    SPDX-FileCopyrightText: 2002 Cornelius Schumacher <schumacher@kde.org>

    SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
*/

#include "docmetainfo.h"

#include "khc_debug.h"

#include <KConfig>
#include <KConfigGroup>
#include <KLocalizedString>

#include "docentrytraverser.h"

#include <prefs.h>

using namespace KHC;

bool DocMetaInfo::mLoaded = false;

DocMetaInfo *DocMetaInfo::mSelf = nullptr;

DocMetaInfo *DocMetaInfo::self()
{
  if ( !mSelf ) mSelf = new DocMetaInfo;
  return mSelf;
}

DocMetaInfo::DocMetaInfo()
{
  qCDebug(KHC_LOG) << "DocMetaInfo()";

  mRootEntry.setName( i18n("Top-Level Documentation") );
}

DocMetaInfo::~DocMetaInfo()
{
  qCDebug(KHC_LOG) << "~DocMetaInfo()";

  DocEntry::List::ConstIterator it;
  for( it = mDocEntries.constBegin(); it != mDocEntries.constEnd(); ++it )
  {
    delete *it;
  }

  mLoaded = false;

  mSelf = nullptr;
}

DocEntry *DocMetaInfo::addDocEntry( const QFileInfo &fi )
{
  if ( !fi.exists() ) return nullptr;

  QString extension = fi.completeSuffix();
  QStringList extensions = extension.split( QLatin1Char('.'));
  QString lang;
  if ( extensions.count() >= 2 )
  {
    lang = extensions[ extensions.count() - 2 ];
  }

  if ( !lang.isEmpty() && !mLanguages.contains( lang ) )
  {
    return nullptr;
  }

  DocEntry *entry = new DocEntry();

  if ( entry->readFromFile( fi ) )
  {
    if ( !lang.isEmpty() && lang != mLanguages.first() )
    {
      QMap<QString,QString>::Iterator it = mLanguageNames.find( lang );
      if ( it == mLanguageNames.end() )
      {
        it = mLanguageNames.insert( lang, languageName( lang ) );
      }

      entry->setLang( lang );
      entry->setName( i18nc("doctitle (language)","%1 (%2)",
                             entry->name() ,
                             *it ) );
    }
    QString indexer = entry->indexer();
    indexer.replace( QStringLiteral("%f"), fi.absoluteFilePath() );
    entry->setIndexer( indexer );
    addDocEntry( entry );
    return entry;
  } 
  else 
  {
    delete entry;
    return nullptr;
  }
}

void DocMetaInfo::addDocEntry( DocEntry *entry )
{
  mDocEntries.append( entry );
  if ( !entry->search().isEmpty() ) mSearchEntries.append( entry );
}

DocEntry::List DocMetaInfo::docEntries() const
{
  return mDocEntries;
}

DocEntry::List DocMetaInfo::searchEntries() const
{
  return mSearchEntries;
}

QString DocMetaInfo::languageName( const QString &langcode )
{
  if ( langcode == QLatin1String("en") )
      return i18nc("Describes documentation entries that are in English", "English");

  const QString cfgfile = QStandardPaths::locate( QStandardPaths::GenericDataLocation, QStringLiteral( "locale/%1/kf5_entry.desktop" ).arg( langcode ) );

  qCDebug(KHC_LOG) << "-- langcode: " << langcode << " cfgfile: " << cfgfile;

  KConfig _cfg( cfgfile, KConfig::SimpleConfig );
  KConfigGroup cfg(&_cfg, QStringLiteral("KCM Locale") );
  const QString name = cfg.readEntry( "Name", langcode );

  return name;
}

void DocMetaInfo::scanMetaInfo( bool force )
{
  if ( mLoaded && !force ) return;

  mLanguages = KLocalizedString::languages();

  QStringList::ConstIterator it;

  QStringList metaInfos = Prefs::metaInfoDirs();

  if ( metaInfos.isEmpty() ) 
  {
    metaInfos = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("plugins"), QStandardPaths::LocateDirectory);
  }
  for( it = metaInfos.constBegin(); it != metaInfos.constEnd(); ++it)
  {
    qCDebug(KHC_LOG) << "DocMetaInfo::scanMetaInfo(): scanning " << *it;
    scanMetaInfoDir( *it, &mRootEntry );
  }

  mLoaded = true;
}

DocEntry *DocMetaInfo::scanMetaInfoDir( const QString &dirName,
                                        DocEntry *parent )
{
  QDir dir( dirName );
  if ( !dir.exists() ) return nullptr;

  const auto entryInfoList = dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
  for ( const QFileInfo &fi : entryInfoList )
  {
    DocEntry *entry = nullptr;
    if ( fi.isDir() ) 
    {
      DocEntry *dirEntry = addDirEntry( QDir( fi.absoluteFilePath() ), parent );
      entry = scanMetaInfoDir( fi.absoluteFilePath(), dirEntry );
    } 
    else if ( fi.suffix() == QLatin1String("desktop") )
    {
      entry = addDocEntry( fi );
      if ( parent && entry ) parent->addChild( entry );
    }
  }

  return nullptr;
}

DocEntry *DocMetaInfo::addDirEntry( const QDir &dir, DocEntry *parent )
{
  DocEntry *dirEntry = addDocEntry( QFileInfo(dir.absoluteFilePath( QStringLiteral(".directory"))));

  if ( !dirEntry ) 
  {
    dirEntry = new DocEntry;
    dirEntry->setName( dir.dirName() );
    addDocEntry( dirEntry );
  }

  dirEntry->setDirectory( true );
  if ( parent ) parent->addChild( dirEntry );

  return dirEntry;
}


void DocMetaInfo::traverseEntries( DocEntryTraverser *traverser )
{
  traverseEntry( &mRootEntry, traverser );
}

void DocMetaInfo::traverseEntry( DocEntry *entry, DocEntryTraverser *traverser )
{
  DocEntry::List children = entry->children();
  DocEntry::List::ConstIterator it;
  for( it = children.constBegin(); it != children.constEnd(); ++it )
  {
    if ( (*it)->isDirectory() && !(*it)->hasChildren() &&
         (*it)->khelpcenterSpecial().isEmpty() ) continue;
    traverser->process( *it );
    if ( (*it)->hasChildren() ) 
    {
      DocEntryTraverser *t = traverser->childTraverser( *it );
      if (t)
      {
        traverseEntry( *it, t );
        t->deleteTraverser();
      }
    }
  }
}

void DocMetaInfo::startTraverseEntries( DocEntryTraverser *traverser )
{
  qCDebug(KHC_LOG) << "DocMetaInfo::startTraverseEntries()";
  traverser->setNotifyee( this );
  startTraverseEntry( &mRootEntry, traverser );
}

void DocMetaInfo::startTraverseEntry( DocEntry *entry,
                                      DocEntryTraverser *traverser )
{
  if ( !traverser ) 
  {
    qCWarning(KHC_LOG) << "DocMetaInfo::startTraverseEntry(): ERROR. No Traverser.";
    return;
  }

  if ( !entry ) 
  {
    qCWarning(KHC_LOG) << "DocMetaInfo::startTraverseEntry(): no entry.";
    endTraverseEntries( traverser );
    return;
  }

  traverser->startProcess( entry );
}

void DocMetaInfo::endProcess( DocEntry *entry, DocEntryTraverser *traverser )
{
  if ( !entry ) 
  {
    endTraverseEntries( traverser );
    return;
  }

  if ( entry->hasChildren() ) 
  {
    startTraverseEntry( entry->firstChild(), traverser->childTraverser( entry ) );
  } else if ( entry->nextSibling() ) 
  {
    startTraverseEntry( entry->nextSibling(), traverser );
  } else 
  {
    DocEntry *parent = entry->parent();
    DocEntryTraverser *parentTraverser = nullptr;
    while ( parent ) {
      parentTraverser = traverser->parentTraverser();
      traverser->deleteTraverser();
      if ( parent->nextSibling() ) {
        startTraverseEntry( parent->nextSibling(), parentTraverser );
        break;
      } 
      else 
      {
        parent = parent->parent();
        traverser = parentTraverser;
      }
    }
    if ( !parent )
    {
      endTraverseEntries( traverser );
    }
  }
}

void DocMetaInfo::endTraverseEntries( DocEntryTraverser *traverser )
{
  qCDebug(KHC_LOG) << "DocMetaInfo::endTraverseEntries()";

  if ( !traverser ) 
  {
    qCDebug(KHC_LOG) << " no more traversers.";
    return;
  }

  traverser->finishTraversal();
}
// vim:ts=2:sw=2:et
