// -*- mode: c++; c-file-style: "linux"; c-basic-offset: 2; indent-tabs-mode: nil -*-
//
//  Copyright (C) 2009-2015 Andrej Vodopivec <andrej.vodopivec@gmail.com>
//  Copyright (C) 2015 Gunter Königsmann     <wxMaxima@physikbuch.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 2 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, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
//  SPDX-License-Identifier: GPL-2.0+

/*! \file
  This file declares the class MaximaManual.

  MaximaManual creates the list of autocompletions for a string and allows
  dynamically appending maxima commands to this list as soon as they are defined.
*/

#ifndef MAXIMAMANUAL_H
#define MAXIMAMANUAL_H

#include <atomic>
#include <thread>
#include <mutex>
#include <memory>
#include <vector>
#include <wx/dir.h>
#include <wx/wx.h>
#include <wx/arrstr.h>
#include <wx/regex.h>
#include <wx/xml/xml.h>
#include <wx/filename.h>
#include "precomp.h"
#include "Configuration.h"
#include "Compat.h"
#include <unordered_map>

/* The autocompletion logic

   The wordlists for autocompletion for keywords come from several sources:

   - wxMaxima::ReadLoadSymbols receive the contents of maxima's variables
   "values" and "functions" after a package is loaded.
   - all words that appear in the worksheet
   - and a list of maxima's builtin commands.
*/
class MaximaManual
{
public:
  explicit MaximaManual(Configuration *configuration);
  typedef std::unordered_map <wxString, wxString, wxStringHash> HelpFileAnchors;
  HelpFileAnchors GetHelpfileAnchors();
  void FindMaximaHtmlDir(const wxString &docDir);
  wxString GetHelpfileAnchorName(wxString keyword);
  wxString GetHelpfileUrl_Singlepage(const wxString &keyword);
  wxString GetHelpfileUrl_FilePerChapter(const wxString &keyword);
  wxString GetHelpfileURL(const wxString &keyword);
  //! Search maxima's help file for command and variable names
  void LoadHelpFileAnchors(const wxString &docdir, const wxString &maximaVersion);
  //! Collect all keyword anchors in the help file
  void CompileHelpFileAnchors(stop_token stopToken,
                              const wxString &maximaHtmlDir,
                              const wxString &maximaVersion,
                              const wxString &saveName);
  //! Load the result from the last CompileHelpFileAnchors from the disk cache
  bool LoadManualAnchorsFromCache();
  //! Load the help file anchors from an wxXmlDocument
  bool LoadManualAnchorsFromXML(const wxXmlDocument &xmlDocument, bool checkManualVersion = true);
  //! Load the help file anchors from the built-in list
  bool LoadBuiltInManualAnchors();
  //! Save the list of help file anchors to the cache.
  void SaveManualAnchorsToCache(const wxString &maximaHtmlDir,
                                const wxString &maximaVersion,
                                const wxString &saveName);
  virtual ~MaximaManual();
private:
  //! Add our aliases to a list of anchors
  static void AnchorAliasses(HelpFileAnchors &anchors);
  //! Scans the maxima directory for a list of loadable files
  class GetHTMLFiles : public wxDirTraverser
  {
  public:
    explicit GetHTMLFiles(std::vector<wxString>& files,
                          stop_token stopToken = {},
                          const wxString &prefix = wxEmptyString) :
      m_files(files), m_stopToken(stopToken), m_prefix(prefix) { }
    virtual wxDirTraverseResult OnFile(const wxString& filename) override;
    virtual wxDirTraverseResult OnDir(const wxString& dirname) override;
    std::vector<wxString>& GetResult() const {return m_files;}
  protected:
    std::vector<wxString>& m_files;
    stop_token m_stopToken;
    wxString m_prefix;
  };
  class GetHTMLFiles_Recursive : public wxDirTraverser
  {
  public:
    explicit GetHTMLFiles_Recursive(std::vector<wxString>& files,
                                    stop_token stopToken = {},
                                    const wxString &prefix = wxEmptyString) :
      m_files(files), m_stopToken(stopToken), m_prefix(prefix) { }
    virtual wxDirTraverseResult OnFile(const wxString& filename) override;
    virtual wxDirTraverseResult OnDir(const wxString& dirname) override;
    std::vector<wxString>& GetResult() const {return m_files;}
  protected:
    std::vector<wxString>& m_files;
    stop_token m_stopToken;
    wxString m_prefix;
  };

  //    m_configuration.MaximaShareDir(dir);

  //! The thread the help file anchors are compiled in
  jthread m_helpfileanchorsThread;
  //! True while a CompileHelpFileAnchors() task is in flight. Lets
  //! LoadHelpFileAnchors() leave a running compile alone instead of killing and
  //! restarting it on every Maxima event (repeated restarts can keep the slow
  //! manual parse from ever finishing and wedge the shutdown join()).
  std::atomic<bool> m_anchorsCompileInFlight{false};
    std::mutex m_helpFileAnchorsLock;
  //! The configuration storage
  Configuration *m_configuration = NULL;
  //! All anchors for keywords maxima's helpfile contains (singlepage version)

  HelpFileAnchors m_helpFileURLs_singlePage;
  //! All anchors for keywords maxima's helpfile contains (file-per-chapter version)
  HelpFileAnchors m_helpFileURLs_filePerChapter;
  //! All anchors for keywords maxima's helpfile contains (without the file name)
  HelpFileAnchors m_helpFileAnchors;
  wxString m_maximaHtmlDir;
  wxString m_maximaVersion;
};

#endif // MAXIMAMANUAL_H
