/*
 * Copyright 2021-2023 epimethix@protonmail.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.epimethix.lumicore.common;

/**
 * This Exception indicates any problem with the configuration of
 * {@code AbstractRepository} or {@code AbstractDatabase}.
 * <p>
 * This Exception is only thrown during initialization of the constructors.
 * 
 * @author epimethix
 */
@SuppressWarnings("serial")
public class ConfigurationException extends Exception {

	public final static int UNKNOWN_ERROR = 1;
//	public final static int ENTITY_CLASS_HAS_NO_FIELDS = 2;
	public static final int AUTO_DETECT_FIELD_TYPE_FAILED = 3;
	public static final int WRONG_CONSTRUCTOR = 4;
	public static final int PRIMARY_KEY_INCONSISTENCY = 5;
	public static final int MULTIPLE_PRIMARY_KEYS_CANNOT_BE_AUTOGENERATED = 6;
	public static final int COULD_NOT_READ_CONSTRAINT = 7;
	public static final int FOREIGN_KEY_CONSTRAINT_MALFORMED = 8;
	public static final int ILLEGAL_ENTITY_NAME = 9;
	public static final int ILLEGAL_FIELD_NAME = 10;
	public static final int FIELD_NAME_COLLISION = 11;
	public static final int ENTITY_NAME_COLLISION = 12;
	public static final int USER_ENTITY_SHOULD_EXTEND_USER_CLASS = 13;
	public static final int SOFT_DELETE_REQUIRES_META_ENTITY = 14;
	public static final int ENTITY_GETTER_MISSING = 15;
	public static final int ENTITY_SETTER_MISSING = 16;
	public static final int EMPTY_CONSTRUCTOR_MISSING = 17;
	public static final int UNSUPPORTED_TYPE_USED = 18;
	public static final int DATABASE_ARGUMENT_MUST_NOT_BE_NULL = 19;
	public static final int ENTITY_CLASS_ARGUMENT_MUST_NOT_BE_NULL = 20;
	public static final int ENTITY_ID_CLASS_ARGUMENT_MUST_NOT_BE_NULL = 21;
	public static final int OUT_DATED_SOFTWARE_IN_USE = 22;
	public static final int COULD_NOT_ESTABLISH_CONNECTION = 23;
	public static final int WRONG_APPLICATION_ID = 24;
	public static final int CIRCULAR_FOREIGN_KEY_CONSTRUCT_DETECTED = 25;
	public static final int COULD_NOT_INSTANTIATE_REPOSITORY = 26;
	public static final int MULTIPLE_DB_CLASSES_NEED_REPOSITORIES_ANNOTATION = 27;
	public static final int INITIALIZE_DATABASE_FAILED = 28;
	public static final int INITIALIZE_REPOSITORY_FAILED = 29;
	public static final int THERE_SHOULD_BE_ZERO_OR_ONE_SWING_UI_IMPLEMENTATIONS = 30;
	public static final int INITIALIZE_DB_APPLICATION_FAILED = 31;
	public static final int INITIALIZE_SWING_COMPONENT_FAILED = 32;
	public static final int INITIALIZE_SWING_UI_FAILED = 33;
	public static final int INITIALIZE_COMPONENT_FAILED = 34;
	public static final int FIELD_TYPE_IS_NOT_MAPPABLE = 35;
	public static final int ENTITY_CONSTRUCTOR_INACCESSIBLE = 36;
	public static final int MULTIPLE_SELF_REFERENCES = 37;
	public static final int REFERENCED_REPOSITORY_IS_MISSING = 38;
	public static final int CUSTOM_IMPLEMENTATION_OF_META_ENTITY_DETECTED = 39;
	public static final int THERE_SHOULD_BE_ZERO_OR_ONE_LOGGER_CONFIGURATIONS = 40;
	public static final int LOGGER_CONFIGURATION_NEEDS_EMPTY_CONSTRUCTOR = 41;
	public static final int SWING_LIB_MISSING = 42;
	public static final int MTM_FIELD_IS_STATIC = 43;
	public static final int MTM_COLLECTION_TYPE_IS_NOT_SUPPORTED = 44;
	public static final int MTM_COLLECTION_TYPE_IS_NOT_ASSIGNABLE_TO_ENTITY = 45;
	public static final int IMMUTABLE_BUILDER_NOT_FOUND = 46;
	public static final int IMMUTABLE_BUILDER_NEEDS_AN_EMPTY_CONSTRUCTOR = 47;
	public static final int IMMUTABLE_BUILDER_NEEDS_A_COPY_CONSTRUCTOR = 48;

	private final static String getErrorMessage(int errorCode) {
		switch (errorCode) {
//		case ENTITY_CLASS_HAS_NO_FIELDS:
//			return "Entity class has no fields!";
		case AUTO_DETECT_FIELD_TYPE_FAILED:
			return "%s >> AbstractRepository: Auto detecting field type failed! %s";
		case WRONG_CONSTRUCTOR:
			return "%s >> Wrong Constructor";
		case PRIMARY_KEY_INCONSISTENCY:
//			TODO update error message when multiple primary keys are implemented
			return "Primary key inconsistency in '%s' (%d defined keys, %d were expected) "
					+ "... multiple primary keys are not implemented yet, consider using unique constraint.";
		case MULTIPLE_PRIMARY_KEYS_CANNOT_BE_AUTOGENERATED:
			return "Multiple primary keys cannot be autogenerated: %s.%s";
		case COULD_NOT_READ_CONSTRAINT:
			return "Could not read constraint: %s.%s";
		case FOREIGN_KEY_CONSTRAINT_MALFORMED:
			return "Foreign key constraint malformed! fields: %d / referenced fields: %d";
		case ILLEGAL_ENTITY_NAME:
			return "Illegal entity name: %s";
		case ILLEGAL_FIELD_NAME:
			return "Illegal field name: %s.%s";
		case FIELD_NAME_COLLISION:
			return "Field name collision: %s.%s";
		case ENTITY_NAME_COLLISION:
			return "Entity name collision '%s'";
		case USER_ENTITY_SHOULD_EXTEND_USER_CLASS:
			return "%s >> AbstractUserRepository: User entity should extend AbstractUser or implement UserEntity!";
		case SOFT_DELETE_REQUIRES_META_ENTITY:
			return "Soft delete in '%s' requires an entity class that extends AbstractMetaEntity<T>";
		case ENTITY_GETTER_MISSING:
			return "Getter missing for %s.%s";
		case ENTITY_SETTER_MISSING:
			return "Setter missing for %s.%s";
		case EMPTY_CONSTRUCTOR_MISSING:
			return "Constructor with empty parameter list is missing or inaccessible for entity '%s'";
		case UNSUPPORTED_TYPE_USED:
			return "Unsupported type found! %s.%s :: '%s' is not supported";
		case DATABASE_ARGUMENT_MUST_NOT_BE_NULL:
			return "%s >> AbstractRepository: the database argument must not be null";
		case ENTITY_CLASS_ARGUMENT_MUST_NOT_BE_NULL:
			return "%s >> AbstractRepository: the entity class argument must not be null";
		case ENTITY_ID_CLASS_ARGUMENT_MUST_NOT_BE_NULL:
			return "%s >> AbstractRepository: the entity-id class argument must not be null";
		case OUT_DATED_SOFTWARE_IN_USE:
			return "You are using the out-dated application version #%d! The required application version is #%d!";
		case COULD_NOT_ESTABLISH_CONNECTION:
			return "%s >> Could not establish connection!";
		case WRONG_APPLICATION_ID:
			return "Wrong Application ID! expected id is %d but the file id is %d.";
		case CIRCULAR_FOREIGN_KEY_CONSTRUCT_DETECTED:
			return "Circular FOREIGN KEY reference in `%s` detected!";
		case COULD_NOT_INSTANTIATE_REPOSITORY:
			return "Could not instantiate repository: %s! is there a Constructor taking the DB implementation as argument?";
		case MULTIPLE_DB_CLASSES_NEED_REPOSITORIES_ANNOTATION:
			return "@Repositories annotation is missing: %s";
		case INITIALIZE_DATABASE_FAILED:
			return "Initialize database [%s] failed! the database  implementation class needs a constructor taking one argument of type DatabaseApplication";
		case INITIALIZE_REPOSITORY_FAILED:
			return "Initialize repository [%s] failed! the repository  implementation class needs a constructor taking one argument of type Database";
		case THERE_SHOULD_BE_ZERO_OR_ONE_SWING_UI_IMPLEMENTATIONS:
			return "Injector: there should be zero or one implementations of SwingUI but there were %d found: {%s}";
		case INITIALIZE_DB_APPLICATION_FAILED:
			return "Initialize (Database)Application [%s] failed! the class needs a constructor without parameters or a constructor receiving a String[] args!";
		case INITIALIZE_SWING_COMPONENT_FAILED:
			return "Initialize SwingComponent failed! The class [%s] needs a constructor without parameters or parameters of Component or SwingComponent values!";
		case INITIALIZE_SWING_UI_FAILED:
			return "Initialize SwingUI failed! The class [%s] could not be instantiated!";
		case INITIALIZE_COMPONENT_FAILED:
			return "Initializing component [%s] failed!";
		case FIELD_TYPE_IS_NOT_MAPPABLE:
			return "%s.%s: field type [%s] is not mappable!";
		case ENTITY_CONSTRUCTOR_INACCESSIBLE:
			return "%s: entity constructor inaccessible!";
		case MULTIPLE_SELF_REFERENCES:
			return "%s: multiple self references are not supported!";
		case REFERENCED_REPOSITORY_IS_MISSING:
			return "[%s -> %s] the referenced repository is missing!";
		case CUSTOM_IMPLEMENTATION_OF_META_ENTITY_DETECTED:
			return "Entity class [%s] is not permitted to implement the interface MetaEntity. Extend AbstractMetaEntity instead.";
		case THERE_SHOULD_BE_ZERO_OR_ONE_LOGGER_CONFIGURATIONS:
			return "There should be at most one logger configuration! found configurations: %s";
		case LOGGER_CONFIGURATION_NEEDS_EMPTY_CONSTRUCTOR:
			return "The logger configuration [%s] needs a constructor with an empty parameter list!";
		case SWING_LIB_MISSING:
			return "[%s] implements [SwingUI]. To start swing the artifact 'lumicore:swing:%s' needs to be on the classpath!";
		case MTM_FIELD_IS_STATIC:
			return "[%s.%s] field may not be static!";
		case MTM_COLLECTION_TYPE_IS_NOT_SUPPORTED:
			return "[%s.%s] collection type not supported!";
		case MTM_COLLECTION_TYPE_IS_NOT_ASSIGNABLE_TO_ENTITY:
			return "[%s.%s] collection type parameter must be assignable to Entity.class!";
		case IMMUTABLE_BUILDER_NOT_FOUND:
			return "[%s] Immutable Entity class needs a member class called 'Builder'!";
		case IMMUTABLE_BUILDER_NEEDS_AN_EMPTY_CONSTRUCTOR:
			return "[%s.Builder] The Builder class needs an empty constructor but doesn't have one!";
		case IMMUTABLE_BUILDER_NEEDS_A_COPY_CONSTRUCTOR:
			return "[%s.Builder] The Builder class needs a copy constructor but doesn't have one!";
		default:
			return "Unknown Error";
		}
	}

	/**
	 * the error code associated with this ConfigurationException
	 */
	private final int errorCode;

	private final static String getErrorMessage(int errorCode, Object... params) {
		return String.format(getErrorMessage(errorCode), params);
	}

	/**
	 * Use this constructor to fill in an error messages variables
	 * usingString.format(String, Object... args)
	 * 
	 * @param errorCode the error code
	 * @param params    the arguments to fill into the error message format
	 */
	public ConfigurationException(int errorCode, Object... params) {
		super(getErrorMessage(errorCode, params));
		this.errorCode = errorCode;
	}

	/**
	 * gets the error code associated with this ConfigurationException
	 * 
	 * @return this {@link ConfigurationException}s error code
	 */
	public int getErrorCode() {
		return errorCode;
	}

	/**
	 * compares the error code of this {@link ConfigurationException} to specified
	 * error code for equality.
	 * 
	 * @param errorCodeToCheck the error code to check for
	 * 
	 * @return true if the specified error code equals this
	 *         {@link ConfigurationException}s error code.
	 */
	public boolean errorCodeEquals(int errorCodeToCheck) {
		return this.errorCode == errorCodeToCheck;
	}
}
