/* SPDX-FileCopyrightText: 2026 Project Tick
 * SPDX-FileContributor: Project Tick
 * SPDX-License-Identifier: GPL-3.0-or-later WITH LicenseRef-MeshMC-MMCO-Module-Exception-1.0
 *
 *   MeshMC - A Custom Launcher for Minecraft
 *   Copyright (C) 2026 Project Tick
 *
 *   This program 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, with the additional permission
 *   described in the MeshMC MMCO Module Exception 1.0.
 *
 *   This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 *   You should have received a copy of the MeshMC MMCO Module Exception 1.0
 *   along with this program.  If not, see <https://projecttick.org/licenses/>.
 */

#pragma once
#include "classfile.h"
#include <map>
#include <vector>

namespace java
{
	enum element_value_type : uint8_t {
		INVALID = 0,
		STRING = 's',
		ENUM_CONSTANT = 'e',
		CLASS = 'c',
		ANNOTATION = '@',
		ARRAY = '[',			// one array dimension
		PRIMITIVE_INT = 'I',	// integer
		PRIMITIVE_BYTE = 'B',	// signed byte
		PRIMITIVE_CHAR = 'C',	// Unicode character code point in the Basic
								// Multilingual Plane, encoded with UTF-16
		PRIMITIVE_DOUBLE = 'D', // double-precision floating-point value
		PRIMITIVE_FLOAT = 'F',	// single-precision floating-point value
		PRIMITIVE_LONG = 'J',	// long integer
		PRIMITIVE_SHORT = 'S',	// signed short
		PRIMITIVE_BOOLEAN = 'Z' // true or false
	};
	/**
	 * The element_value structure is a discriminated union representing the
	 * value of an element-value pair. It is used to represent element values in
	 * all attributes that describe annotations
	 * - RuntimeVisibleAnnotations
	 * - RuntimeInvisibleAnnotations
	 * - RuntimeVisibleParameterAnnotations
	 * - RuntimeInvisibleParameterAnnotations).
	 *
	 * The element_value structure has the following format:
	 */
	class element_value
	{
	  protected:
		element_value_type type;
		constant_pool& pool;

	  public:
		element_value(element_value_type type, constant_pool& pool)
			: type(type), pool(pool) {};
		virtual ~element_value() {}

		element_value_type getElementValueType()
		{
			return type;
		}

		virtual std::string toString() = 0;

		static element_value* readElementValue(util::membuffer& input,
											   constant_pool& pool);
	};

	/**
	 * Each value of the annotations table represents a single runtime-visible
	 * annotation on a program element. The annotation structure has the
	 * following format:
	 */
	class annotation
	{
	  public:
		typedef std::vector<std::pair<uint16_t, element_value*>> value_list;

	  protected:
		/**
		 * The value of the type_index item must be a valid index into the
		 * constant_pool table. The constant_pool entry at that index must be a
		 * CONSTANT_Utf8_info (§4.4.7) structure representing a field descriptor
		 * representing the annotation type corresponding to the annotation
		 * represented by this annotation structure.
		 */
		uint16_t type_index;
		/**
		 * map between element_name_index and value.
		 *
		 * The value of the element_name_index item must be a valid index into
		 * the constant_pool table. The constant_pool entry at that index must
		 * be a CONSTANT_Utf8_info (§4.4.7) structure representing a valid field
		 * descriptor (§4.3.2) that denotes the name of the annotation type
		 * element represented by this element_value_pairs entry.
		 */
		value_list name_val_pairs;
		/**
		 * Reference to the parent constant pool
		 */
		constant_pool& pool;

	  public:
		annotation(uint16_t type_index, constant_pool& pool)
			: type_index(type_index), pool(pool) {};
		~annotation()
		{
			for (unsigned i = 0; i < name_val_pairs.size(); i++) {
				delete name_val_pairs[i].second;
			}
		}
		void add_pair(uint16_t key, element_value* value)
		{
			name_val_pairs.push_back(std::make_pair(key, value));
		};
		value_list::const_iterator begin()
		{
			return name_val_pairs.cbegin();
		}
		value_list::const_iterator end()
		{
			return name_val_pairs.cend();
		}
		std::string toString();
		static annotation* read(util::membuffer& input, constant_pool& pool);
	};
	typedef std::vector<annotation*> annotation_table;

	/// type for simple value annotation elements
	class element_value_simple : public element_value
	{
	  protected:
		/// index of the constant in the constant pool
		uint16_t index;

	  public:
		element_value_simple(element_value_type type, uint16_t index,
							 constant_pool& pool)
			: element_value(type, pool), index(index) {
				  // TODO: verify consistency
			  };
		uint16_t getIndex()
		{
			return index;
		}
		virtual std::string toString()
		{
			return pool[index].toString();
		};
	};
	/// The enum_const_value item is used if the tag item is 'e'.
	class element_value_enum : public element_value
	{
	  protected:
		/**
		 * The value of the type_name_index item must be a valid index into the
		 * constant_pool table. The constant_pool entry at that index must be a
		 * CONSTANT_Utf8_info (§4.4.7) structure representing a valid field
		 * descriptor (§4.3.2) that denotes the internal form of the binary name
		 * (§4.2.1) of the type of the enum constant represented by this
		 * element_value structure.
		 */
		uint16_t typeIndex;
		/**
		 * The value of the const_name_index item must be a valid index into the
		 * constant_pool table. The constant_pool entry at that index must be a
		 * CONSTANT_Utf8_info (§4.4.7) structure representing the simple name of
		 * the enum constant represented by this element_value structure.
		 */
		uint16_t valueIndex;

	  public:
		element_value_enum(element_value_type type, uint16_t typeIndex,
						   uint16_t valueIndex, constant_pool& pool)
			: element_value(type, pool), typeIndex(typeIndex),
			  valueIndex(valueIndex)
		{
			// TODO: verify consistency
		}
		uint16_t getValueIndex()
		{
			return valueIndex;
		}
		uint16_t getTypeIndex()
		{
			return typeIndex;
		}
		virtual std::string toString()
		{
			return "enum value";
		};
	};

	class element_value_class : public element_value
	{
	  protected:
		/**
		 * The class_info_index item must be a valid index into the
		 * constant_pool table. The constant_pool entry at that index must be a
		 * CONSTANT_Utf8_info (§4.4.7) structure representing the return
		 * descriptor (§4.3.3) of the type that is reified by the class
		 * represented by this element_value structure.
		 *
		 * For example, 'V' for Void.class, 'Ljava/lang/Object;' for Object,
		 * etc.
		 *
		 * Or in plain english, you can store type information in annotations.
		 * Yay.
		 */
		uint16_t classIndex;

	  public:
		element_value_class(element_value_type type, uint16_t classIndex,
							constant_pool& pool)
			: element_value(type, pool), classIndex(classIndex)
		{
			// TODO: verify consistency
		}
		uint16_t getIndex()
		{
			return classIndex;
		}
		virtual std::string toString()
		{
			return "class";
		};
	};

	/// nested annotations... yay
	class element_value_annotation : public element_value
	{
	  private:
		annotation* nestedAnnotation;

	  public:
		element_value_annotation(element_value_type type,
								 annotation* nestedAnnotation,
								 constant_pool& pool)
			: element_value(type, pool), nestedAnnotation(nestedAnnotation) {};
		~element_value_annotation()
		{
			if (nestedAnnotation) {
				delete nestedAnnotation;
				nestedAnnotation = nullptr;
			}
		}
		virtual std::string toString()
		{
			return "nested annotation";
		};
	};

	/// and arrays!
	class element_value_array : public element_value
	{
	  public:
		typedef std::vector<element_value*> elem_vec;

	  protected:
		elem_vec values;

	  public:
		element_value_array(element_value_type type,
							std::vector<element_value*>& values,
							constant_pool& pool)
			: element_value(type, pool), values(values) {};
		~element_value_array()
		{
			for (unsigned i = 0; i < values.size(); i++) {
				delete values[i];
			}
		};
		elem_vec::const_iterator begin()
		{
			return values.cbegin();
		}
		elem_vec::const_iterator end()
		{
			return values.cend();
		}
		virtual std::string toString()
		{
			return "array";
		};
	};
} // namespace java