package com.one;

import com.vmx.AuxClass;
import com.vmx.CharacterIterator;
import com.vmx.StringIterator;
import java.io.EOFException;
import java.io.IOException;
import java.util.Vector;
import javax.microedition.lcdui.Graphics;
import modules.text.TextOptions;

public abstract class AbstractFont
{
	public static final int FONT_COUNT = 3 * 3 * (1 << 3);

	public static final int FACE_MONOSPACE = 32;
	public static final int FACE_PROPORTIONAL = 64;
	public static final int FACE_SYSTEM = 0;

	public static final int SIZE_LARGE = 16;
	public static final int SIZE_MEDIUM = 0;
	public static final int SIZE_SMALL = 8;

	public static final int STYLE_BOLD = 1;
	public static final int STYLE_ITALIC = 2;
	public static final int STYLE_PLAIN = 0;
	public static final int STYLE_UNDERLINED = 4;

	private static AbstractFont[] fonts = new AbstractFont[FONT_COUNT];

	public static AbstractFont getDefaultFont()
	{
		return getFont(FACE_SYSTEM, STYLE_PLAIN, SIZE_MEDIUM);
	}

	public static AbstractFont getFont(int index)
	{
		return fonts[index];
	}

	public static AbstractFont getFont(int face, int style, int size)
	{
		int index = getFontIndex(face, style, size);
		
		if(fonts[index] == null)
		{
			fonts[index] = new SystemFont(face, style, size);
		}

		return fonts[index];
	}

	public static void setFont(AbstractFont font, int index)
	{
		fonts[index] = font;

		if(font != null)
		{
			font.face = getFontFace(index);
			font.size = getFontSize(index);
			font.style = getFontStyle(index);
		}
	}

	public static void setFont(AbstractFont font, int face, int style, int size)
	{
		fonts[getFontIndex(face, style, size)] = font;

		if(font != null)
		{
			font.face = face;
			font.size = size;
			font.style = style;
		}
	}

	protected int face;
	protected int size;
	protected int style;

	public abstract int charWidth(char ch);
	public abstract int getBaselinePosition();
	public abstract int getHeight();
	public abstract void drawChar(Graphics g, char character, int x, int y);

	public int getIndex()
	{
		return getFontIndex(face, style, size);
	}

	public int getFace()
	{
		return face;
	}

	public int getSize()
	{
		return size;
	}

	public int getStyle()
	{
		return style;
	}

	public boolean isBold()
	{
		return (style & STYLE_BOLD) != 0;
	}

	public boolean isItalic()
	{
		return (style & STYLE_ITALIC) != 0;
	}

	public boolean isPlain()
	{
		return style == STYLE_PLAIN;
	}

	public boolean isUnderlined()
	{
		return (style & STYLE_UNDERLINED) != 0;
	}

	public int charsWidth(char[] ch, int offset, int length)
	{
		int width = 0;

		for(length += offset; offset < length; offset++)
		{
			width += charWidth(ch[offset]);
		}

		return width;
	}

	public int stringWidth(String str)
	{
		return charsWidth(str.toCharArray(), 0, str.length());
	}

	public int substringWidth(String str, int offset, int length)
	{
		return stringWidth(str.substring(offset, offset + length));
	}

	public void drawChar(Graphics g, char character, int x, int y, int anchor)
	{
		if((anchor & Graphics.RIGHT) != 0)
		{
			x -= charWidth(character);
		}
		else if((anchor & Graphics.HCENTER) != 0)
		{
			x -= charWidth(character) / 2;
		}

		if((anchor & Graphics.BASELINE) != 0)
		{
			y -= getBaselinePosition();
		}
		else if((anchor & Graphics.BOTTOM) != 0)
		{
			y -= getHeight();
		}
		else if((anchor & Graphics.VCENTER) != 0)
		{
			y -= getHeight() / 2;
		}

		drawChar(g, character, x, y);
	}

	public void drawChars(Graphics g, char[] data, int offset, int length, int x, int y, int anchor)
	{
		if((anchor & Graphics.RIGHT) != 0)
		{
			x -= charsWidth(data, offset, length);
		}
		else if((anchor & Graphics.HCENTER) != 0)
		{
			x -= charsWidth(data, offset, length) / 2;
		}

		if((anchor & Graphics.BASELINE) != 0)
		{
			y -= getBaselinePosition();
		}
		else if((anchor & Graphics.BOTTOM) != 0)
		{
			y -= getHeight();
		}
		else if((anchor & Graphics.VCENTER) != 0)
		{
			y -= getHeight() / 2;
		}

		for(length += offset; offset < length; offset++)
		{
			drawChar(g, data[offset], x, y);
			x += charWidth(data[offset]);
		}
	}

	public void drawString(Graphics g, String str, int x, int y, int anchor)
	{
		drawChars(g, str.toCharArray(), 0, str.length(), x, y, anchor);
	}

	public void drawSubstring(Graphics g, String str, int offset, int len, int x, int y, int anchor)
	{
		drawString(g, str.substring(offset, offset + len), x, y, anchor);
	}

	public boolean equals(Object obj)
	{
		if(obj == null)
		{
			return false;
		}

		if(getClass() != obj.getClass())
		{
			return false;
		}

		final AbstractFont other = (AbstractFont)obj;

		if(this.face != other.face)
		{
			return false;
		}

		if(this.size != other.size)
		{
			return false;
		}

		if(this.style != other.style)
		{
			return false;
		}

		return true;
	}

	public int hashCode()
	{
		return getFontIndex(face, style, size);
	}

	public static int getFontIndex(int face, int style, int size)
	{
		switch(face)
		{
			case FACE_MONOSPACE:
				face = 0;
				break;

			case FACE_PROPORTIONAL:
				face = 1;
				break;

			case FACE_SYSTEM:
				face = 2;
				break;
		}

		switch(size)
		{
			case SIZE_SMALL:
				size = 0;
				break;

			case SIZE_MEDIUM:
				size = 1;
				break;

			case SIZE_LARGE:
				size = 2;
				break;
		}

		return ((face * 3 + size) << 3) + style;
	}

	public static int getFontFace(int index)
	{
		index = (index >>> 3) / 3;

		switch(index)
		{
			case 0:
				return FACE_MONOSPACE;

			case 1:
				return FACE_PROPORTIONAL;

			case 2:
			default:
				return FACE_SYSTEM;
		}
	}

	public static int getFontSize(int index)
	{
		index = (index >>> 3) % 3;

		switch(index)
		{
			case 0:
				return SIZE_SMALL;

			case 1:
			default:
				return SIZE_MEDIUM;

			case 2:
				return SIZE_LARGE;
		}
	}

	public static int getFontStyle(int index)
	{
		return index & 7;
	}

	/**
	 * Получить подстроку исходной строки,
	 * которая по ширине не превышала бы заданного значения.
	 *
	 * @param text исходная строка
	 * @param ellipsis замещающая строка, например ".." или "..."
	 * @param width максимальная ширина конечной строки
	 * @param direction false - "very long str...", true - "...ry long string"
	 * @return см. последний параметр
	 */
	public String getLastPartOfString(String text, String ellipsis, int width, boolean direction)
	{
		text = text.trim();

		if(stringWidth(text) <= width)
		{
			return text;
		}
		else
		{
			if(direction)
			{
				int index = 0;
				String res;

				do
				{
					res = ellipsis + text.substring(++index).trim();
				}
				while(stringWidth(res) > width);

				return res;
			}
			else
			{
				int index = text.length();
				String res;

				do
				{
					res = text.substring(0, --index).trim() + ellipsis;
				}
				while(stringWidth(res) > width);

				return res;
			}
		}
	}

	/**
	 * Разбить строку, чтобы она вписывалась по ширине
	 * в экран шириной maxStrWidth
	 */
	public String[] splitString(String text, int maxStrWidth)
	{
		CharacterIterator ci = new StringIterator(text);

		Vector vector = new Vector();
		String line;

		try
		{
			while((line = readString(ci, maxStrWidth, true)) != null)
			{
				vector.addElement(line);
			}
		}
		catch(IOException x)
		{
			x.printStackTrace();
		}

		return TextProcessor.vectorToStringArray(vector);
	}

	/**
	 * Прочитать строку из потока
	 */
	public String readString(CharacterIterator ci, int maxStrWidth, boolean wordWrap) throws IOException
	{
		StringBuffer s = new StringBuffer();
		String res;

		int afterspace = -1;
		int cut = 0;
		int strwidth = 0;
		char c;

		int strlen = 0;
		int index;

		try
		{
			if(ci == null || ci.available() <= 0)
			{
				return null;
			}

			if(ci.getPosition() < 0)
			{
				ci.setPosition(0);
			}

			try
			{
				do
				{
					/* считываем символ */
					c = ci.readChar();

					/* если это не символ конца строки и не конец файла,
					   то добавляем его к строке и обновляем ее длину.
					   В противном случае сообщаем, что перенос слов не требуется и выходим из цикла */
					if(c == '\n')
					{
						if(TextOptions.showLF)
						{
							s.append('\n');
							strwidth += charWidth('\n');
							strlen++;
						}

						wordWrap = false;
						break;
					}
					else if(c == '\r')
					{
						if(TextOptions.showCR)
						{
							s.append('\r');
							strwidth += charWidth('\r');
							strlen++;
						}
					}
					else if(c == '\t')
					{
						s.append('\t');

						if(true)
						{
							index = TextOptions.tabSize - (strlen % TextOptions.tabSize) - 1;
							strwidth += stringWidth(TextOptions.tabSpaces[index]);
							strlen += TextOptions.tabSpaces[index].length();

							c = ' ';
						}
						else
						{
							strwidth += charWidth(c);
							strlen++;
						}
					}
					else
					{
						s.append(c);
						strwidth += charWidth(c);
						strlen++;
					}

					if(AuxClass.SEPARATOR_CHARS.indexOf(c) >= 0)
					{
						afterspace = 0;
					}
					else if(afterspace >= 0)
					{
						afterspace++;
					}
				}
				while(strwidth < maxStrWidth);
			}
			catch(EOFException eof)
			{
				wordWrap = false;
			}

			res = s.toString();

			if(wordWrap && afterspace >= 0)
			{
				cut = afterspace;
			}
			else if(strwidth > maxStrWidth)
			{
				cut = 1;
			}

			if(cut > 0)
			{
                res = res.substring(0, res.length() - cut);
				ci.rewind(cut);
			}
		}
		catch(IOException x)
		{
			throw x;

			//ErrScreen.showErrMsg(21, x);
			//return null;
		}

		if(true)
		{
			res = TextProcessor.tabsToSpaces(res, TextOptions.tabSpaces, TextOptions.accurateTAB);
		}

		return res;
	}

	/**
	 * Прочитать строку из потока НАЗАД
	 */
	public String readStringBack(CharacterIterator ci, int maxStrWidth, boolean wordWrap) throws IOException
	{
        StringBuffer s = new StringBuffer();
		String res;

		int afterspace = -1;
		int cut = 0;
		int strwidth = 0;
		char c = (char)-1;

		int strlen = 0;
		int index;

		try
		{
			if(ci == null || ci.getPosition() <= 0)
			{
				return null;
			}

			try
			{
				do
				{
					if(ci.getPosition() <= 0)
					{
						wordWrap = false;
						break;
					}

					/* считываем символ */
					c = ci.readCharBack();

					/* если это не символ конца строки и не конец файла,
					   то добавляем его к строке и обновляем ее длину.
					   В противном случае сообщаем, что перенос слов не требуется и выходим из цикла */
					if(c == '\n')
					{
						if(s.length() > 0 || afterspace >= 0)
						{
							/*
							 * В строке уже есть символы,
							 * то есть это не конец строки.
							 * Здесь символ LF не показываем в любом случае,
							 * так как это конец предыдущей строки.
							 *
							 * Тем не менее в буфер нужно что-то воткнуть,
							 * чтобы было потом что отбросить, когда будем
							 * читать символ назад.
							 */

							s.insert(0, '\n');
							strwidth += charWidth('\n');
							strlen++;

							wordWrap = false;
							break;
						}
						else
						{
							/*
							 * Строка пока пустая, так что это может быть
							 * конец этой строки.
							 * Пихаем символ LF в строку на усмотрение пользователя.
							 */

							if(TextOptions.showLF)
							{
								s.insert(0, '\n');
								strwidth += charWidth('\n');
								strlen++;
							}
	//						else
	//						{
	//							s.insert(0, ' ');
	//							strwidth += fntText.charWidth(' ');
	//						}
						}
					}
					else if(c == '\r')
					{
						if(TextOptions.showCR)
						{
							s.insert(0, '\r');
							strwidth += charWidth('\r');
							strlen++;
						}
					}
					else if(c == '\t')
					{
						s.insert(0, '\t');

						if(true)
						{
							index = TextOptions.tabSize - (strlen % TextOptions.tabSize) - 1;
							strwidth += stringWidth(TextOptions.tabSpaces[index]);
							strlen += TextOptions.tabSpaces[index].length();

							c = ' ';
						}
						else
						{
							strwidth += charWidth('\t');
							strlen++;
						}
					}
					else
					{
						s.insert(0, c);
						strwidth += charWidth(c);
						strlen++;
					}

					if(AuxClass.SEPARATOR_CHARS.indexOf(c) >= 0)
					{
						afterspace = 1;
					}
					else if(c == '\n')
					{
						afterspace = 0;
					}
					else if(afterspace >= 0)
					{
						afterspace++;
					}
				}
				while(strwidth < maxStrWidth);
			}
			catch(EOFException eof)
			{
				wordWrap = false;
			}

			res = s.toString();

			if(wordWrap && afterspace >= 0 && res.length() > afterspace)
			{
				cut = afterspace;
			}
			else if((strwidth > maxStrWidth) || (c == '\n' && res.length() > 0))
			{
				cut = 1;
			}

			if(cut > 0)
			{
                res = res.substring(cut);
				ci.skip(cut);
			}
		}
		catch(IOException x)
		{
			throw x;

			//ErrScreen.showErrMsg(22, x);
			//return null;
		}

		if(true)
		{
			res = TextProcessor.tabsToSpaces(res, TextOptions.tabSpaces, TextOptions.accurateTAB);
		}

		return res;
	}
}