/****************************************************************************
 *
 *   Copyright (c) 2024 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file offline_task_parser.cpp
 *
 * JSON parser implementation for offline tasks
 */

#include "offline_task_parser.hpp"
#include "cJSON.h"

#include <px4_platform_common/log.h>
#include <stdio.h>
#include <string.h>

namespace offline_task_manager
{

static constexpr size_t MAX_FILE_SIZE = 65536; // 64KB max

bool load_offline_tasks_from_json(
	const char *file_path,
	OfflineTask *tasks,
	int max_tasks,
	int &task_count)
{
	task_count = 0;

	if (!file_path || !tasks || max_tasks <= 0) {
		PX4_ERR("invalid parameters");
		return false;
	}

	/* Open file */
	FILE *fp = fopen(file_path, "r");
	if (!fp) {
		PX4_ERR("failed to open file: %s", file_path);
		return false;
	}

	/* Get file size */
	fseek(fp, 0, SEEK_END);
	long file_size = ftell(fp);
	fseek(fp, 0, SEEK_SET);

	if (file_size <= 0 || file_size > (long)MAX_FILE_SIZE) {
		PX4_ERR("invalid file size: %ld", file_size);
		fclose(fp);
		return false;
	}

	/* Read file */
	char *buffer = new char[file_size + 1];
	if (!buffer) {
		PX4_ERR("failed to allocate buffer");
		fclose(fp);
		return false;
	}

	size_t read_size = fread(buffer, 1, file_size, fp);
	fclose(fp);

	if (read_size != (size_t)file_size) {
		PX4_ERR("failed to read file completely");
		delete[] buffer;
		return false;
	}

	buffer[file_size] = '\0';

	/* Parse JSON */
	cJSON *root = cJSON_Parse(buffer);
	delete[] buffer;

	if (!root) {
		PX4_ERR("failed to parse JSON");
		return false;
	}

	if (!cJSON_IsObject(root)) {
		PX4_ERR("JSON root is not an object");
		cJSON_Delete(root);
		return false;
	}

	/* Get tasks array */
	cJSON *tasks_array = cJSON_GetObjectItem(root, "tasks");
	if (!tasks_array) {
		PX4_ERR("missing 'tasks' field");
		cJSON_Delete(root);
		return false;
	}

	if (!cJSON_IsArray(tasks_array)) {
		PX4_ERR("'tasks' is not an array");
		cJSON_Delete(root);
		return false;
	}

	int array_size = cJSON_GetArraySize(tasks_array);
	if (array_size <= 0) {
		PX4_ERR("tasks array is empty");
		cJSON_Delete(root);
		return false;
	}

	if (array_size > max_tasks) {
		PX4_WARN("tasks array truncated from %d to %d", array_size, max_tasks);
		array_size = max_tasks;
	}

	/* Parse each task */
	for (int i = 0; i < array_size; i++) {
		cJSON *task_obj = cJSON_GetArrayItem(tasks_array, i);
		if (!task_obj || !cJSON_IsObject(task_obj)) {
			PX4_WARN("task %d is not an object, skipping", i);
			continue;
		}

		OfflineTask task{};

		/* task_id (required) */
		cJSON *task_id = cJSON_GetObjectItem(task_obj, "task_id");
		if (!task_id || !cJSON_IsNumber(task_id)) {
			PX4_WARN("task %d missing 'task_id', skipping", i);
			continue;
		}
		task.task_id = (int32_t)cJSON_GetNumberValue(task_id);

		/* lat (required) */
		cJSON *lat = cJSON_GetObjectItem(task_obj, "lat");
		if (!lat || !cJSON_IsNumber(lat)) {
			PX4_WARN("task %d missing 'lat', skipping", i);
			continue;
		}
		task.lat = cJSON_GetNumberValue(lat);
		if (task.lat < -90.0 || task.lat > 90.0) {
			PX4_WARN("task %d invalid lat: %.6f, skipping", i, task.lat);
			continue;
		}

		/* lon (required) */
		cJSON *lon = cJSON_GetObjectItem(task_obj, "lon");
		if (!lon || !cJSON_IsNumber(lon)) {
			PX4_WARN("task %d missing 'lon', skipping", i);
			continue;
		}
		task.lon = cJSON_GetNumberValue(lon);
		if (task.lon < -180.0 || task.lon > 180.0) {
			PX4_WARN("task %d invalid lon: %.6f, skipping", i, task.lon);
			continue;
		}

		/* alt (required) */
		cJSON *alt = cJSON_GetObjectItem(task_obj, "alt");
		if (!alt || !cJSON_IsNumber(alt)) {
			PX4_WARN("task %d missing 'alt', skipping", i);
			continue;
		}
		task.alt = (float)cJSON_GetNumberValue(alt);
		if (task.alt < 0.0f || task.alt > 5000.0f) {
			PX4_WARN("task %d invalid alt: %.2f, skipping", i, task.alt);
			continue;
		}

		/* priority (required) */
		cJSON *priority = cJSON_GetObjectItem(task_obj, "priority");
		if (!priority || !cJSON_IsNumber(priority)) {
			PX4_WARN("task %d missing 'priority', skipping", i);
			continue;
		}
		task.priority = (int32_t)cJSON_GetNumberValue(priority);
		if (task.priority < 1 || task.priority > 5) {
			PX4_WARN("task %d invalid priority: %d, skipping", i, task.priority);
			continue;
		}

		/* must_first (optional, default false) */
		cJSON *must_first = cJSON_GetObjectItem(task_obj, "must_first");
		if (must_first && cJSON_IsBool(must_first)) {
			task.must_first = cJSON_IsNumber(must_first) ?
					  (cJSON_GetNumberValue(must_first) != 0) :
					  cJSON_IsNumber(must_first);
		} else {
			task.must_first = false;
		}

		/* Initialize other fields */
		task.reachable = true;
		task.selected = false;

		/* Store task */
		tasks[task_count] = task;
		task_count++;

		PX4_INFO("loaded task %d: id=%d lat=%.6f lon=%.6f alt=%.1f pri=%d must_first=%s",
			 i, task.task_id, task.lat, task.lon, task.alt, task.priority,
			 task.must_first ? "true" : "false");
	}

	cJSON_Delete(root);

	if (task_count == 0) {
		PX4_ERR("no valid tasks loaded");
		return false;
	}

	PX4_INFO("successfully loaded %d tasks", task_count);
	return true;
}

} // namespace offline_task_manager
