#pragma once

#include "RE/G/GFxState.h"
#include "RE/G/GFxWWHelper.h"

namespace RE
{
	class GFxWStringBuffer;

	class GFxTranslator : public GFxState
	{
	public:
		inline static constexpr auto RTTI = RTTI_GFxTranslator;
		inline static constexpr auto VTABLE = VTABLE_GFxTranslator;

		using WordWrappingType = GFxWWHelper::WordWrappingType;

		// TranslateCaps is an enumeration type defining the translation capabilities of the Translator object. In general, capability flags are used to determine two things:
		//	* The type of strings can be passed to the Translate virtual function as a key.
		//	* The type of result strings that will be generated by Translate into the result buffer.
		enum class TranslateCap
		{
			kNone = 0,
			kReceiveHTML = 1 << 0,           // Specifies that Translate key can include Flash-HTML tags. If not specified, translate will only receive stripped text content (default)
			kStripTrailingNewLines = 1 << 1  // Forces all trailing new-line symbols to be stripped before the text is passed to Translate. This is important if the original text was in HTML format, since it can have a trailing paragraph tag that is turned into a new line
		};

		class TranslateInfo
		{
		public:
			enum Flag
			{
				kNone = 0,
				kTranslated = 1 << 0,
				kResultHTML = 1 << 1,
				kResultSourceHTML = 1 << 2
			};

			TranslateInfo();

			[[nodiscard]] const char*    GetInstanceName() const;                                                    // An input method which returns the instance name of the textfield being translated.
			[[nodiscard]] const wchar_t* GetKey() const;                                                             // An input method which returns the 'key' string - original text value of the textfield being translated.
			[[nodiscard]] bool           IsKeyHTML() const;                                                          // Determines if the key string (returned by GetKey) is HTML or not.
			void                         SetResult(const wchar_t* a_resultText, UPInt a_resultLen = UPINT_MAX);      // An output method which sets the translated string as a plain text.
			void                         SetResultHTML(const wchar_t* a_resultHTML, UPInt a_resultLen = UPINT_MAX);  // An output method which sets translated string as a HTML text.

			// members
			const wchar_t*                   key;           // 00
			GFxWStringBuffer*                result;        // 08
			const char*                      instanceName;  // 10
			REX::EnumSet<Flag, std::uint8_t> flags;         // 18
			std::uint8_t                     pad19;         // 19
			std::uint16_t                    pad1A;         // 1A
			std::uint32_t                    pad1C;         // 1C
		};
		static_assert(sizeof(TranslateInfo) == 0x20);

		// LineFormatDesc provides information of the line text to be formatted like the length of the text, text position etc. This structure is mainly used by OnWordWrapping to control word wrapping of the text.
		// Note that all members of LineFormatDesc marked as "[in]" are used as input values only and shouldn't be modified. Members marked as "[out]" or "[in, out]" might be modified.
		struct LineFormatDesc
		{
			enum class Alignment
			{
				kLeft = 0,
				kRight = 1,
				kCenter = 2,
				kJustify = 3
			};

			// members
			const wchar_t*                        paraText;                 // 00 - [in] Text of the current paragraph, wide-characters are used
			UPInt                                 paraTextLen;              // 08 - [in] Length of the paragraph text, in characters
			const float*                          widths;                   // 10 - [in] An array of line widths, in pixels, before the character at the corresponding index. The size of the array is NumCharsInLine + 1. Note, this is not the array of character widths. For example, there is a line that contains three characters: ABC. The NumCharInLine will be equal 3, the size of the pWidths will be 4; the pWidth[0] will be always 0 (since there are no characters before the A), the pWidth[1] will contain width of A symbol, pWidths[2] will contain width of A PLUS width of B, and, finally, pWidths[3] will contain total width of the line (width of A PLUS width of B PLUS width of C)
			UPInt                                 lineStartPos;             // 18 - [in] The text position of the first character in line. ParaTextLen[LineStartPos] might be used to get the value of this character
			UPInt                                 numCharsInLine;           // 20 - [in] Number of characters currently in the line
			float                                 visibleRectWidth;         // 28 - [in] Width, in pixels, of client rectangle. This width might be used in calculation of word wrapping position: the total width of line should not exceed this width
			float                                 currentLineWidth;         // 2C - [in] Current line width, in pixels
			float                                 lineWidthBeforeWordWrap;  // 30 - [in] Line width before the proposedWordWrapPoint, in pixels. For example, if line is ABC DEF and proposedWordWrapPoint = 3 (space) then lineWidthBeforeWordWrap will contain the width of ABC (w/o space) part of the line
			float                                 dashSymbolWidth;          // 34 - [in] Supplementary member, width of the hyphen symbol, in pixels. It might be used to calculate hyphenation
			REX::EnumSet<Alignment, std::uint8_t> alignment;                // 38 - [in] Alignment of the line
			std::uint8_t                          pad39;                    // 39
			std::uint16_t                         pad3A;                    // 3A
			std::uint32_t                         pad3C;                    // 3C
			UPInt                                 proposedWordWrapPoint;    // 40 - [in,out] An index in the line of the proposed word wrap position. For example, if the line text is "ABC DEF" and only "ABC DE" fits in visibleRectWidth then the proposedWordWrapPoint will be equal to 3. Note, this is the index in line, not in text (paraText), not in line. Use lineStartPos to calculate the proposed word wrapping position in the text. The user's OnWordWrapping method should change this member if it is necessary to change the word wrapping position according to custom rules
			bool                                  useHyphenation;           // 48 - [out] The OnWordWrapping method may set this to true to indicate to put hyphen symbol at the word-wrapping position. This might be useful for implementing hyphenation
			std::uint8_t                          pad49;                    // 49
			std::uint16_t                         pad4A;                    // 4A
			std::uint32_t                         pad4C;                    // 4C
		};
		static_assert(sizeof(LineFormatDesc) == 0x50);

		GFxTranslator();
		explicit GFxTranslator(WordWrappingType a_wwMode);
		~GFxTranslator() override = default;  // 00

		// add
		[[nodiscard]] virtual TranslateCap GetCaps() const;                            // 01 - { return TranslateCap::kNone; } - Specifies capabilities of the Translate implementation
		virtual void                       Translate(TranslateInfo* a_translateInfo);  // 02 - { return; } - Translate method implements a UTF-8/UCS-2 translation interface and performs lookup of 'a_translateInfo->GetKey()' string for language translation, filling in the destination buffer by calling TranslateInfo::SetResult or TranslateInfo::SetResultHTML method. 'a_translateInfo' is guaranteed to be not null. If neither TranslateInfo::SetResult nor TranslateInfo::SetResultHTML is called then original text will not be changed
		virtual bool                       OnWordWrapping(LineFormatDesc* a_desc);     // 03 - OnWordWrapping is a virtual method, a callback, which is invoked once a necessity of word-wrapping for any text field is determined. This method is invoked only if custom word-wrapping is turned on by using the Translator(a_wwMode) constructor

		[[nodiscard]] bool CanReceiveHTML() const;
		[[nodiscard]] bool NeedStripNewLines() const;
		[[nodiscard]] bool HandlesCustomWordWrapping() const;

		// members
		REX::EnumSet<WordWrappingType, std::uint32_t> wwMode;  // 18
		std::uint32_t                                 pad1C;   // 1C
	};
	static_assert(sizeof(GFxTranslator) == 0x20);
}
