/*
 * Copyright 2022 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
 *
 *      https://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.shell.test.jediterm.terminal.emulator.charset;

/**
 * Provides an enum with names for the supported character sets.
 *
 * @author jediterm authors
 */
public enum CharacterSet
{
	ASCII( 'B' )
		{
			@Override
			public int map( int index )
			{
				return -1;
			}
		},
	BRITISH( 'A' )
		{
			@Override
			public int map( int index )
			{
				if ( index == 3 )
				{
					// Pound sign...
					return '\u00a3';
				}
				return -1;
			}
		},
	DANISH( 'E', '6' )
		{
			@Override
			public int map( int index )
			{
				switch ( index )
				{
					case 32:
						return '\u00c4';
					case 59:
						return '\u00c6';
					case 60:
						return '\u00d8';
					case 61:
						return '\u00c5';
					case 62:
						return '\u00dc';
					case 64:
						return '\u00e4';
					case 91:
						return '\u00e6';
					case 92:
						return '\u00f8';
					case 93:
						return '\u00e5';
					case 94:
						return '\u00fc';
					default:
						return -1;
				}
			}
		},
	DEC_SPECIAL_GRAPHICS( '0', '2' )
		{
			@Override
			public int map( int index )
			{
				if ( index >= 64 && index < 96 )
				{
					return ( ( Character )CharacterSets.DEC_SPECIAL_CHARS[index - 64][0] ).charValue();
				}
				return -1;
			}
		},
	DEC_SUPPLEMENTAL( 'U', '<' )
		{
			@Override
			public int map( int index )
			{
				if ( index >= 0 && index < 64 )
				{
					// Set the 8th bit...
					return index + 160;
				}
				return -1;
			}
		},
	DUTCH( '4' )
		{
			@Override
			public int map( int index )
			{
				switch ( index )
				{
					case 3:
						return '\u00a3';
					case 32:
						return '\u00be';
					case 59:
						return '\u0133';
					case 60:
						return '\u00bd';
					case 61:
						return '|';
					case 91:
						return '\u00a8';
					case 92:
						return '\u0192';
					case 93:
						return '\u00bc';
					case 94:
						return '\u00b4';
					default:
						return -1;
				}
			}
		},
	FINNISH( 'C', '5' )
		{
			@Override
			public int map( int index )
			{
				switch ( index )
				{
					case 59:
						return '\u00c4';
					case 60:
						return '\u00d4';
					case 61:
						return '\u00c5';
					case 62:
						return '\u00dc';
					case 64:
						return '\u00e9';
					case 91:
						return '\u00e4';
					case 92:
						return '\u00f6';
					case 93:
						return '\u00e5';
					case 94:
						return '\u00fc';
					default:
						return -1;
				}
			}
		},
	FRENCH( 'R' )
		{
			@Override
			public int map( int index )
			{
				switch ( index )
				{
					case 3:
						return '\u00a3';
					case 32:
						return '\u00e0';
					case 59:
						return '\u00b0';
					case 60:
						return '\u00e7';
					case 61:
						return '\u00a6';
					case 91:
						return '\u00e9';
					case 92:
						return '\u00f9';
					case 93:
						return '\u00e8';
					case 94:
						return '\u00a8';
					default:
						return -1;
				}
			}
		},
	FRENCH_CANADIAN( 'Q' )
		{
			@Override
			public int map( int index )
			{
				switch ( index )
				{
					case 32:
						return '\u00e0';
					case 59:
						return '\u00e2';
					case 60:
						return '\u00e7';
					case 61:
						return '\u00ea';
					case 62:
						return '\u00ee';
					case 91:
						return '\u00e9';
					case 92:
						return '\u00f9';
					case 93:
						return '\u00e8';
					case 94:
						return '\u00fb';
					default:
						return -1;
				}
			}
		},
	GERMAN( 'K' )
		{
			@Override
			public int map( int index )
			{
				switch ( index )
				{
					case 32:
						return '\u00a7';
					case 59:
						return '\u00c4';
					case 60:
						return '\u00d6';
					case 61:
						return '\u00dc';
					case 91:
						return '\u00e4';
					case 92:
						return '\u00f6';
					case 93:
						return '\u00fc';
					case 94:
						return '\u00df';
					default:
						return -1;
				}
			}
		},
	ITALIAN( 'Y' )
		{
			@Override
			public int map( int index )
			{
				switch ( index )
				{
					case 3:
						return '\u00a3';
					case 32:
						return '\u00a7';
					case 59:
						return '\u00ba';
					case 60:
						return '\u00e7';
					case 61:
						return '\u00e9';
					case 91:
						return '\u00e0';
					case 92:
						return '\u00f2';
					case 93:
						return '\u00e8';
					case 94:
						return '\u00ec';
					default:
						return -1;
				}
			}
		},
	SPANISH( 'Z' )
		{
			@Override
			public int map( int index )
			{
				switch ( index )
				{
					case 3:
						return '\u00a3';
					case 32:
						return '\u00a7';
					case 59:
						return '\u00a1';
					case 60:
						return '\u00d1';
					case 61:
						return '\u00bf';
					case 91:
						return '\u00b0';
					case 92:
						return '\u00f1';
					case 93:
						return '\u00e7';
					default:
						return -1;
				}
			}
		},
	SWEDISH( 'H', '7' )
		{
			@Override
			public int map( int index )
			{
				switch ( index )
				{
					case 32:
						return '\u00c9';
					case 59:
						return '\u00c4';
					case 60:
						return '\u00d6';
					case 61:
						return '\u00c5';
					case 62:
						return '\u00dc';
					case 64:
						return '\u00e9';
					case 91:
						return '\u00e4';
					case 92:
						return '\u00f6';
					case 93:
						return '\u00e5';
					case 94:
						return '\u00fc';
					default:
						return -1;
				}
			}
		},
	SWISS( '=' )
		{
			@Override
			public int map( int index )
			{
				switch ( index )
				{
					case 3:
						return '\u00f9';
					case 32:
						return '\u00e0';
					case 59:
						return '\u00e9';
					case 60:
						return '\u00e7';
					case 61:
						return '\u00ea';
					case 62:
						return '\u00ee';
					case 63:
						return '\u00e8';
					case 64:
						return '\u00f4';
					case 91:
						return '\u00e4';
					case 92:
						return '\u00f6';
					case 93:
						return '\u00fc';
					case 94:
						return '\u00fb';
					default:
						return -1;
				}
			}
		};

	private final int[] myDesignations;

	/**
	 * Creates a new {@link CharacterSet} instance.
	 *
	 * @param designations the characters that designate this character set, cannot
	 *                     be {@code null}.
	 */
	CharacterSet(int... designations)
	{
		myDesignations = designations;
	}

	// METHODS

	/**
	 * Returns the {@link CharacterSet} for the given character.
	 *
	 * @param designation the character to translate to a {@link CharacterSet}.
	 * @return a character set name corresponding to the given character, defaulting
	 *         to ASCII if no mapping could be made.
	 */
	public static CharacterSet valueOf( char designation )
	{
		for ( CharacterSet csn : values() )
		{
			if ( csn.isDesignation( designation ) )
			{
				return csn;
			}
		}
		return ASCII;
	}

	/**
	 * Maps the character with the given index to a character in this character
	 * set.
	 *
	 * @param index the index of the character set, {@literal >= 0 && < 128}.
	 * @return a mapped character, or -1 if no mapping could be made and the
	 *         ASCII value should be used.
	 */
	public abstract int map( int index );

	/**
	 * Returns whether or not the given designation character belongs to this
	 * character set's set of designations.
	 *
	 * @param designation the designation to test for.
	 * @return {@code true} if the given designation character maps to this
	 *         character set, {@code true} otherwise.
	 */
	private boolean isDesignation( char designation )
	{
		for (int myDesignation : myDesignations) {
			if (myDesignation == designation) {
				return true;
			}
		}
		return false;
	}
}
