#ifndef PORTAL_H
#define PORTAL_H

/*************************************************************************/
/*  portal.h                                                             */
/*************************************************************************/
/*                         This file is part of:                         */
/*                          PANDEMONIUM ENGINE                           */
/*             https://github.com/Relintai/pandemonium_engine            */
/*************************************************************************/
/* Copyright (c) 2022-present Péter Magyar.                              */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "core/containers/local_vector.h"
#include "core/containers/rid.h"
#include "scene/main/spatial.h"

class RoomManager;
class MeshInstance;
class Room;

class Portal : public Spatial {
	GDCLASS(Portal, Spatial);

	RID _portal_rid;

	friend class RoomManager;
	friend class PortalGizmoPlugin;
	friend class PortalEditorPlugin;
	friend class PortalSpatialGizmo;

public:
	// ui interface .. will have no effect after room conversion
	void set_linked_room(const NodePath &link_path);
	NodePath get_linked_room() const;

	// open and close doors
	void set_portal_active(bool p_active);
	bool get_portal_active() const;

	// whether the portal can be seen through in both directions or not
	void set_two_way(bool p_two_way) {
		_settings_two_way = p_two_way;
		_changed();
	}
	bool is_two_way() const { return _settings_two_way; }

	// call during each conversion
	void clear();

	// whether to use the room manager default
	void set_use_default_margin(bool p_use);
	bool get_use_default_margin() const;

	// custom portal margin (per portal) .. only valid if use_default_margin is off
	void set_portal_margin(real_t p_margin);
	real_t get_portal_margin() const;

	// either the default margin or the custom portal margin, depending on the setting
	real_t get_active_portal_margin() const;

	// the raw points are used for the IDE Inspector, and also to allow the user
	// to edit the geometry of the portal at runtime (they can also just change the portal node transform)
	void set_points(const PoolVector<Vector2> &p_points);
	PoolVector<Vector2> get_points() const;

	// primarily for the gizmo
	void set_point(int p_idx, const Vector2 &p_point);

	void set_include_in_bound(bool p_enabled) { _settings_include_in_bound = p_enabled; }
	bool get_include_in_bound() const { return _settings_include_in_bound; }

	String get_configuration_warning() const;

	Portal();
	~Portal();

	// whether the convention is that the normal of the portal points outward (false) or inward (true)
	// normally I'd recommend portal normal faces outward. But you may make a booboo, so this can work
	// with either convention.
	static bool _portal_plane_convention;

private:
	// updates world coords when the transform changes, and updates the visual server
	void portal_update();

	void set_linked_room_internal(const NodePath &link_path);
	bool try_set_unique_name(const String &p_name);
	bool is_portal_internal(int p_room_outer) const { return _internal && (_linkedroom_ID[0] != p_room_outer); }

	bool create_from_mesh_instance(const MeshInstance *p_mi);
	void flip();
	void _sanitize_points();
	void _update_aabb();
	static Vector3 _vec2to3(const Vector2 &p_pt) { return Vector3(p_pt.x, p_pt.y, 0.0); }
	void _sort_verts_clockwise(const Vector3 &p_portal_normal, Vector<Vector3> &r_verts);
	Plane _plane_from_points_newell(const Vector<Vector3> &p_pts);
	void resolve_links(const LocalVector<Room *, int32_t> &p_rooms, const RID &p_from_room_rid);
	void _changed();

	// nodepath to the room this outgoing portal leads to
	NodePath _settings_path_linkedroom;

	// portal can be turned on and off at runtime, for e.g.
	// opening and closing a door
	bool _settings_active;

	// user can choose not to include the portal in the convex hull of the room
	// during conversion
	bool _settings_include_in_bound;

	// portals can be seen through one way or two way
	bool _settings_two_way;

	// room from and to, ID in the room manager
	int _linkedroom_ID[2];

	// whether the portal is from a room within a room
	bool _internal;

	// normal determined by winding order
	Vector<Vector3> _pts_world;

	// points in local space of the plane,
	// not necessary in correct winding order
	// (as they can be edited by the user)
	// Note: these are saved by the IDE
	PoolVector<Vector2> _pts_local_raw;

	// sanitized
	Vector<Vector2> _pts_local;
	AABB _aabb_local;

	// center of the world points
	Vector3 _pt_center_world;

	// portal plane in world space, always pointing OUTWARD from the source room
	Plane _plane;

	// extension margin
	real_t _margin;
	bool _use_default_margin;

	// during conversion, we need to know
	// whether this portal is being imported from a mesh
	// and is using an explicitly named link room with prefix.
	// If this is not the case, and it is already a Pandemonium Portal node,
	// we will either use the assigned nodepath, or autolink.
	bool _importing_portal = false;

	// for editing
#ifdef TOOLS_ENABLED
	ObjectID _room_manager_pandemonium_ID;

	// warnings
	bool _warning_outside_room_aabb = false;
	bool _warning_facing_wrong_way = false;
	bool _warning_autolink_failed = false;
#endif

	// this is read from the gizmo
	static bool _settings_gizmo_show_margins;

public:
	// makes sure portals are not converted more than once per
	// call to rooms_convert
	int _conversion_tick = -1;

protected:
	static void _bind_methods();
	void _notification(int p_what);
};

#endif