#pragma once
#include "membuffer.h"
#include "constants.h"
#include "annotations.h"
#include <map>
namespace java
{
	/**
	 * Class representing a Java .class file
	 */
	class classfile : public util::membuffer
	{
	public:
		classfile(char * data, std::size_t size) : membuffer(data, size)
		{
			valid = false;
			is_synthetic = false;
			read_be(magic);
			if(magic != 0xCAFEBABE)
				throw new classfile_exception();
			read_be(minor_version);
			read_be(major_version);
			constants.load(*this);
			read_be(access_flags);
			read_be(this_class);
			read_be(super_class);
			
			// Interfaces
			uint16_t iface_count = 0;
			read_be(iface_count);
			while (iface_count)
			{
				uint16_t iface;
				read_be(iface);
				interfaces.push_back(iface);
				iface_count --;
			}
			
			// Fields
			// read fields (and attributes from inside fields) (and possible inner classes. yay for recursion!)
			// for now though, we will ignore all attributes
			/*
			 * field_info
			 * {
			 * 	u2 access_flags;
			 * 	u2 name_index;
			 * 	u2 descriptor_index;
			 * 	u2 attributes_count;
			 * 	attribute_info attributes[attributes_count];
			 * }
			 */
			uint16_t field_count = 0;
			read_be(field_count);
			while (field_count)
			{
				// skip field stuff
				skip(6);
				// and skip field attributes
				uint16_t attr_count = 0;
				read_be(attr_count);
				while(attr_count)
				{
					skip(2);
					uint32_t attr_length = 0;
					read_be(attr_length);
					skip(attr_length);
					attr_count --;
				}
				field_count --;
			}

			// class methods
			/*
			 * method_info
			 * {
			 * 	u2 access_flags;
			 * 	u2 name_index;
			 * 	u2 descriptor_index;
			 * 	u2 attributes_count;
			 * 	attribute_info attributes[attributes_count];
			 * }
			 */
			uint16_t method_count = 0;
			read_be(method_count);
			while( method_count )
			{
				skip(6);
				// and skip method attributes
				uint16_t attr_count = 0;
				read_be(attr_count);
				while(attr_count)
				{
					skip(2);
					uint32_t attr_length = 0;
					read_be(attr_length);
					skip(attr_length);
					attr_count --;
				}
				method_count --;
			}

			// class attributes
			// there are many kinds of attributes. this is just the generic wrapper structure.
			// type is decided by attribute name. extensions to the standard are *possible*
			// class annotations are one kind of a attribute (one per class)
			/*
			 * attribute_info
			 * {
			 * 	u2 attribute_name_index;
			 * 	u4 attribute_length;
			 * 	u1 info[attribute_length];
			 * }
			 */
			uint16_t class_attr_count = 0;
			read_be(class_attr_count);
			while(class_attr_count)
			{
				uint16_t name_idx = 0;
				read_be(name_idx);
				uint32_t attr_length = 0;
				read_be(attr_length);
				
				auto name = constants[name_idx];
				if(name.str_data == "RuntimeVisibleAnnotations")
				{
					uint16_t num_annotations = 0;
					read_be(num_annotations);
					while (num_annotations)
					{
						visible_class_annotations.push_back(annotation::read(*this, constants));
						num_annotations --;
					}
				}
				else skip(attr_length);
				class_attr_count --;
			}
			valid = true;
		};
		bool valid;
		bool is_synthetic;
		uint32_t magic;
		uint16_t minor_version;
		uint16_t major_version;
		constant_pool constants;
		uint16_t access_flags;
		uint16_t this_class;
		uint16_t super_class;
		// interfaces this class implements ? must be. investigate.
		std::vector<uint16_t> interfaces;
		// FIXME: doesn't free up memory on delete
		java::annotation_table visible_class_annotations;
	};
}