//# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
//# License for more details.
//#
//# You should have received a copy of the GNU Library General Public License
//# along with this library; if not, write to the Free Software Foundation,
//# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
//#
//# Correspondence concerning AIPS++ should be addressed as follows:
//#        Internet email: aips2-request@nrao.edu.
//#        Postal address: AIPS++ Project Office
//#                        National Radio Astronomy Observatory
//#                        520 Edgemont Road
//#                        Charlottesville, VA 22903-2475 USA
//#

#ifndef IMAGES_ASCIIANNOTATIONFILEPARSER_H
#define IMAGES_ASCIIANNOTATIONFILEPARSER_H

#include <casacore/casa/aips.h>
#include <casacore/casa/Arrays/Vector.h>
#include <casacore/casa/Containers/Record.h>
#include <casacore/casa/Logging/LogIO.h>
#include <casacore/casa/OS/RegularFile.h>
#include <casacore/casa/Utilities/Regex.h>
#include <casacore/coordinates/Coordinates/SpectralCoordinate.h>
#include <imageanalysis/Annotations/AnnotationBase.h>
#include <imageanalysis/IO/AsciiAnnotationFileLine.h>


#include <casacore/coordinates/Coordinates/CoordinateSystem.h>

namespace casa {

// <summary>
// Parse and store regions and annotations from an ascii region file
// </summary>
// <author>Dave Mehringer</author>
// <use visibility=export>
// <reviewed reviewer="" date="yyyy/mm/dd" tests="" demos="">
// </reviewed>
// <prerequisite>

// </prerequisite>

// <etymology>
// This is a class designed to parse and store regions and annotations from an ascii region file
// </etymology>

// <synopsis>
// This class is for parsing and storing regions and annotations from an ascii region (CRTF) file.
// The format spec can be found at
// https://casa.nrao.edu/casadocs-devel/stable/imaging/image-analysis/region-file-format
// </synopsis>

class RegionTextParser {

public:

    static const casacore::Int CURRENT_VERSION;
    static const casacore::Regex MAGIC;

    // because of nonstandard access patterns, be careful when using ParamValue and ParamSet
    // outside this class. These should probably be made into full fledged classes at some
    // point.
    struct ParamValue {
        casacore::Double doubleVal;
        casacore::Int intVal;
        casacore::String stringVal;
        casacore::Bool boolVal;
        AnnotationBase::LineStyle lineStyleVal;
        AnnotationBase::FontStyle fontStyleVal;
        std::shared_ptr<std::pair<casacore::MFrequency, casacore::MFrequency> > freqRange;
        casacore::Vector<casacore::Stokes::StokesTypes> stokes;
        AnnotationBase::RGB color;
        std::vector<casacore::Int> intVec;
    };

    using ParamSet = std::map<AnnotationBase::Keyword, ParamValue>;

    RegionTextParser() = delete;

    // <group>
    // differentiating between the filename and simple text constructors
    // <src>globalOverrideChans</src> override all spectral selections in the file
    // or text by using this channel selection<src>
    // <src>globalOverrideStokes</src> override all correlation selections in the file
    // or text by using this polarization selection<src>
    // <src>prependRegion</src> allows one to specify region(s) that will be prepended to
    // any text in <src>filename</src> or <src>text</src>
	// <src>requireImageRegion</src> is passed to the AnnRegion constructors
	// to indicate whether to rethrow ToLCRegionConversionError exception when
	// the region is outside the image lattice, or create the object even if
	// the ImageRegion has no lattice region. The default (true) rethrows the exception.
	// CAS-12631: added for CARTA, which can import regions outside an image.
    RegionTextParser(
        const casacore::String& filename, const casacore::CoordinateSystem& csys,
        const casacore::IPosition& imShape, const casacore::Int requireAtLeastThisVersion,
        const casacore::String& prependRegion="",
        const casacore::String& globalOverrideChans="",
        const casacore::String& globalOverrrideStokes="",
        casacore::Bool verbose=true,
        casacore::Bool requireImageRegion=true
    );

    RegionTextParser(
        const casacore::CoordinateSystem& csys,
        const casacore::IPosition& imShape,
        const casacore::String& text,
        const casacore::String& prependRegion="",
        const casacore::String& globalOverrideChans="",
        const casacore::String& globalOverrrideStokes="",
        casacore::Bool verbose=true,
        casacore::Bool requireImageRegion=true
    );
    //</group>

    ~RegionTextParser();

    RegionTextParser& operator=(const RegionTextParser&) = delete;

    casacore::Int getFileVersion() const;

    std::vector<AsciiAnnotationFileLine> getLines() const;

    // get the parameter set from a line of <src>text</src>. <src>preamble</src> is prepended to exception messages.
    static ParamSet getParamSet(
        casacore::Bool& spectralParmsUpdated,
        casacore::LogIO& log, const casacore::String& text, const casacore::String& preamble,
        const casacore::CoordinateSystem& csys,
        std::shared_ptr<std::pair<casacore::MFrequency, casacore::MFrequency> > overridingFreqRange,
        std::shared_ptr<casacore::Vector<casacore::Stokes::StokesTypes> > overridingCorrRange
    );

private:

    const static casacore::String sOnePair;
    const static casacore::String bTwoPair;
    const static casacore::String sNPair;
    const static casacore::Regex startOnePair;
    const static casacore::Regex startNPair;

    casacore::CoordinateSystem _csys;
    std::unique_ptr<casacore::LogIO> _log;
    ParamSet _currentGlobals;
    std::vector<AsciiAnnotationFileLine> _lines;
    casacore::Vector<AnnotationBase::Keyword> _globalKeysToApply;
    casacore::Int _fileVersion;
    casacore::IPosition _imShape;
    casacore::uInt _regions;
    casacore::Bool _verbose = true;

    std::shared_ptr<std::pair<casacore::MFrequency, casacore::MFrequency> > _overridingFreqRange;

    std::shared_ptr<casacore::Vector<casacore::Stokes::StokesTypes> > _overridingCorrRange;

    void _parse(const casacore::String& contents, const casacore::String& fileDesc, casacore::Bool requireImageRegion);

    casacore::Array<casacore::String> _extractTwoPairs(casacore::uInt& end, const casacore::String& string) const;

    // extract s1 and s2 from a string of the form "[s1, s2]"
    static casacore::Vector<casacore::String> _extractSinglePair(const casacore::String& string);

    void _addLine(const AsciiAnnotationFileLine& line);

    AnnotationBase::Type _getAnnotationType(
        casacore::Vector<casacore::Quantity>& qDirs,
        std::vector<casacore::Quantity>& quantities,
        casacore::String& textString,
        casacore::String& consumeMe, const casacore::String& preamble
    ) const;

    ParamSet _getCurrentParamSet(
        casacore::Bool& spectralParmsUpdated, ParamSet& newParams,
        casacore::String& consumeMe, const casacore::String& preamble
    ) const;

    void _createAnnotation(
        const AnnotationBase::Type annType,
        //const casacore::Vector<casacore::MDirection> dirs,
        const casacore::Vector<casacore::Quantity>& qDirs,
        const std::pair<casacore::Quantity, casacore::Quantity>& qFreqs,
        const std::vector<casacore::Quantity>& quantities,
        const casacore::String& textString,
        const ParamSet& currentParamSet,
        const casacore::Bool annOnly, const casacore::Bool isDifference,
        const casacore::String& preamble, casacore::Bool requireImageRegion
    );

    std::pair<casacore::Quantity, casacore::Quantity> _quantitiesFromFrequencyString(
        const casacore::String& freqString, const casacore::String& preamble
    ) const;

    static casacore::String _doLabel(casacore::String& consumeMe, const casacore::String& logPreamble);

    static casacore::String _getKeyValue(casacore::String& consumeMe, const casacore::String& preamble);

    casacore::Vector<casacore::Quantity> _extractQuantityPairAndSingleQuantity(
        casacore::String& consumeMe, const casacore::String& preamble
    ) const;

    casacore::Vector<casacore::Quantity> _extractNQuantityPairs(
            casacore::String& consumeMe, const casacore::String& preamble
    ) const;

    casacore::Vector<casacore::Quantity> _extractTwoQuantityPairs(
        casacore::String& consumeMe, const casacore::String& preamble
    ) const;

    std::pair<casacore::Quantity, casacore::Quantity> _extractSingleQuantityPair(
        const casacore::String& pair, const casacore::String& preamble
    ) const;

    void _setInitialGlobals();

    static casacore::Vector<casacore::Stokes::StokesTypes> _stokesFromString(
        const casacore::String& stokes, const casacore::String& preamble
    );

    casacore::Vector<casacore::Quantity> _extractTwoQuantityPairsAndSingleQuantity(
        casacore::String& consumeMe, const casacore::String& preamble
    ) const;

    void _extractQuantityPairAndString(
        std::pair<casacore::Quantity, casacore::Quantity>& quantities, casacore::String& string,
        casacore::String& consumeMe, const casacore::String& preamble,
        const casacore::Bool requireQuotesAroundString
    ) const;

    casacore::Vector<casacore::Quantity> _extractQuantitiesFromPair(
        const casacore::String& pair, const casacore::String& preamble
    ) const;

    void _determineVersion(
        const casacore::String& chunk, const casacore::String& filename,
        const casacore::Int requireAtLeastThisVersion
    );

    // set the casacore::Stokes/polarizations/correlations that will override all global and per line correlation
    // specifications. If multiple ranges are specified, an exception will be thrown.
    void _setOverridingCorrelations(const casacore::String& globalOverrideStokes);

    // set the (single) channel range that will override all global and per line frequency
    // specifications. If multiple ranges are specified, an exception will be thrown.
    void _setOverridingChannelRange(const casacore::String& globalOverrideChans);

};
}

#endif