/*
 * Copyright 2024-2025 MicroEJ Corp. All rights reserved.
 * This library is provided in source code for use, modification and test, subject to license terms.
 * Any modification of the source code will break MicroEJ Corp. warranties on the whole library.
 */
package ej.mwt;

import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Logger;

import ej.bon.ByteArray;
import ej.bon.Constants;
import ej.microui.display.GraphicsContext;
import ej.trace.Tracer;

/*package*/ class Trace {

	/**
	 * {@link Constants#getBoolean(String) BON boolean constant} to enable/disable the logs associated with the traces.
	 * <p>
	 * If enabled, {@link Tracer#TRACE_ENABLED_CONSTANT_PROPERTY} must also be set.
	 * <p>
	 * It helps to understand the traces and more specifically the IDs associated to the desktops and widgets.
	 */
	/* package */ static final String LOG_TRACE_ENABLED_CONSTANT = "ej.mwt.debug.log.trace.enabled";

	/* package */ static final int CREATE_WIDGET_EVENT = 0;
	/* package */ static final int CREATE_DESKTOP_EVENT = 1;
	/* package */ static final int REQUEST_DESKTOP_SHOW_EVENT = 2;
	/* package */ static final int REQUEST_LAYOUT_EVENT = 3;
	/* package */ static final int REQUEST_DESKTOP_LAYOUT_EVENT = 4;
	/* package */ static final int REQUEST_RENDER_EVENT = 5;
	/* package */ static final int REQUEST_DESKTOP_RENDER_EVENT = 6;
	/* package */ static final int UPDATE_STYLE_EVENT = 7;
	/* package */ static final int COMPUTE_SIZE_EVENT = 8;
	/* package */ static final int LAYOUT_EVENT = 9;
	/* package */ static final int RENDER_EVENT = 10;
	/* package */ static final int ON_SHOWN_EVENT = 11;
	/* package */ static final int ON_HIDDEN_EVENT = 12;
	/* package */ static final int ON_SHOWN_DESKTOP_EVENT = 13;
	/* package */ static final int ON_HIDDEN_DESKTOP_EVENT = 14;
	/* package */ static final int ON_ATTACHED_EVENT = 15;
	/* package */ static final int ON_DETACHED_EVENT = 16;
	/* package */ static final int EVENT_COUNT = 64;

	/* package */ static final Tracer TRACER = new Tracer("MWT", EVENT_COUNT);
	private static final Logger LOGGER = Logger.getLogger(Trace.class.getSimpleName());

	private static int currentId = 0;
	private static final Map<Desktop, Integer> desktopsIds = new WeakHashMap<>();
	private static final Map<Widget, Integer> widgetsIds = new WeakHashMap<>();

	private Trace() {
		// Prevent initialization.
	}

	/**
	 * Should only be called if {@link Tracer#TRACE_ENABLED_CONSTANT_PROPERTY} is {@code true}.
	 */
	/* package */ static int getDesktopId(Desktop desktop) {
		Integer id = desktopsIds.get(desktop);
		if (id != null) {
			return id.intValue();
		} else {
			return -1;
		}
	}

	/**
	 * Should only be called if {@link Tracer#TRACE_ENABLED_CONSTANT_PROPERTY} is {@code true}.
	 */
	/* package */ static void createNewDesktop(Desktop desktop) {
		int id = ++currentId;
		desktopsIds.put(desktop, Integer.valueOf(id));
		Trace.TRACER.recordEvent(Trace.CREATE_DESKTOP_EVENT, id, desktop.getClass().hashCode());
		if (Constants.getBoolean(LOG_TRACE_ENABLED_CONSTANT)) {
			LOGGER.info("Desktop " + desktop + " gets ID " + id);
		}
	}

	/**
	 * Should only be called if {@link Tracer#TRACE_ENABLED_CONSTANT_PROPERTY} is {@code true}.
	 */
	/* package */ static int getWidgetId(Widget widget) {
		Integer id = widgetsIds.get(widget);
		if (id != null) {
			return id.intValue();
		} else {
			return -1;
		}
	}

	/**
	 * Should only be called if {@link Tracer#TRACE_ENABLED_CONSTANT_PROPERTY} is {@code true}.
	 */
	/* package */ static void createNewWidget(Widget widget) {
		int id = ++currentId;
		widgetsIds.put(widget, Integer.valueOf(id));
		Trace.TRACER.recordEvent(Trace.CREATE_WIDGET_EVENT, id, widget.getClass().hashCode());
		if (Constants.getBoolean(LOG_TRACE_ENABLED_CONSTANT)) {
			LOGGER.info("Widget " + widget + " gets ID " + id);
		}
	}

	/**
	 * Should only be called if {@link Tracer#TRACE_ENABLED_CONSTANT_PROPERTY} is {@code true}.
	 */
	public static int getGraphicsContextId(GraphicsContext gc) {
		return getInt(gc.getSNIContext(), 0 /*OFFSET_I_OFFSCREEEN_ID*/);
	}

	private static int getInt(byte[] sniContext, int offset) {
		if (Constants.getBoolean("com.microej.library.microui.onS3" /*MicroUIProperties.CONSTANT_ON_S3*/)) {
			// ByteArray.readInt() always uses emb endianness whereas the simulator
			// can have another endianness
			return ByteArray.readInt(sniContext, offset, ByteArray.BIG_ENDIAN);
		} else {
			return ByteArray.readInt(sniContext, offset);
		}
	}
}
