#include "..\script_component.hpp"

/*
 * File: fnc_initContextService.sqf
 * Author: IDSolutions
 * Date: 2026-03-27
 * Public: No
 *
 * Description:
 * Initializes the garage context service for local garage context and nearby state.
 *
 * Arguments:
 * None
 *
 * Return Value:
 * Garage context service object [HASHMAP OBJECT]
 *
 * Example:
 * call forge_client_garage_fnc_initContextService;
 */

#pragma hemtt ignore_variables ["_self"]
GVAR(GarageContextServiceBaseClass) = compileFinal createHashMapFromArray [
    ["#type", "GarageContextServiceBaseClass"],
    ["#create", compileFinal { _self set ["lastContext", createHashMap]; }],
    ["#delete", compileFinal { _self set ["lastContext", createHashMap]; }],
    ["createDefaultContext", compileFinal {
        createHashMapFromArray [
            ["name", "Vehicle Garage"],
            ["anchorPosition", getPosATL player],
            ["sourceObject", objNull],
            ["garageType", ""],
            ["spawnHeading", getDir player],
            ["spawnPosition", player getPos [8, getDir player]],
            ["spawnLanes", createHashMap],
            ["spawnRadius", 6],
            ["nearbyRadius", 30],
            ["laneRadius", 150]
        ]
    }],
    ["findNearbyGarageObject", compileFinal {
        private _nearestGarage = objNull;
        private _nearestDistance = 1e10;

        {
            if (isNull _x || { !(_x getVariable ["isGarage", false]) }) then { continue; };
            private _distance = player distance2D _x;
            if (_distance < _nearestDistance) then {
                _nearestDistance = _distance;
                _nearestGarage = _x;
            };
        } forEach (player nearObjects 12);

        _nearestGarage
    }],
    ["resolveGarageName", compileFinal {
        params [["_garageObject", objNull, [objNull]]];

        if (isNull _garageObject) exitWith { "Vehicle Garage" };

        private _displayName = _garageObject getVariable ["garageName", ""];
        if (_displayName isNotEqualTo "") exitWith { _displayName };

        private _varName = vehicleVarName _garageObject;
        if (_varName isEqualTo "") exitWith { "Vehicle Garage" };

        _varName
    }],
    ["buildMarkerLane", compileFinal {
        params [["_markerName", "", [""]], ["_garageObject", objNull, [objNull]]];

        if (_markerName isEqualTo "" || { markerShape _markerName isEqualTo "" }) exitWith { createHashMap };

        private _spawnCategory = GVAR(GarageHelperService) call ["inferGarageCategory", [_markerName]];
        if (_spawnCategory isEqualTo "") exitWith { createHashMap };

        private _spawnPosition = markerPos _markerName;
        private _interactionPosition = if (isNull _garageObject) then { _spawnPosition } else { getPosATL _garageObject };
        private _markerDistance = if (isNull _garageObject) then { player distance2D _spawnPosition } else { _garageObject distance2D _spawnPosition };
        private _garageVarName = if (isNull _garageObject) then { "" } else { toLowerANSI (vehicleVarName _garageObject) };
        private _markerKey = toLowerANSI _markerName;
        private _nameScore = 0;

        if (_garageVarName isNotEqualTo "" && { (_markerKey find _garageVarName) >= 0 }) then {
            _nameScore = -50;
        };

        createHashMapFromArray [
            ["name", _markerName],
            ["interactionPosition", _interactionPosition],
            ["sourceObject", _garageObject],
            ["spawnCategory", _spawnCategory],
            ["spawnHeading", markerDir _markerName],
            ["spawnPosition", _spawnPosition],
            ["score", _markerDistance + _nameScore]
        ]
    }],
    ["discoverSpawnLanes", compileFinal {
        params [["_garageObject", objNull, [objNull]]];

        private _laneRadius = (_self call ["createDefaultContext", []]) getOrDefault ["laneRadius", 150];
        private _lanes = createHashMap;

        {
            private _markerName = _x;
            if ((toLowerANSI _markerName find "garage") < 0) then { continue; };

            private _entry = _self call ["buildMarkerLane", [_markerName, _garageObject]];
            if (_entry isEqualTo createHashMap) then { continue; };

            private _spawnPosition = _entry getOrDefault ["spawnPosition", []];
            if (_spawnPosition isEqualTo []) then { continue; };

            private _distance = if (isNull _garageObject) then { player distance2D _spawnPosition } else { _garageObject distance2D _spawnPosition };
            if (_distance > _laneRadius) then { continue; };

            private _spawnCategory = _entry getOrDefault ["spawnCategory", ""];
            private _currentEntry = _lanes getOrDefault [_spawnCategory, createHashMap];

            if (_currentEntry isEqualTo createHashMap || { (_entry getOrDefault ["score", 1e10]) < (_currentEntry getOrDefault ["score", 1e10]) }) then {
                _lanes set [_spawnCategory, _entry];
            };
        } forEach allMapMarkers;

        _lanes
    }],
    ["selectSpawnLane", compileFinal {
        params [
            ["_lanes", createHashMap, [createHashMap]],
            ["_preferredCategory", "", [""]],
            ["_defaultPosition", [], [[]]],
            ["_defaultHeading", 0, [0]]
        ];

        private _normalizedCategory = GVAR(GarageHelperService) call ["normalizeGarageCategory", [_preferredCategory]];
        private _lane = createHashMap;

        if (_normalizedCategory isNotEqualTo "") then {
            _lane = _lanes getOrDefault [_normalizedCategory, createHashMap];
        };

        if (_lane isEqualTo createHashMap) then {
            {
                private _candidate = _lanes getOrDefault [_x, createHashMap];
                if (_candidate isNotEqualTo createHashMap) exitWith { _lane = _candidate; };
            } forEach ["cars", "armor", "helis", "planes", "naval", "other"];
        };

        if (_lane isEqualTo createHashMap) then {
            _lane = createHashMapFromArray [
                ["spawnCategory", _normalizedCategory],
                ["spawnHeading", _defaultHeading],
                ["spawnPosition", _defaultPosition]
            ];
        };

        _lane
    }],
    ["getSpawnLane", compileFinal {
        params [["_category", "", [""]], ["_context", createHashMap, [createHashMap]]];

        private _resolvedContext = _context;
        if (_resolvedContext isEqualTo createHashMap) then {
            _resolvedContext = _self call ["getContext", []];
        };

        private _spawnLanes = _resolvedContext getOrDefault ["spawnLanes", createHashMap];
        private _defaultPosition = _resolvedContext getOrDefault ["spawnPosition", getPosATL player];
        private _defaultHeading = _resolvedContext getOrDefault ["spawnHeading", getDir player];
        _self call ["selectSpawnLane", [_spawnLanes, _category, _defaultPosition, _defaultHeading]]
    }],
    ["getExactSpawnLane", compileFinal {
        params [["_category", "", [""]], ["_context", createHashMap, [createHashMap]]];

        private _resolvedContext = _context;
        if (_resolvedContext isEqualTo createHashMap) then {
            _resolvedContext = _self call ["getContext", []];
        };

        private _normalizedCategory = GVAR(GarageHelperService) call ["normalizeGarageCategory", [_category]];
        if (_normalizedCategory isEqualTo "") exitWith { createHashMap };

        private _spawnLanes = _resolvedContext getOrDefault ["spawnLanes", createHashMap];
        _spawnLanes getOrDefault [_normalizedCategory, createHashMap]
    }],
    ["resolveContext", compileFinal {
        private _context = _self call ["createDefaultContext", []];
        private _garageObject = _self call ["findNearbyGarageObject", []];
        private _garageName = _self call ["resolveGarageName", [_garageObject]];
        private _garageType = "";
        private _anchorPosition = getPosATL player;
        private _spawnHeading = getDir player;
        private _spawnPosition = player getPos [8, _spawnHeading];
        private _spawnLanes = createHashMap;

        if (!isNull _garageObject) then {
            _garageType = GVAR(GarageHelperService) call ["normalizeGarageCategory", [_garageObject getVariable ["garageType", ""]]];
            _anchorPosition = getPosATL _garageObject;
            _spawnHeading = getDir _garageObject;
            _spawnPosition = _garageObject getPos [8, _spawnHeading];
            _spawnLanes = _self call ["discoverSpawnLanes", [_garageObject]];
        };

        private _selectedLane = _self call ["selectSpawnLane", [_spawnLanes, _garageType, _spawnPosition, _spawnHeading]];
        _spawnHeading = _selectedLane getOrDefault ["spawnHeading", _spawnHeading];
        _spawnPosition = _selectedLane getOrDefault ["spawnPosition", _spawnPosition];

        _context set ["name", _garageName];
        _context set ["anchorPosition", _anchorPosition];
        _context set ["sourceObject", _garageObject];
        _context set ["garageType", _garageType];
        _context set ["spawnHeading", _spawnHeading];
        _context set ["spawnPosition", _spawnPosition];
        _context set ["spawnLanes", _spawnLanes];
        _self set ["lastContext", _context];
        _context
    }],
    ["getContext", compileFinal { _self call ["resolveContext", []] }],
    ["buildNearbyState", compileFinal {
        private _context = _self call ["getContext", []];
        private _anchorPosition = _context getOrDefault ["anchorPosition", []];
        private _spawnPosition = _context getOrDefault ["spawnPosition", getPosATL player];
        private _spawnRadius = _context getOrDefault ["spawnRadius", 6];
        private _nearbyRadius = _context getOrDefault ["nearbyRadius", 30];
        private _nearbyOrigin = [_anchorPosition, _spawnPosition] select (_anchorPosition isEqualTo []);
        private _nearbyVehicles = [];
        private _nearbyEntities = [];
        private _candidateVehicles = [];
        { _candidateVehicles pushBackUnique _x; } forEach (_nearbyOrigin nearEntities [["Car", "Tank", "Air", "Ship"], _nearbyRadius]);
        { _candidateVehicles pushBackUnique _x; } forEach ((getPosATL player) nearEntities [["Car", "Tank", "Air", "Ship"], _nearbyRadius]);
        { _candidateVehicles pushBackUnique _x; } forEach (nearestObjects [_nearbyOrigin, ["AllVehicles"], _nearbyRadius]);
        { _candidateVehicles pushBackUnique _x; } forEach (nearestObjects [getPosATL player, ["AllVehicles"], _nearbyRadius]);
        {
            if (isNull _x) then { continue; };
            if (_x isKindOf "CAManBase") then { continue; };
            if !(_x isKindOf "Car" || _x isKindOf "Tank" || _x isKindOf "Air" || _x isKindOf "Ship") then { continue; };
            _nearbyEntities pushBackUnique _x;
        } forEach _candidateVehicles;
        {
            if (isNull _x) then { continue; };
            private _builtVehicle = GVAR(GarageHelperService) call ["buildNearbyVehicle", [_x, _nearbyOrigin]];
            if (_builtVehicle isEqualTo createHashMap) then { continue; };
            _nearbyVehicles pushBack _builtVehicle;
        } forEach _nearbyEntities;
        private _nearbyVehiclePairs = _nearbyVehicles apply { [_x getOrDefault ["distance", 0], _x] };
        _nearbyVehiclePairs sort true;
        _nearbyVehicles = _nearbyVehiclePairs apply { _x param [1, createHashMap] };
        private _spawnBlocked = ((_spawnPosition nearEntities [["Car", "Tank", "Air", "Ship"], _spawnRadius]) + (nearestObjects [_spawnPosition, ["Car", "Tank", "Air", "Ship"], _spawnRadius])) isNotEqualTo [];
        createHashMapFromArray [["session", createHashMapFromArray [["garageName", _context getOrDefault ["name", "Vehicle Garage"]], ["nearbyCount", count _nearbyVehicles], ["spawnBlocked", _spawnBlocked], ["spawnStatus", ["Ready", "Blocked"] select _spawnBlocked]]], ["nearby", createHashMapFromArray [["vehicles", _nearbyVehicles]]]]
    }]
];

GVAR(GarageContextService) = createHashMapObject [GVAR(GarageContextServiceBaseClass)];
GVAR(GarageContextService)
