/*
 * Copyright 2022-2024 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.widget.debug;

import ej.microui.display.Colors;
import ej.microui.display.Font;
import ej.mwt.style.background.Background;
import ej.mwt.style.background.RoundedBackground;
import ej.mwt.style.dimension.Dimension;
import ej.mwt.style.dimension.FixedDimension;
import ej.mwt.style.dimension.OptimalDimension;
import ej.mwt.style.dimension.RelativeDimension;
import ej.mwt.style.outline.Outline;
import ej.mwt.style.outline.border.RoundedBorder;
import ej.mwt.stylesheet.selector.ClassSelector;
import ej.mwt.stylesheet.selector.DisabledSelector;
import ej.mwt.stylesheet.selector.EnabledSelector;
import ej.mwt.stylesheet.selector.EvenChildSelector;
import ej.mwt.stylesheet.selector.FirstChildSelector;
import ej.mwt.stylesheet.selector.LastChildSelector;
import ej.mwt.stylesheet.selector.NotSelector;
import ej.mwt.stylesheet.selector.NthChildSelector;
import ej.mwt.stylesheet.selector.OddChildSelector;
import ej.mwt.stylesheet.selector.RootSelector;
import ej.mwt.stylesheet.selector.Selector;
import ej.mwt.stylesheet.selector.StateSelector;
import ej.mwt.stylesheet.selector.StrictTypeSelector;
import ej.mwt.stylesheet.selector.TypeSelector;
import ej.mwt.stylesheet.selector.UniversalSelector;
import ej.mwt.stylesheet.selector.combinator.AdjacentSiblingCombinator;
import ej.mwt.stylesheet.selector.combinator.AndCombinator;
import ej.mwt.stylesheet.selector.combinator.ChildCombinator;
import ej.mwt.stylesheet.selector.combinator.Combinator;
import ej.mwt.stylesheet.selector.combinator.DescendantCombinator;
import ej.mwt.stylesheet.selector.combinator.GeneralSiblingCombinator;
import ej.mwt.util.Alignment;

/* package */ class Stringifier {

	private Stringifier() {
		// Forbid instantiation
	}

	public static String toString(Dimension dimension) {
		String string;
		if (dimension instanceof FixedDimension) {
			string = toString((FixedDimension) dimension);
		} else if (dimension instanceof OptimalDimension) {
			string = toString((OptimalDimension) dimension);
		} else if (dimension instanceof RelativeDimension) {
			string = toString((RelativeDimension) dimension);
		} else {
			string = getClassName(dimension);
		}
		return string;
	}

	/**
	 * Serializes the given color.
	 * <p>
	 * Attempts to detect a named color. Otherwise, the hexa-decimal format {@code #RRGGBB} is used.
	 *
	 * @param color
	 *            the color to serialize.
	 * @return the serialized color.
	 * @see Colors named colors
	 */
	public static String colorToString(int color) {
		switch (color) {
		// @formatter:off
		case Colors.BLACK:
			return "black";
		case Colors.BLUE:
			return "blue";
		case Colors.CYAN:
			return "cyan";
		case Colors.GRAY:
			return "gray";
		case Colors.GREEN:
			return "green";
		case Colors.LIME:
			return "lime";
		case Colors.MAGENTA:
			return "magenta";
		case Colors.MAROON:
			return "maroon";
		case Colors.NAVY:
			return "navy";
		case Colors.OLIVE:
			return "olive";
		case Colors.PURPLE:
			return "purple";
		case Colors.RED:
			return "red";
		case Colors.SILVER:
			return "silver";
		case Colors.TEAL:
			return "teal";
		case Colors.WHITE:
			return "white";
		case Colors.YELLOW:
			return "yellow";
		// @formatter:on
		default:
			String hex = Integer.toHexString(color);
			char[] chars = new char[7];
			chars[0] = '#';
			for (int i = 1, n = hex.length(), len = chars.length; i < len; i++) {
				int j = i + n - len;
				chars[i] = j < 0 ? '0' : hex.charAt(j);
			}
			return new String(chars);
		}
	}

	public static String toString(Outline border) {
		String string;
		if (border instanceof RoundedBorder) {
			string = toString((RoundedBorder) border);
		} else {
			string = getClassName(border);
		}
		return string;
	}

	public static String toString(Background background) {
		String string;
		if (background instanceof RoundedBackground) {
			string = toString((RoundedBackground) background);
		} else {
			string = getClassName(background);
		}
		return string;
	}

	public static String toString(Font font) {
		String string = getClassName(font);
		String descriptor = font.getDescriptor();
		if (!descriptor.isEmpty()) {
			string += "[" + descriptor + "]";
		}
		return string;
	}

	public static String alignmentToString(int alignment) {
		switch (alignment) {
		// @formatter:off
		case Alignment.BOTTOM:
			return "bottom";
		case Alignment.HCENTER:
			return "hcenter";
		case Alignment.LEFT:
			return "left";
		case Alignment.RIGHT:
			return "right";
		case Alignment.TOP:
			return "top";
		case Alignment.VCENTER:
			return "vcenter";
		default:
			throw new IllegalArgumentException();
		// @formatter:on
		}
	}

	private static String getClassName(Object object) {
		return getClassName(object.getClass());
	}

	private static String getClassName(Class<?> clazz) {
		String simpleName = clazz.getSimpleName();
		int length = simpleName.length();
		if (simpleName.charAt(length - 1) == '@' && simpleName.charAt(0) != '@') {
			simpleName = simpleName.substring(0, length - 1);
		}
		return simpleName;
	}

	private static String toString(RoundedBackground roundedBackground) {
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append(getClassName(RoundedBackground.class));
		stringBuilder.append("[color=").append(colorToString(roundedBackground.getColor()));
		stringBuilder.append(", radius=").append(roundedBackground.getCornerRadius());
		stringBuilder.append(", thickness=").append(roundedBackground.getBorderThickness());
		stringBuilder.append(']');
		return stringBuilder.toString();
	}

	private static String toString(RoundedBorder roundedBorder) {
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append(getClassName(RoundedBorder.class));
		stringBuilder.append("[color=").append(colorToString(roundedBorder.getColor()));
		stringBuilder.append(", radius=").append(roundedBorder.getCornerRadius());
		stringBuilder.append(", thickness=").append(roundedBorder.getThickness());
		stringBuilder.append(']');
		return stringBuilder.toString();
	}

	public static String toString(FixedDimension dimension) {
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append(getClassName(FixedDimension.class));
		stringBuilder.append("[width=").append(dimension.getWidth());
		stringBuilder.append(", height=").append(dimension.getHeight());
		stringBuilder.append(']');
		return stringBuilder.toString();
	}

	public static String toString(OptimalDimension dimension) {
		String type;
		if (dimension == OptimalDimension.OPTIMAL_DIMENSION_X) {
			type = "X";
		} else if (dimension == OptimalDimension.OPTIMAL_DIMENSION_Y) {
			type = "Y";
		} else if (dimension == OptimalDimension.OPTIMAL_DIMENSION_XY) {
			type = "XY";
		} else {
			type = "unknown";
		}
		return getClassName(OptimalDimension.class) + "[" + type + "]";
	}

	public static String toString(RelativeDimension dimension) {
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append(getClassName(FixedDimension.class));
		stringBuilder.append("[widthRatio=").append(dimension.getWidthRatio());
		stringBuilder.append(", heightRatio=").append(dimension.getHeightRatio());
		stringBuilder.append(']');
		return stringBuilder.toString();
	}

	public static String toString(Selector selector) { // NOSONAR ignore cognitive complexity.
		StringBuilder stringBuilder = new StringBuilder();
		if (selector instanceof ClassSelector) {
			stringBuilder.append(".class ").append(((ClassSelector) selector).getClassName());
		} else if (selector instanceof DisabledSelector) {
			stringBuilder.append(":disabled");
		} else if (selector instanceof EnabledSelector) {
			stringBuilder.append(":enabled");
		} else if (selector instanceof EvenChildSelector) {
			stringBuilder.append(":nth-child(even)");
		} else if (selector instanceof FirstChildSelector) {
			stringBuilder.append(":first-child");
		} else if (selector instanceof LastChildSelector) {
			stringBuilder.append(":last-child");
		} else if (selector instanceof NotSelector) {
			stringBuilder.append(":not()");
		} else if (selector instanceof NthChildSelector) {
			stringBuilder.append(":nth-child(").append(((NthChildSelector) selector).getIndex()).append(')');
		} else if (selector instanceof OddChildSelector) {
			stringBuilder.append(":nth-child(odd)");
		} else if (selector instanceof RootSelector) {
			stringBuilder.append(":root");
		} else if (selector instanceof StateSelector) {
			stringBuilder.append(":state ").append(((StateSelector) selector).getState());
		} else if (selector instanceof StrictTypeSelector) {
			stringBuilder.append("type ").append(((StrictTypeSelector) selector).getType().getName());
		} else if (selector instanceof TypeSelector) {
			stringBuilder.append("type ").append(((TypeSelector) selector).getType().getName());
		} else if (selector instanceof UniversalSelector) {
			stringBuilder.append("*");
		} else if (selector instanceof Combinator) {
			stringBuilder.append(toString((Combinator) selector));
		} else {
			stringBuilder.append(selector.toString());
		}
		return stringBuilder.toString();
	}

	public static String toString(Combinator combinator) {
		String separator;
		if (combinator instanceof AdjacentSiblingCombinator) {
			separator = " + ";
		} else if (combinator instanceof AndCombinator) {
			separator = " and ";
		} else if (combinator instanceof ChildCombinator) {
			separator = " > ";
		} else if (combinator instanceof DescendantCombinator) {
			separator = " ";
		} else if (combinator instanceof GeneralSiblingCombinator) {
			separator = " ~ ";
		} else {
			separator = combinator.getClass().getName();
		}
		return toString(combinator.getFirstSelector()) + separator + toString(combinator.getSecondSelector());
	}
}
