/*
    SPDX-License-Identifier: GPL-3.0-or-later
    Copyright © 2018-2022 Amebis
*/

#pragma once

#include <sal.h>
#include <list>
#include <memory>
#include <string>
#include <vector>


namespace ZRCola {
    ///
    /// Decomposed sequence parser
    ///
    class DecompParser
    {
    public:
        ///
        /// Base class for expressions
        ///
        class Expression
        {
        };


        ///
        /// Expression in "parentheses"
        ///
        class Parentheses : public Expression
        {
        public:
            inline Parentheses(_In_opt_ wchar_t open = 0, _In_opt_ wchar_t close = 0, _In_opt_ Expression *expr = NULL) : m_open(open), m_close(close), m_expr(expr) {}

        public:
            wchar_t m_open, m_close;
            std::unique_ptr<Expression> m_expr;
        };


        ///
        /// Operator
        ///
        class Operator : public Expression
        {
        public:
            inline Operator(_In_opt_ wchar_t chr = 0) : m_char(chr) {}

            wchar_t m_char;
            std::vector<wchar_t> m_mod;
        };


        ///
        /// Character
        ///
        class Character : public Operator
        {
        public:
            inline Character(_In_opt_ wchar_t chr = 0) : Operator(chr) {}

            std::vector<wchar_t> m_prefix;
        };


        ///
        /// Ligature
        ///
        class Ligature : public Expression
        {
        public:
            class Element : public Operator
            {
            public:
                inline Element(_In_opt_ wchar_t chr = 0) : Operator(chr) {}

                std::unique_ptr<Expression> m_el;
            };

            inline Ligature(_In_opt_ Expression *first = NULL) : m_first(first) {}

            std::unique_ptr<Expression> m_first;
            std::list<std::unique_ptr<Element> > m_rest;
        };


        ///
        /// Two joined terms
        ///
        class Joined2 : public Operator
        {
        public:
            Joined2(_In_opt_ wchar_t chr = 0, _In_opt_ Expression *first = NULL) : Operator(chr), m_first(first) {}

            std::unique_ptr<Expression> m_first, m_second;
        };


        ///
        /// Three joined terms
        ///
        class Joined3 : public Joined2
        {
        public:
            Joined3(_In_opt_ wchar_t chr = 0, _In_opt_ Expression *first = NULL) : Joined2(chr, first) {}

            std::unique_ptr<Expression> m_third;
        };


    public:
        DecompParser(_In_ const std::wstring &decomposed);

    protected:
        inline void NextChar();
        void ParsePrefixes(_Inout_ std::vector<wchar_t> &prefix);
        void ParseModifiers(_Inout_ std::vector<wchar_t> &mod);
        Expression* ParseParentheses(_In_ wchar_t open, _In_ wchar_t close);
        Expression* ParseCharacter();
        Expression* ParseTerm();
        Expression* ParseExpression();

    public:
        Expression* Parse();

    protected:
        std::wstring::size_type m_idx;      ///< Index of current character
        const std::wstring &m_decomposed;   ///< Decomposed character sequence to analyse
        wchar_t m_sym;                      ///< Current character
    };
}


inline void ZRCola::DecompParser::NextChar()
{
    m_sym = m_idx < m_decomposed.length() ? m_decomposed[m_idx++] : 0;
}
