/*
 * Copyright The WildFly Authors
 * SPDX-License-Identifier: Apache-2.0
 */
package org.wildfly.clustering.server.immutable;

import java.io.File;
import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.security.Permission;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.MonthDay;
import java.time.Period;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.Chronology;
import java.time.chrono.Era;
import java.time.format.DateTimeFormatter;
import java.time.format.DecimalStyle;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit;
import java.time.temporal.ValueRange;
import java.time.temporal.WeekFields;
import java.time.zone.ZoneOffsetTransition;
import java.time.zone.ZoneOffsetTransitionRule;
import java.time.zone.ZoneRules;
import java.util.Collections;
import java.util.Currency;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.UUID;
import java.util.stream.Stream;

/**
 * Default set of immutability tests.
 * @author Paul Ferraro
 */
enum DefaultImmutability implements Immutability {

	IDENTITY(Immutability.identity(List.of(
				Collections.emptyEnumeration(),
				Collections.emptyIterator(),
				Collections.emptyList(),
				Collections.emptyListIterator(),
				Collections.emptyMap(),
				Collections.emptyNavigableMap(),
				Collections.emptyNavigableSet(),
				Collections.emptySet(),
				Collections.emptySortedMap(),
				Collections.emptySortedSet()))),
	CLASS(Immutability.classes(List.of(
				BigDecimal.class,
				BigInteger.class,
				Boolean.class,
				Byte.class,
				Character.class,
				Class.class,
				Currency.class,
				DateTimeFormatter.class,
				DecimalStyle.class,
				Double.class,
				Duration.class,
				File.class,
				Float.class,
				Inet4Address.class,
				Inet6Address.class,
				InetSocketAddress.class,
				Instant.class,
				Integer.class,
				Locale.class,
				LocalDate.class,
				LocalDateTime.class,
				LocalTime.class,
				Long.class,
				MathContext.class,
				MonthDay.class,
				Period.class,
				Short.class,
				StackTraceElement.class,
				String.class,
				URI.class,
				URL.class,
				UUID.class,
				ValueRange.class,
				WeekFields.class,
				Year.class,
				YearMonth.class,
				ZoneOffset.class,
				ZoneOffsetTransition.class,
				ZoneOffsetTransitionRule.class,
				ZoneRules.class,
				ZonedDateTime.class))),
	ABSTRACT_CLASS(Immutability.instanceOf(List.of(
				Chronology.class,
				ChronoLocalDate.class,
				Clock.class,
				Enum.class, // In theory, one could implement a mutable enum, but that would just be weird.
				Era.class,
				Path.class,
				Permission.class,
				TemporalField.class,
				TemporalUnit.class,
				TimeZone.class, // Strictly speaking, this class is mutable, although in practice it is never mutated.
				ZoneId.class))),
	ANNOTATION(new Immutability() {
		// Match any @Immutable annotation
		// e.g. JCIP, Hibernate, or auto-generated classes (e.g. via Immutables library)
		@Override
		public boolean test(Object object) {
			return (object == null) || Stream.of(object.getClass().getAnnotations()).map(Annotation::annotationType).map(Class::getSimpleName).anyMatch("Immutable"::equals);
		}
	}),
	;
	private final Immutability immutability;

	DefaultImmutability(Immutability immutability) {
		this.immutability = immutability;
	}

	@Override
	public boolean test(Object object) {
		return this.immutability.test(object);
	}
}
