/*
 * Copyright 2012-2017 the original author or authors.
 *
 * 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 org.springframework.boot.ansi;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

import org.springframework.core.env.PropertyResolver;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;

/**
 * {@link PropertyResolver} for {@link AnsiStyle}, {@link AnsiColor} and
 * {@link AnsiBackground} elements. Supports properties of the form {@code AnsiStyle.BOLD}
 * , {@code AnsiColor.RED} or {@code AnsiBackground.GREEN}. Also supports a prefix of
 * {@code Ansi.} which is an aggregation of everything (with background colors prefixed
 * {@code BG_}).
 *
 * @author Phillip Webb
 * @since 1.3.0
 */
public class AnsiPropertySource extends PropertySource<AnsiElement> {

	private static final Iterable<MappedEnum<?>> MAPPED_ENUMS;

	static {
		List<MappedEnum<?>> enums = new ArrayList<>();
		enums.add(new MappedEnum<>("AnsiStyle.", AnsiStyle.class));
		enums.add(new MappedEnum<>("AnsiColor.", AnsiColor.class));
		enums.add(new MappedEnum<>("AnsiBackground.", AnsiBackground.class));
		enums.add(new MappedEnum<>("Ansi.", AnsiStyle.class));
		enums.add(new MappedEnum<>("Ansi.", AnsiColor.class));
		enums.add(new MappedEnum<>("Ansi.BG_", AnsiBackground.class));
		MAPPED_ENUMS = Collections.unmodifiableList(enums);
	}

	private final boolean encode;

	/**
	 * Create a new {@link AnsiPropertySource} instance.
	 * @param name the name of the property source
	 * @param encode if the output should be encoded
	 */
	public AnsiPropertySource(String name, boolean encode) {
		super(name);
		this.encode = encode;
	}

	@Override
	public Object getProperty(String name) {
		if (StringUtils.hasLength(name)) {
			for (MappedEnum<?> mappedEnum : MAPPED_ENUMS) {
				if (name.startsWith(mappedEnum.getPrefix())) {
					String enumName = name.substring(mappedEnum.getPrefix().length());
					for (Enum<?> ansiEnum : mappedEnum.getEnums()) {
						if (ansiEnum.name().equals(enumName)) {
							if (this.encode) {
								return AnsiOutput.encode((AnsiElement) ansiEnum);
							}
							return ansiEnum;
						}
					}
				}
			}
		}
		return null;
	}

	/**
	 * Mapping between an enum and the pseudo property source.
	 */
	private static class MappedEnum<E extends Enum<E>> {

		private final String prefix;

		private final Set<E> enums;

		MappedEnum(String prefix, Class<E> enumType) {
			this.prefix = prefix;
			this.enums = EnumSet.allOf(enumType);

		}

		public String getPrefix() {
			return this.prefix;
		}

		public Set<E> getEnums() {
			return this.enums;
		}

	}

}
