/*
	Author: Aaron Clark - EpochMod.com

    Contributors:

	Description:
	Base building base building with physics

    Licence:
    Arma Public License Share Alike (APL-SA) - https://www.bistudio.com/community/licenses/arma-public-license-share-alike

    Github:
    https://github.com/EpochModTeam/Epoch/tree/release/Sources/epoch_code/compile/building/EPOCH_simulSwap.sqf

    Example:
    [_object] spawn EPOCH_simulSwap;

    Parameter(s):
		_this select 0: OBJECT - Base building object

	Returns:
	NOTHING
*/
//[[[cog import generate_private_arrays ]]]
private ["_allowedSnapObjects","_allowedSnapPoints","_cfgBaseBuilding","_class","_create","_currentTarget","_dir2","_direction","_disallowed","_distance","_distanceMod","_distanceNear","_energyCost","_isSnap","_lastCheckTime","_maxHeight","_nearestObject","_nearestObjectRaw","_newObj","_objSlot","_objType","_object","_oemType","_offset","_onContactEH","_pOffset","_playerEnergy","_playerEnergyKeyFinal","_playerdistance","_pos2","_prevSnapDistance","_previousDistanceNear","_rejectMove","_return","_simulClassConfig","_snapArrayPara","_snapArrayPerp","_snapConfig","_snapDistance","_snapObjects","_snapPointsPara","_snapPointsPerp","_snapPos","_snapPosition","_snapType","_textureSlot","_up2","_vel2","_velocityTransformation","_worldspace"];
//[[[end]]]

if !(isNil "EPOCH_simulSwap_Lock") exitWith{};

_object = param [0,objNull];
if (isNull _object) exitWith{ EPOCH_target = objNull; };

_objType = typeOf _object;

_isSnap = false;

_playerEnergyKeyFinal = "EPOCH_playerEnergy";
if !(isNil "_playerEnergyKey") then {_playerEnergyKeyFinal = _playerEnergyKey};
_playerEnergy = missionNamespace getVariable [_playerEnergyKeyFinal,[]];

if (_playerEnergy <= 0) exitWith {
	["Need Energy", 5] call Epoch_message;
};
if !(_object call EPOCH_isBuildAllowed) exitWith{};

EPOCH_simulSwap_Lock = true;
_return = _object;
_velocityTransformation = [];
_prevSnapDistance = 0;
_distanceMod = 0;
_oemType = (typeOf _object);

_cfgBaseBuilding = 'CfgBaseBuilding' call EPOCH_returnConfig;

_simulClassConfig = (_cfgBaseBuilding >> _oemType >> "simulClass");
if (isText(_simulClassConfig)) then {
	_class = getText(_simulClassConfig);
	_create = true;
	_allowedSnapPoints = getArray(_cfgBaseBuilding >> _class >> "allowedSnapPoints");
	_allowedSnapObjects = ["Constructions_static_F"];
	_snapObjects = _cfgBaseBuilding >> _class >> "allowedSnapObjects";
	_energyCost = getNumber(_cfgBaseBuilding >> _class >> "energyCost");
	_maxHeight = getNumber(_cfgBaseBuilding >> _class >> "maxHeight");
	if (_energyCost == 0) then {
		_energyCost = 0.1;
	};
	if (_maxHeight == 0) then {
		_maxHeight = 500;
	};
	if !(EPOCH_maxBuildingHeight == 0) then {
		_maxHeight = _maxHeight min EPOCH_maxBuildingHeight;
	};
	if (isArray(_snapObjects)) then {
		_allowedSnapObjects = getArray(_snapObjects);
	};
	_newObj = _object;
	if (_create) then {
		_worldspace = [(getposATL _object),(vectordir _object),(vectorup _object)];
		_objSlot = _object getVariable["BUILD_SLOT", -1];
		_textureSlot = _object getVariable["TEXTURE_SLOT", 0];
		deleteVehicle _object;
		waitUntil {sleep 0.01; isNull _object};
		_newObj = createVehicle [_class, (_worldspace select 0), [], 0, "CAN_COLLIDE"];
		if (_objSlot != -1) then {
			_newObj setVariable ["BUILD_SLOT",_objSlot,true];
		};

		_newObj setposATL (_worldspace select 0);
		_newObj setVectorDirAndUp [_worldspace select 1,_worldspace select 2];

		if (_textureSlot != 0) then {
			[_newObj, _textureSlot, player, Epoch_personalToken] remoteExec ["EPOCH_server_paintBUILD",2];
		};
	};
	EP_velocityTransformation = [];
	EPOCH_oldTarget = EPOCH_target;
	EPOCH_target = _newObj;
	_currentTarget = EPOCH_target;
	EPOCH_velTransform = true;
	EPOCH_objHold = 0;
	_onContactEH = _currentTarget addEventHandler["EpeContactStart", { if ((_this select 1) isKindOf "LandVehicle" || (_this select 1) isKindOf "Air" || (_this select 1) isKindOf "Ship" || (_this select 1) isKindOf "Tank") then{ EPOCH_target = objNull }; }];
	EP_snap = objNull;
	_previousDistanceNear = 0;
	_offset = player worldToModel (getposATL _currentTarget);
	if (surfaceiswater (getpos _currentTarget)) then {
		_offset = player worldToModel (getposASL _currentTarget);
	};
	EPOCH_X_OFFSET = _offset select 0;
	EPOCH_Y_OFFSET = _offset select 1;
	EPOCH_Z_OFFSET = _offset select 2;
	_lastCheckTime = diag_tickTime;
	while {EPOCH_target == _currentTarget} do {
		_playerEnergy = missionNamespace getVariable [_playerEnergyKeyFinal,[]];
		if (_playerEnergy <= 0) exitWith { EPOCH_target = objNull; };
		_rejectMove = false;
		if ((diag_tickTime - _lastCheckTime) > 10) then {
			_lastCheckTime = diag_tickTime;
			_rejectMove = !(EPOCH_target call EPOCH_isBuildAllowed);
		};
		if (_rejectMove) exitWith { EPOCH_target = objNull; };
		_playerdistance = player distance EPOCH_target;
		if (_playerdistance < 10) then {
			_isSnap = false;
			_snapPosition = [0,0,0];
			_snapType = "para";
			_nearestObject = objNull;

			// see if this can prevent riding on object
			if (EPOCH_Y_OFFSET < 3.6) then {
				EPOCH_Y_OFFSET = EPOCH_Y_OFFSET + 0.1;
			};

			_pos2 = player modelToWorld[EPOCH_X_OFFSET, EPOCH_Y_OFFSET, EPOCH_Z_OFFSET];
			if (_pos2 select 2 > _maxHeight) then {
				_pos2 set[2, _maxHeight];
			};
			_distance = _pos2 distance EPOCH_target;
			if (EPOCH_buildMode == 1) then {
				if (isNull _nearestObject) then {
					{
						_nearestObjectRaw = nearestObject [EPOCH_target,_x];
						_distanceNear = EPOCH_target distance _nearestObjectRaw;
						if (_distanceNear < _previousDistanceNear) then {
							_nearestObject = _nearestObjectRaw;
						};
						_previousDistanceNear = _distanceNear;
					} forEach _allowedSnapObjects;
				};
				if (!isNull _nearestObject) then {
					_snapConfig = _cfgBaseBuilding >> (typeOf _nearestObject);
					_snapPointsPara = getArray(_snapConfig >> "snapPointsPara");
					_snapPointsPerp = getArray(_snapConfig >> "snapPointsPerp");
					_snapArrayPara = [];
					{
						if (_x in _allowedSnapPoints) then {
							_pOffset = getArray (_snapConfig >> _x);
							_snapPos = _nearestObject modelToWorld _pOffset;
							if ((_pos2 distance _snapPos) < 3) then {
								_snapArrayPara pushBackUnique _snapPos;
							};
						};
					} forEach _snapPointsPara;
					_snapArrayPerp = [];
					{
						if (_x in _allowedSnapPoints) then {
							_pOffset = getArray (_snapConfig >> _x);
							_snapPos = _nearestObject modelToWorld _pOffset;
							if ((_pos2 distance _snapPos) < 3) then {
								_snapArrayPerp pushBackUnique _snapPos;
							};
						};
					} forEach _snapPointsPerp;
					{
						_snapDistance = _pos2 distance _x;
						if (_snapDistance < 1 && (_snapDistance < _prevSnapDistance)) exitWith {
							_isSnap = true;
							_snapPosition = _x;
							_snapType = "para";
						};
						_prevSnapDistance = _snapDistance;
					} forEach _snapArrayPara;
					{
						_snapDistance = _pos2 distance _x;
						if (_snapDistance < 1 && (_snapDistance < _prevSnapDistance)) exitWith {
							_isSnap = true;
							_snapPosition = _x;
							_snapType = "perp";
						};
						_prevSnapDistance = _snapDistance;
					} forEach _snapArrayPerp;
				};
				if (_isSnap && _distance < 5) then {
					_pos2 = _snapPosition;
					_vel2 = (velocity _nearestObject);
					_direction = getDir _nearestObject;
					if (_snapType == "perp") then {
						_direction = _direction - (_snapPosition getDir _nearestObject);
					} else {
						_direction = 0;
					};
					if (EPOCH_snapDirection > 0) then {
						if (EPOCH_snapDirection == 1) then {
							_direction = _direction + 90;
						};
						if (EPOCH_snapDirection == 2) then {
							_direction = _direction + 180;
						};
						if (EPOCH_snapDirection == 3) then {
							_direction = _direction + 270;
						};
					};
					if (_direction > 360) then {
						_direction = _direction - 360;
					};
					if (_direction < 0) then {
						_direction = 360 + _direction;
					};
					_dir2 = [vectorDir _nearestObject, _direction] call BIS_fnc_returnVector;
					_up2 = (vectorUp _nearestObject);
					EP_velocityTransformation = [AGLToASL _pos2,_vel2,_dir2,_up2];
					EPOCH_arr_snapObjects = [_nearestObject, _currentTarget];
				};
			};
			if (!_isSnap) then {
				if (EPOCH_doRotate) then {
					_vel2 = (velocity player);
					_dir2 = [vectorDir player, EPOCH_buildDirection] call BIS_fnc_returnVector;
					_up2 = (vectorUp player);
					EPOCH_doRotate = false;
					EP_velocityTransformation = [AGLToASL _pos2,_vel2,_dir2,_up2];
				} else {
					EP_velocityTransformation = [];
				};
				EPOCH_arr_snapObjects = [];
			};
		};
		[_playerEnergyKeyFinal, -_energyCost, 2500 , 0] call EPOCH_fnc_setVariableLimited;
		uiSleep 0.1;
	};
	_currentTarget removeEventHandler["EpeContactStart", _onContactEH];
	EPOCH_velTransform = false;
	EPOCH_arr_snapObjects = [];
	_disallowed = ["Tarp_SIM_EPOCH", "Freezer_SIM_EPOCH", "Fridge_SIM_EPOCH", "Shelf_SIM_EPOCH", "Pelican_SIM_EPOCH", "Wardrobe_SIM_EPOCH", "Bed_SIM_EPOCH", "Couch_SIM_EPOCH", "Cooker_SIM_EPOCH", "Chair_SIM_EPOCH", "Filing_SIM_EPOCH", "Table_SIM_EPOCH", "Locker_SIM_EPOCH", "ToolRack_SIM_EPOCH", "Shoebox_SIM_EPOCH", "Bunk_SIM_EPOCH", "Jack_SIM_EPOCH", "toilet_SIM_EPOCH", "KitchenSink_SIM_EPOCH"];
	if !(_class in _disallowed) then {
		_currentTarget spawn EPOCH_countdown;
	};
	_currentTarget setVelocity [0,0,-0.01];
};
[] spawn{
	uiSleep 2;
	EPOCH_simulSwap_Lock = nil;
};