package com.simpleteam.coder;

/**
 * <p>Title: SimpleTeam</p>
 * <p>Description: Quoted Printable Coder</p>
 * <p>Copyright: All rights reserved by www.simpleteam.com. Copyright (c) 2003 ~ 2006</p>
 * <p>Company: www.simpleteam.com</p>
 * @author forest_luo@simpleteam.com
 * @version 1.1.2
 */

public class QuotedPrintableCoder
{
	//Max Chars
	private final static int maxChars = 76;
	//Alphabet Table
	private final static String alphabetTable = "0123456789ABCDEF";

	//Type of Coder
	public final static String TYPE                         = "Q";

	/**
	 * Decode char.
	 *
	 * @param c Char for decoding.
	 * @return
	 *     <p>Decoded value.</p>
	 */
	private static int decode(char c)
	{
		//Check table.
		for(int i = 0;i < 16;i ++)
		{
			//Check alphabet.
			if(alphabetTable.charAt(i) == c) return i;
		}
		//Return -1.
		return -1;
	}

	/**
	 * Encode char.
	 *
	 * @param c Char for encoding.
	 * @return
	 *     <p>Encoded value.</p>
	 */
	private static String encode(char c)
	{
		//Encoded string.
		String encode = "=";
		//Encode high part.
		encode += alphabetTable.charAt((c >> 4) & 0x0f);
		//Encode low part.
		encode += alphabetTable.charAt(c & 0x0f);
		//Return encoded string.
		return encode;
	}


	/**
	 * Whether value is a safe character or not.
	 *
	 * @param value Integer value.
	 * @return
	 *     <p>Return true, if it is a safe character.</p>
	 */
	public static boolean isSafable(int value)
	{
		//Check value.
		if(value >= 33 && value <= 126)
		{
			//Return result.
			return value != 61;
		}
		//Return false.
		return false;
	}

	/**
	 * Whether value is a safe character or not.
	 *
	 * @param value String value.
	 * @return
	 *     <p>Return true, if it is a safe string.</p>
	 */
	public static boolean isSafable(String value)
	{
		//Check string.
		for(int i = 0;i < value.length();i ++)
		{
			//Check char.
			if(!isSafable(value.charAt(i))) return false;
		}
		//Return true.
		return true;
	}

	/**
	 * Encode string.
	 *
	 * @param source Source string for encoding.
	 * @return
	 *     <p>Encoded string.</p>
	 */
	public static String encode(String source)
	{
		//Encode string.
		String encoded = "";

		//Char count.
		int charCount = 0;
		//Encode string.
		for(int i = 0;i < source.length();i ++)
		{
			//Get char.
			char c = source.charAt(i);
			//Check char.
			if(isSafable(c))
			{
				//Check char count.
				if((charCount + 1) >= maxChars)
				{
					//Add end of line.
					encoded += "=\r\n";
					//Reset char count.
					charCount = 0;
				}
				//Add char.
				encoded += c;
				//Add char count.
				charCount ++;
			}
			else
			{
				//Check char.
				switch(c)
				{
				case '\r':
				case '\n':
					//Reset char count.
					charCount = 0;
					//Add char.
					encoded += c;
					break;
				case '\t':
				case ' ':
					//Check char count.
					if((charCount + 4) >= maxChars)
					{
						//Add end of line.
						encoded += "=\r\n";
						//Reset char count.
						charCount = 0;
					}
					//Add char.
					encoded += c;
					//Add char count.
					charCount ++;
					break;
				default:
					//Check char count.
					if((charCount + 3) >= maxChars)
					{
						//Add end of line.
						encoded += "=\r\n";
						//Reset char count.
						charCount = 0;
					}
					//Encode char.
					encoded += encode(c);
					//Add char count.
					charCount += 3;
				}
			}
		}
		//Return encoded string.
		return encoded;
	}

	/**
	 * Decode string.
	 *
	 * @param source Source string for decoding.
	 * @return
	 *     <p>Decoded string.</p>
	 */
	public static String decode(String source)
	{
		//Decoded string.
		String decoded = "";
		//Decode string.
		for(int i = 0;i < source.length();i ++)
		{
			//Get char.
			char c = source.charAt(i);
			//Check char.
			if(c == '=')
			{
				//Get first char.
				char firstChar = (i + 1 < source.length()) ? source.charAt(i + 1) : 0;
				//Get second char.
				char secondChar = (i + 2 < source.length()) ? source.charAt(i + 2) : 0;
				//Check result.
				if(firstChar == '\r' || secondChar == '\n')
				{
					//Add i.
					i += 2;
					continue;
				}

				//Get high byte.
				int highByte = decode(firstChar);
				//Get low byte.
				int lowByte = decode(secondChar);
				//Check result.
				if(highByte >= 0 && lowByte >= 0)
				{
					//Get char.
					c = (char)((highByte << 4) | lowByte);
					//Add char.
					decoded += c;

					//Add i.
					i += 2;
				}
				else
				{
					//Add char.
					decoded += c;
				}
			}
			else
			{
				//Add char.
				decoded += c;
			}
		}
		//Return original string.
		return decoded;
	}
}
