#pragma once

#include <climits>
#include <cstdint>
#include <map>
#include <string>

#include "zeek/List.h"
#include "zeek/Obj.h"

namespace zeek::detail
	{

class RuleCondition;
class RuleAction;
class RuleHdrTest;
class RuleMatcher;
class Rule;

using rule_list = PList<Rule>;
using rule_dict = std::map<std::string, Rule*>;

class Rule
	{
public:
	Rule(const char* arg_id, const Location& arg_location)
		{
		id = util::copy_string(arg_id);
		idx = rule_counter++;
		location = arg_location;
		active = true;
		next = nullptr;
		}

	~Rule();

	const char* ID() const { return id; }
	unsigned int Index() const { return idx; }

	enum PatternType
		{
		FILE_MAGIC,
		PAYLOAD,
		HTTP_REQUEST,
		HTTP_REQUEST_BODY,
		HTTP_REQUEST_HEADER,
		HTTP_REPLY_BODY,
		HTTP_REPLY_HEADER,
		FTP,
		FINGER,
		TYPES,
		};

	bool Active() { return active; }
	void SetActiveStatus(bool new_status) { active = new_status; }
	void AddAction(RuleAction* act) { actions.push_back(act); }
	void AddCondition(RuleCondition* cond) { conditions.push_back(cond); }
	void AddHdrTest(RuleHdrTest* hdr_test) { hdr_tests.push_back(hdr_test); }
	void AddPattern(const char* str, Rule::PatternType type, uint32_t offset = 0,
	                uint32_t depth = INT_MAX);
	void AddRequires(const char* id, bool opposite_direction, bool negate);

	const Location& GetLocation() const { return location; }

	void PrintDebug();

	static const char* TypeToString(Rule::PatternType type);

private:
	friend class RuleMatcher;

	void SortHdrTests();

	using rule_action_list = PList<RuleAction>;
	using rule_condition_list = PList<RuleCondition>;
	using rule_hdr_test_list = PList<RuleHdrTest>;

	rule_hdr_test_list hdr_tests;
	rule_condition_list conditions;
	rule_action_list actions;

	// Matching of this rule can depend on the state of other rules.
	struct Precond
		{
		const char* id;
		Rule* rule; // set by RuleMatcher
		bool opposite_dir; // if true, rule must match other dir.
		bool negate; // negate test
		};

	using precond_list = PList<Precond>;

	precond_list preconds;
	rule_list dependents; // rules w/ us as a precondition
	                      // (set by RuleMatcher)

	const char* id;
	unsigned int idx; // unique index of this rule
	bool active; // set the active status of the rule, default true

	struct Pattern
		{
		char* pattern; // the pattern itself
		PatternType type;
		int id; // ID of pattern (for identifying it within regexps)
		uint32_t offset;
		uint32_t depth;
		};

	using pattern_list = PList<Pattern>;
	pattern_list patterns;

	Rule* next; // Linkage within RuleHdrTest tree:
	            // Ptr to next rule using the same RuleHdrTests

	Location location;

	// Rules and payloads are numbered individually.
	static unsigned int rule_counter;
	static unsigned int pattern_counter;

	// Array of rules indexed by payloadid.
	static rule_list rule_table;
	};

	} // namespace zeek::detail
