/*
 * Copyright 2018 Arm Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SPIRV_CROSS_PARSED_IR_HPP
#define SPIRV_CROSS_PARSED_IR_HPP

#include "spirv_common.hpp"
#include <stdint.h>
#include <unordered_map>
#include <vector>

namespace spirv_cross
{

// This data structure holds all information needed to perform cross-compilation and reflection.
// It is the output of the Parser, but any implementation could create this structure.
// It is intentionally very "open" and struct-like with some helper functions to deal with decorations.
// Parser is the reference implementation of how this data structure should be filled in.

class ParsedIR
{
public:
	// Resizes ids, meta and block_meta.
	void set_id_bounds(uint32_t bounds);

	// The raw SPIR-V, instructions and opcodes refer to this by offset + count.
	std::vector<uint32_t> spirv;

	// Holds various data structures which inherit from IVariant.
	std::vector<Variant> ids;

	// Various meta data for IDs, decorations, names, etc.
	std::vector<Meta> meta;

	// Declared capabilities and extensions in the SPIR-V module.
	// Not really used except for reflection at the moment.
	std::vector<spv::Capability> declared_capabilities;
	std::vector<std::string> declared_extensions;

	// Meta data about blocks. The cross-compiler needs to query if a block is either of these types.
	// It is a bitset as there can be more than one tag per block.
	enum BlockMetaFlagBits
	{
		BLOCK_META_LOOP_HEADER_BIT = 1 << 0,
		BLOCK_META_CONTINUE_BIT = 1 << 1,
		BLOCK_META_LOOP_MERGE_BIT = 1 << 2,
		BLOCK_META_SELECTION_MERGE_BIT = 1 << 3,
		BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4
	};
	using BlockMetaFlags = uint8_t;
	std::vector<BlockMetaFlags> block_meta;
	std::unordered_map<uint32_t, uint32_t> continue_block_to_loop_header;

	// Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction.
	// Entry points can therefore be seen as some sort of meta structure.
	std::unordered_map<uint32_t, SPIREntryPoint> entry_points;
	uint32_t default_entry_point = 0;

	struct Source
	{
		uint32_t version = 0;
		bool es = false;
		bool known = false;
		bool hlsl = false;

		Source() = default;
	};

	Source source;

	// Decoration handling methods.
	// Can be useful for simple "raw" reflection.
	// However, most members are here because the Parser needs most of these,
	// and might as well just have the whole suite of decoration/name handling in one place.
	void set_name(uint32_t id, const std::string &name);
	const std::string &get_name(uint32_t id) const;
	void set_decoration(uint32_t id, spv::Decoration decoration, uint32_t argument = 0);
	void set_decoration_string(uint32_t id, spv::Decoration decoration, const std::string &argument);
	bool has_decoration(uint32_t id, spv::Decoration decoration) const;
	uint32_t get_decoration(uint32_t id, spv::Decoration decoration) const;
	const std::string &get_decoration_string(uint32_t id, spv::Decoration decoration) const;
	const Bitset &get_decoration_bitset(uint32_t id) const;
	void unset_decoration(uint32_t id, spv::Decoration decoration);

	// Decoration handling methods (for members of a struct).
	void set_member_name(uint32_t id, uint32_t index, const std::string &name);
	const std::string &get_member_name(uint32_t id, uint32_t index) const;
	void set_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0);
	void set_member_decoration_string(uint32_t id, uint32_t index, spv::Decoration decoration,
	                                  const std::string &argument);
	uint32_t get_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration) const;
	const std::string &get_member_decoration_string(uint32_t id, uint32_t index, spv::Decoration decoration) const;
	bool has_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration) const;
	const Bitset &get_member_decoration_bitset(uint32_t id, uint32_t index) const;
	void unset_member_decoration(uint32_t id, uint32_t index, spv::Decoration decoration);

	void mark_used_as_array_length(uint32_t id);
	uint32_t increase_bound_by(uint32_t count);
	Bitset get_buffer_block_flags(const SPIRVariable &var) const;

private:
	template <typename T>
	T &get(uint32_t id)
	{
		return variant_get<T>(ids[id]);
	}

	template <typename T>
	const T &get(uint32_t id) const
	{
		return variant_get<T>(ids[id]);
	}
};
} // namespace spirv_cross

#endif