#pragma once

#include <locale>

constexpr auto TABWIDTH_MIN = 2;
constexpr auto TABWIDTH_DEFAULT = 8;
constexpr auto TABWIDTH_MAX = 16;

constexpr auto DEF_TBLSZ1 = 32;
constexpr auto DEF_TBLSZ2 = 128;


class CBKToken;

class CReader
{
	public:
		enum class FILE_CHARSET : uint8_t
		{
			FILEERROR,
			UNDEFINE,
			KOI8,
			CP866,
			CP1251,
			UTF8,
			UTF16LE
		};
		enum class CHAR_TYPE : uint8_t
		{
			CT_EOF,     // конец файла
			LN,         // перевод строки
			SPACES,     // пробельные символы
			LETTERS,    // буквы
			DIGITS,     // цифры
			OTHERS      // прочие символы
		};

	private:
		static const std::array<wchar_t, 256> m_cp866;
		static const std::array<wchar_t, 256> m_cp1251;

		using RdrState = struct RdrState_t
		{
			wchar_t     chCurrent{ 0 };                 // текущий символ
			wchar_t     chNext{ 0 };                    // следующий символ
			CHAR_TYPE   nCurrCharType{ CHAR_TYPE::LN }; // тип текущего символа
			size_t      nLen{ 0 };
			size_t      nPos{ 0 };
			int         nLineNum{ 0 };
			int64_t     nFilePos{ 0 };
			std::wstring strCurStr;
		};
		std::vector<RdrState> m_vState;

		fs::path        m_strFileName;          // имя обрабатываемого файла
		wchar_t         m_chCurrent{ 0 };       // текущий символ
		wchar_t         m_chNext{ 0 };          // следующий символ
		CHAR_TYPE       m_nCurrCharType{ CHAR_TYPE::LN }; // тип текущего символа
		FILE           *m_file{ nullptr };
		FILE_CHARSET    m_nCharset{ FILE_CHARSET::UNDEFINE };
		int             m_nLineNum{ 0 };// номер текущей строки
		std::wstring    m_strCur;       // собственно текущая строка. Нужна чтобы выводить её при выводе ошибки.
		size_t          m_nLen{ 0 };    // длина текущей строки
		size_t          m_nPos{ size_t(-1)};   // указатель на текущий символ в строке

		/*
		Используется алгоритм автоматического определения кодировки текста (ALT, WIN, KOI)
		Описание алгоритма: http://ivr.webzone.ru/articles/defcod_2/
		(c) Иван Рощин, Москва, 2004.
		*/

		static const std::array <uint8_t, DEF_TBLSZ2> table_2s;
		/* =========================================================================
		Вспомогательная функция alt2num.
		Вход: a - код русской буквы в кодировке ALT.
		Выход: порядковый номер этой буквы (0-31).
		========================================================================= */
		int alt2num(int a) const
		{
			if (a >= 0xE0)
			{
				a -= 0x30;
			}

			return (a & 31);
		}
		/* =========================================================================
		Вспомогательная функция koi2num.
		Вход: a - код русской буквы в кодировке KOI.
		Выход: порядковый номер этой буквы (0-31).
		========================================================================= */

		int koi2num(int a) const
		{
			static const std::array <uint8_t, DEF_TBLSZ1> t =
			{
				30, 0, 1, 22, 4, 5, 20, 3, 21, 8, 9, 10, 11, 12, 13, 14, 15, 31,
				16, 17, 18, 19, 6, 2, 28, 27, 7, 24, 29, 25, 23, 26
			};
			return (t[a & (DEF_TBLSZ1 - 1)]);
		}

		/* =========================================================================
		Вспомогательная функция work_2s - обработка двухбуквенного сочетания.
		Вход:  с1 - порядковый номер первой буквы (0-31),
		c2 - порядковый номер второй буквы (0-31),
		check - надо ли проверять, встречалось ли сочетание раньше
		(1 - да, 0 - нет),
		buf - адрес массива с информацией о встреченных сочетаниях.
		Выход: 0 - указанное сочетание уже встречалось раньше,
		1 - сочетание не встречалось раньше и является допустимым,
		2 - сочетание не встречалось раньше и является недопустимым.
		========================================================================= */
		int work_2s(int c1, int c2, bool check, std::span<uint8_t> buf) const;

		/* =========================================================================
		Вспомогательная функция def_code - определение кодировки текста.
		Вход:
		n - количество различных сочетаний русских букв (1-255), которого
		достаточно для определения кодировки.
		Выход: определённая кодировка
		========================================================================= */
		FILE_CHARSET    def_code(int n = 128);
		int             get_char();
		// прочитать очередной символ из файла.
		wchar_t         getChar();
		FILE_CHARSET    AnalyzeCharset(const fs::path &strFileName);

		uint16_t        ReadWord() const;
		bool            WriteWord(uint16_t w) const;

		uint32_t        ReadUTF8Char() const;
		int             WriteUTF8Char(uint32_t tch);

		uint32_t        ReadUtf16LEChar();
		int             WriteUtf16LEChar(uint32_t tch);

		uint32_t        o2w(uint8_t b);
		uint8_t         w2o(uint32_t tch);
		uint32_t        a2w(uint8_t b);
		uint8_t         w2a(uint32_t tch);

		void            getString();


	public:
		CReader(const fs::path &strFileName, const FILE_CHARSET nInputCharset = FILE_CHARSET::UNDEFINE);
		virtual ~CReader();

		const fs::path &GetFileName() const
		{
			return m_strFileName;
		}

		FILE_CHARSET GetFileCharset() const
		{
			return m_nCharset;
		}

		wchar_t readChar(bool bSkipWS = false);

		/*
		*Посмотреть текущий символ, без передвижения указателя
		*/
		inline wchar_t getCurrChar() const
		{
			return m_chCurrent;
		}

		/*
		* Посмотреть следующий символ, без передвижения указателя
		*/
		inline wchar_t getNextChar() const
		{
			return std::toupper(m_chNext, std::locale());
		}

		inline CHAR_TYPE getCurrCharType() const
		{
			return m_nCurrCharType;
		}

		inline CHAR_TYPE getNextCharType() const
		{
			return AnalyzeChar(std::toupper(m_chNext, std::locale()));
		}

		bool            isEOF() const;
		CHAR_TYPE       AnalyzeChar(wchar_t ch) const;
		bool            SkipWhitespacesLN(wchar_t &ch);
		bool            SkipWhitespaces(wchar_t &ch);
		bool            readToken(CBKToken *token, wchar_t &ch);
		bool            readTokenR(CBKToken *token, wchar_t &ch);

		inline const std::wstring &getCurString() const
		{
			return m_strCur;
		}

		inline int      getLineNum() const
		{
			return m_nLineNum;
		}

// идея сохранения состояния. нужно сохранять текущие переменные
// и положение указателей в файлах.
// чтобы можно было делать откат к нужной позиции.
// Для этого сделаем вектор и три функции: StoreState, RestoreState
// и PopState - сброс сохранённого состояния, если не пригодилось.
// и тогда можно будет различать число и локальную метку
// и в арифметическом выражении можно станет использовать локальные метки.

		void            StoreState();
		wchar_t         RestoreState();
		void            PopState();
};

/*
При выдаче символов, у нас m_chCurrent - текущий символ,
m_chNext - следующий за ним, а указатель в файле указывает на
следующий за следующим.
 */
