/*
    Copyright 2009-2021 Stéphane De Mita, Mathieu Siol

    This file is part of the EggLib library.

    EggLib 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.

    EggLib 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 EggLib.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef EGGLIB_EGGLIB_HPP
#define EGGLIB_EGGLIB_HPP

#ifdef DEBUG
#include <iostream>
#include <iomanip>
using namespace std;
#endif

#include <string>
#include <exception>
#include <limits>
#include <sstream>


/** \mainpage EggLib C++ library documentation
 *
 * For a description of the EggLib project as a whole, installation
 * instruction and other, go  <a href="http://egglib.sourceforge.net/">here</a>.

 * The EggLib C++ library provides support for the following features:
 *
 * Main features:
 *   \li Fasta-centered input and output, format conversion utilities.
 *   \li Data management, where data are represented by signed integers.
 *   \li Molecular population genetics statistics (neutrality tests,
 *       population divergence).
 *   \li Integrated coalescent simulator implementing partial selfing,
 *       custom population structure and history, microsatellite models
 *       and others.
 *
 * These pages contain the documentation of all classes, functions and
 * methods defined in the C++ layer of EggLib. This documentation was
 * automatically generated by Doxygen and should be consulted to get
 * usage information of given classes or functions. Most classes and
 * methods are available in EggLib's Python module through the SWIG
 * binding, so the interface should be valid (although implicit type
 * conversion takes place, such as const char * to str).
 *
 * To navigate, use the "Modules" and "Classes" indexes above.
 *
 */

/** \defgroup core Core
 *
 * These classes holds data containers and core utilities of the
 * library.
 *
 */

/** \defgroup exceptions EggLib exceptions
 *
 * Here are defined the EggLib-specific types of exceptions. They
 * provides a means to intercept logical errors that may be recovered.
 * They include errors occurring while reading data file (file format
 * errors), errors occurring when specific constraints on argument
 * values are not fulfilled, runtime errors (corresponding to unexpected
 * behaviour) and memory error, which may occur because the computer's
 * memory is saturated but also when the program is faulty. In general,
 * the latter should never be recovered.
 *
 */

/** \defgroup parsers Data files support
 *
 * This module gathers classes and functions allowing to import (and in
 * some cases export) data from and to files, streams and strings under
 * standard formats.
 *
 */

/** \defgroup coalesce Coalescent simulator
 *
 * This is the coalescent simulator of EggLib. It accommodates a
 * flexible model with custom population structure, custom population
 * history, diploid individuals with custom self-fertilization rates,
 * recombination (the last two might vary over time), and flexible
 * mutation models that can emulate microsatellite, nucleotide sequences
 * and SNPs with or without homoplasy. The main class is Coalesce, and
 * parameters can be passed through Params. The output takes the form
 * of DataMatrix instances storing allele values for sampled individuals
 * and can be further analyzed for polymorphism using the diversity
 * module.
 *
 */

/** \defgroup diversity Diversity analyses
 *
 * Contains classes to perform analysis of polymorphism based on
 * DataMatrix instances.
 *
 */

/** \brief Namespace holding all EggLib C++ classes
 *
 * All classes of the EggLib C++ library are defined within this
 * namespace.
 *
 */
namespace egglib {

    extern const int DEBUG_FLAG; ///< 1 if DEBUG is set, 0 otherwise
    extern const double UNDEF; ///< Unknown value (small / very negative value)
    extern const int MISSINGDATA; ///< Missing data (large value)
    extern const int MAX_ALLELE_RANGE; ///< Maximal allelee (large but not nearly as large a MISSINGDATA)
    extern const unsigned int UNKNOWN; ///< Unknown value (large value)
    extern const char MAXCHAR; ///< Unknown value (large value)
    extern const unsigned int MAX; ///< Unknown/undefined (large value)
    extern const unsigned int OUTGROUP; ///< Outgroup (large value)
    extern const unsigned int MISSING; ///< Missing data (large value)
    extern const unsigned long BEFORE; ///< Value before the first position (large value)
    extern const unsigned long FIRST; ///< First available position (large value)
    extern const unsigned long LAST; ///< Last available position (large value)

    const char * to_string(unsigned int); ///< Convert to string (returns a pointer to a local array)

   /** \brief Base exception type for errors occurring in this library
    *
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggException : public std::exception {

        public:

            /// Constructor with empty error message
            EggException();

            /// Creates the exception
            EggException(const char * message);

            /// Destructor
            ~EggException() throw() {}

            /// Gets error message
            virtual const char * what() const throw();

      protected:

            std::string message;
    };

   /** \brief Exception type for memory errors
    *
    * There is a macro EGGMEM which stands for
    * EggMemoryError(__LINE__, __FILE__).
    *
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggMemoryError : public EggException {

        public:

            /// Creates the exception
            EggMemoryError(unsigned int line, const char * file);

            /// Destructor
            ~EggMemoryError() throw() {}
    };

   /** \brief Exception type for argument value errors
    *
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggArgumentValueError : public EggException {

        public:

            /// Creates the exception
            EggArgumentValueError(const char * m): EggException(m) {}

            /// Destructor
            ~EggArgumentValueError() throw() {}
    };

   /** \brief Exception type for argument value errors
    *
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggIndexError : public EggException {

        public:

            /// Creates the exception
            EggIndexError(const char * m ) : EggException(m) {}

            /// Destructor
            ~EggIndexError() throw() {}
    };

   /** \brief Exception type for runtime errors
    *
    * Runtime error definition is rather large. Includes bugs as well
    * as logical errors.
    *
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggRuntimeError : public EggException {

        public:

            /// Creates the exception
            EggRuntimeError(const char * m);

            /// Destructor
            ~EggRuntimeError() throw() {}
    };

   /** \brief Exception type for file/string parsing errors
    *
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggFormatError : public EggException {

        public:

            /// Creates the exception
            EggFormatError(const char * fileName, unsigned int line, const char * expectedFormat, const char * m, char c = 0, const char * paste_end = "");

            /// Destructor
            ~EggFormatError() throw() {}

            /// Get line number
            unsigned int line();

            /// Get character
            char character();

            /// Get additional information field
            const char * info();

            /// Get bare error message (before formatting)
            const char * m();

        private:

            unsigned int _line;
            char c;
            std::string paste_end;
            std::string _message;
    };

   /** \brief Exception type for errors while opening a file
    *
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggOpenFileError : public EggException {

        public:

            /// Creates the exception
            EggOpenFileError(const char * fileName );

            /// Destructor
            ~EggOpenFileError() throw() {}
    };

   /** \brief Exception type for unaligned sequences
    *
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggUnalignedError : public EggException {

        public:

            /// Creates the exception
            EggUnalignedError();

            /// Destructor
            ~EggUnalignedError() throw() {}
    };

   /** \brief Exception type for invalid allele
    *
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggInvalidAlleleError : public EggException {

        public:

            /// Creates the exception
            EggInvalidAlleleError(int c, unsigned int seqIndex, unsigned int posIndex);

            /// Destructor
            ~EggInvalidAlleleError() throw() {}
    };

   /** \brief Exception type for invalid character (not exportable)
    *
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggInvalidCharacterError : public EggException {

        public:

            /// Creates the exception
            EggInvalidCharacterError(int value);

            /// Destructor
            ~EggInvalidCharacterError() throw() {}
    };

   /** \brief Exception type for inconsistent ploidy over individuals
    *EggInvalidChromosomeError
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggPloidyError : public EggException {

        public:

            /// Creates the exception
            EggPloidyError();

            /// Destructor
            ~EggPloidyError() throw() {}
    };

   /** \brief Exception type for non-nested structure
    *
    * \ingroup exceptions
    *
    * Header: <egglib-cpp/egglib.hpp>
    *
    */
    class EggNonHierarchicalStructure : public EggException {

        public:

            /// Creates the exception
            EggNonHierarchicalStructure(bool indiv_flag, const char * label);

            /// Creates the exception (for individual in both ingroup/outgroup)
            EggNonHierarchicalStructure(const char * label);

            /// Destructor
            ~EggNonHierarchicalStructure() throw() {}
    };

    class  EggInvalidChromosomeIdxError : public EggException {
	
	public:
	    //Creates the exeption
	     EggInvalidChromosomeIdxError(const char * chromosome, const char * file);
            /// Destructor
            ~ EggInvalidChromosomeIdxError() throw() {}

    };

    class  EggInvalidPositionIdxError : public EggException {
	
	public:
	    //Creates the exeption
	     EggInvalidPositionIdxError(const char * chromosome, unsigned int position, const char * file);
            /// Destructor
            ~ EggInvalidPositionIdxError() throw() {}

    };

    class  EggInvalidLineIdxError : public EggException {
	
	public:
	    //Creates the exeption
	     EggInvalidLineIdxError(unsigned int line,const char * file);
            /// Destructor
            ~ EggInvalidLineIdxError() throw() {}

    };

    /// \brief Error in case an unknown allele is processed through an alphabet
    template <class TYPE> class EggAlphabetError : public EggException {
        public:
            /// \brief Constructor
            EggAlphabetError(const char * alphabet_name, const TYPE value) {
                std::ostringstream stream;
                stream << "invalid allele for alphabet `";
                stream << alphabet_name << "`: `" << value << "`";
                message = stream.str();
            }

            /// Destructor
            ~EggAlphabetError() throw() {}
    };

}

#define EGGMEM EggMemoryError(__LINE__, __FILE__)
#endif
