// Copyright (c) 2016 University of Helsinki
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
// See the file COPYING included with this distribution for more
// information.

//! @file LexcCompiler.h
//!
//! @brief Functions for building trie representation of lexc data
//!
//! LexcCompiler contains a finite state compilater for objects that resemble
//! Xerox lexc style lexicons. This compiler is modeled in LexcCompiler class.
//! LexcCompiler tries to be a minimal implementation needed to clone original
//! lexc functionality, it supports only the methods necessary to implement
//! original lexc, e.g. deleting entries from a lexicon during compilation is
//! not implemented.

#ifndef GUARD_LexcCompiler_h
#define GUARD_LexcCompiler_h

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <cstdio>
#include <string>

// #include "HfstTransducer.h"
namespace hfst
{
class HfstTransducer;
}
#include "../HfstTokenizer.h"
#include "../implementations/HfstBasicTransducer.h"
#include "XreCompiler.h"

namespace hfst
{
//! @brief Namespace for Xerox LexC related specific functions and classes.
namespace lexc
{

//! @brief A compiler holding information contained in lexc style lexicons.
//! A single LexcCompiler can be extended by adding entries to it, but little
//! else can be done with it. It is sufficient to implement clone of lexc.
class LexcCompiler
{
  public:
    //! @brief create a lexc compiler for unspecified transducer format.
    LexcCompiler();

    //! @brief create a lexc compiler with @c impl as transducer format.
    LexcCompiler(hfst::ImplementationType impl);

    //! @brief create a lexc compiler with @c impl as transducer format and @c
    //! withFlags
    // as indicator as the trasnducer should be build with or without flags
    LexcCompiler(hfst::ImplementationType impl, bool withFlags,
                 bool alignStrings);

    ~LexcCompiler();

    void reset();

    //! @brief compile lexc description from @c infile into current compiler
    LexcCompiler &parse(FILE *infile);

    //! @brief compile lexc description from file @a filename into current
    //!        compiler.
    LexcCompiler &parse(const char *filename);

    //! @brief set verbosity options.
    //! 0 means quiet, 1 the default and 2 (or bigger) the verbose mode.
    //! When verbose is 2, LexcCompiler will output the messages that Xerox
    //! lexc compiler does.
    LexcCompiler &setVerbosity(unsigned int verbose);

    unsigned int getVerbosity();

    void set_error_stream(std::ostream *os);

    std::ostream *get_error_stream();

    void setOutputToConsole(bool);

    bool getOutputToConsole();

    bool isQuiet();

    std::ostream *get_stream(std::ostream *oss);

    void flush(std::ostream *oss);

    LexcCompiler &setTreatWarningsAsErrors(bool value);

    bool areWarningsTreatedAsErrors();

    bool isStrictAlphabets();
    void setStrictAlphabets(bool strictness);
    bool hasSplitCharacters();
    void setSplitCharacters(bool splitness);
    void setWarning(const char *warning, bool value);
    bool isWarning(const char *warning);

    LexcCompiler &setAllowMultipleSublexiconDefinitions(bool value);

    LexcCompiler &setAlignStrings(bool value);

    LexcCompiler &setWithFlags(bool value);

    LexcCompiler &setMinimizeFlags(bool value);

    LexcCompiler &setRenameFlags(bool value);

    //! @brief add @a alphabet to multicharacter symbol set.
    //! These symbolse may be used for regular expression ? for backends that
    //! do not support open alphabets.
    LexcCompiler &addAlphabet(const std::string &alphabet);

    //! @brief add @a lexname to noflaggable lexicon set.
    //! @deprecated experimental, transitional
    LexcCompiler &addNoFlag(const std::string &lexname);

    //! @brief set current processing lexicon name to @a lexicon_name.
    LexcCompiler &setCurrentLexiconName(const std::string &lexicon_name);

    //! @brief add entry defined by a @a entry to current lexicon, pointing to
    //! @a continuation weighing @a weight to current lexicon.
    LexcCompiler &addStringEntry(const std::string &entry,
                                 const std::string &continuation,
                                 const double weight);

    //! @brief add entry defined by @a upper:@a lower, pointing to
    //! @a continuation weighing @a weight to current lexicon.
    LexcCompiler &addStringPairEntry(const std::string &upper,
                                     const std::string &lower,
                                     const std::string &continuation,
                                     const double weight);

    //! @brief add entry defined by regular expression @a xre, pointing to
    //! @a continuation weighing @a weight to current lexicon.
    LexcCompiler &addXreEntry(const std::string &xre,
                              const std::string &continuation,
                              const double weight);

    //! @brief add macro definition named @a name matching regular expression
    //! @a xre to known xerox regular expressions.
    LexcCompiler &addXreDefinition(const std::string &name,
                                   const std::string &xre);

    //! @brief set start lexicon's name to @a lexicon_name.
    LexcCompiler &setInitialLexiconName(const std::string &lexicon_name);

    //! @brief create final usable version of current lexicons and entries.
    hfst::HfstTransducer *compileLexical();

    //! @brief get trie formed by current string entries
    const std::map<std::string, hfst::HfstTransducer> &getStringTries() const;

    //! @brief get union formed by current regular expression entries
    const std::map<std::string, hfst::HfstTransducer> &getRegexpUnions() const;

    //! @brief check that current morphotax is connected and print anomalies.
    //! Works like xerox lexc, for compatibility.
    const LexcCompiler &printConnectedness(bool &warnings_printed);

  private:
    LexcCompiler &unicodeCheck_(const string &data);
    static void warn_about_one_sided_flags(
        const std::pair<std::string, std::string> &symbol_pair);

    bool quiet_;
    bool verbose_;
    bool align_strings_;
    bool with_flags_;
    bool minimize_flags_;
    bool rename_flags_;
    bool split_characters_;
    bool treat_warnings_as_errors_;
    bool warn_everything_;
    bool warn_missing_lexicons_;
    bool warn_unused_lexicons_;
    bool warn_repeated_lexicons_;
    bool warn_missing_alphabets_;
    bool warn_one_sided_flags_;
    bool warn_unnecessary_escapes_;
    std::ostream *error_;
#ifdef WINDOWS
    bool output_to_console_;
    std::ostringstream winoss_;
    std::ostream *redirected_stream_;
#endif

    hfst::ImplementationType format_;
    hfst::HfstTokenizer tokenizer_;
    hfst::xre::XreCompiler xre_;
    std::string initialLexiconName_;
    HfstBasicTransducer stringsTrie_;

    std::map<std::string, hfst::HfstTransducer *> regexps_;
    std::set<std::string> lexiconNames_;
    std::set<std::string> noFlags_;
    std::set<std::string> continuations_;
    std::set<std::string> alphabets_;
    std::string currentLexiconName_;
    size_t totalEntries_;
    size_t currentEntries_;
    bool parseErrors_;
};

// ugh, the global
extern LexcCompiler *lexc_;
}
}

// vim:set ft=cpp.doxygen:
#endif
