/*
 * Paraselene
 * Copyright (c) 2009, 2010  Akira Terasaki
 * このファイルは同梱されているLicense.txtに定めた条件に同意できる場合にのみ
 * 利用可能です。
 */
package paraselene;

import java.net.*;
import java.io.*;
import java.util.*;
import java.text.*;
import paraselene.tag.*;
import paraselene.css.*;

/**
 * テキスト。<br>
 * 単純な文字列情報を持ちますが、HTMLのタグ種別に合わせ
 * 出力フォーマットを変換します。
 */
public class Text extends PlainText implements AttributeValuable, CSSValuable {
	private static final long serialVersionUID = 2L;

	protected Text() {}

	/**
	 * コンストラクタ。StringMode.PLAIN扱い。
	 * @param txt テキスト。
	 */
	public Text( String txt ) {
		this( txt, StringMode.PLAIN );
	}

	/**
	 * コンストラクタ。数値を文字列にします。<BR>
	 * 例えばnew Text( 1234678, &quot;\\#,###&quot; ) は
	 * &quot;\12,345,678&quot;という文字列にして格納します。
	 * @param num 数値。
	 * @param format フォーマット。
	 */
	public Text( long num, String format ) {
		this( new Long( num ), format );
	}

	/**
	 * コンストラクタ。数値を文字列にします。<BR>
	 * 例えばnew Text( 1234678.9, &quot;#,##0.00&quot; ) は
	 * &quot;12,345,678.90&quot;という文字列にして格納します。
	 * @param num 数値。
	 * @param format フォーマット。
	 */
	public Text( double num, String format ) {
		this( new Double( num ), format );
	}

	/**
	 * コンストラクタ。数値を文字列にします。<BR>
	 * 例えばnew Text(new BigInteger( &quot;12345678&quot; ), &quot;\\#,###&quot;) は
	 * &quot;\12,345,678&quot;という文字列にして格納します。
	 * @param num 数値。
	 * @param format フォーマット。
	 */
	public Text( Number num, String format ) {
		this( new DecimalFormat( format ).format( num ) );
	}

	/**
	 * コンストラクタ。日時を文字列にします。<BR>
	 * 例えばnew Text(new Date(), &quot;yyyy/MM/dd HH:mm:ss&quot;) は
	 * &quot;2009/01/01 15:00:00&quot;のような文字列を格納します。
	 * @param date 日時。
	 * @param format フォーマット。
	 */
	public Text( Date date, String format ) {
		this( new SimpleDateFormat( format ).format( date ) );
	}

	/**
	 * コンストラクタ。日時を文字列にします。<BR>
	 * 例えばnew Text(new Date(), &quot;yyyy/MM/dd HH:mm:ss&quot;) は
	 * &quot;2009/01/01 15:00:00&quot;のような文字列を格納します。
	 * @param date 日時。
	 * @param format フォーマット。
	 * @param locale フォーマットに使用するロケール。
	 */
	public Text( Date date, String format, Locale locale ) {
		this( new SimpleDateFormat( format, locale ).format( date ) );
	}

	/**
	 * コンストラクタ。
	 * @param txt テキスト。
	 * @param mode 入力文字列種別。
	 */
	public Text( String txt, StringMode mode ) {
		setText( txt, mode );
	}

	/**
	 * テキストを設定する。
	 * @param txt テキスト。
	 * @param mode 入力文字列種別。
	 */
	public void setText( String txt, StringMode mode ) {
		if ( txt == null || mode == StringMode.PLAIN ) {
			super.setText( txt );
			return;
		}

		char[]	line = txt.toCharArray();
		StringBuilder	buf = new StringBuilder();
		StringBuilder	esc = null;
		int	spc_cnt = 0;
		for ( int i = 0; i < line.length; i++ ) {
			if ( line[i] == '\n' || line[i] == '\r' ) {
				if ( i > 0 ) {
					if ( line[i -1] == '\n' || line[i - 1] == '\r' ) {
						if ( line[i] != line[i - 1] )   continue;
					}
				}
				buf = buf.append( "\n" );
				continue;
			}
			if ( line[i] == ' ' || line[i] == '\t' ) {
				if ( mode == StringMode.TEXTAREA ) {
					buf = buf.append( line[i] );
					continue;
				}
				spc_cnt++;
				continue;
			}
			if ( spc_cnt > 0 ) {
				buf = buf.append( ' ' );
				spc_cnt = 0;
			}
			if ( esc != null ) {
				esc = esc.append( line[i] );
				if ( line[i] != ';' ) continue;
				String	amp = esc.toString();
				if ( "&lt;".equalsIgnoreCase( amp ) ) {
					buf = buf.append( '<' );
				}
				else if ( "&gt;".equalsIgnoreCase( amp ) ) {
					buf = buf.append( '>' );
				}
				else if ( "&amp;".equalsIgnoreCase( amp ) ) {
					buf = buf.append( '&' );
				}
				else if ( "&quot;".equalsIgnoreCase( amp ) ) {
					buf = buf.append( '"' );
				}
				else if ( "&nbsp;".equalsIgnoreCase( amp ) ) {
					buf = buf.append( ' ' );
				}
				else {
					buf = buf.append( esc );
				}
				esc = null;
				continue;
			}
			if ( line[i] == '&' ) {
				esc = new StringBuilder( "&" );
				continue;
			}
			spc_cnt = 0;
			buf = buf.append( line[i] );
		}
		if ( esc != null ) {
			buf = buf.append( esc );
		}
		super.setText( txt );
	}

	/**
	 * テキスト化。
	 * @param mode 出力モード。
	 */
	public String toString( StringMode mode ) {
		String	str = super.toString( mode );
		if ( str == null )	return null;
		if ( mode == StringMode.PLAIN ) {
			return str;
		}
		StringBuilder	buf = new StringBuilder();
		char[]	line = str.toCharArray();
		int	spc_cnt = 0;
		boolean	clm_f = getAssignedTag() instanceof paraselene.tag.table.Column;
		for ( int i = 0; i < line.length; i++ ) {
			if ( line[i] == ' ' ) {
				String	add = " ";
				switch( mode ) {
				case BODY:
					if ( clm_f ) {
						add = ((spc_cnt % 2) == 0)?	"&nbsp;": " ";
					}
					else {
						add = ((spc_cnt % 2) == 1)?	"&nbsp;": " ";
					}
					break;
				}
				spc_cnt++;
				buf = buf.append( add );
				continue;
			}
			spc_cnt = 0;
			WebChar	wc = WebChar.getWebChar( line[i] );
			if ( wc != null ) {
				buf = buf.append( wc.getReference() );
				continue;
			}
			if ( mode != StringMode.ATTRIBUTE ) {
				if ( mode != StringMode.TEXTAREA ) {
					if ( line[i] == '\n' || line[i] == '\r' ) {
						if ( i > 0 ) {
							if ( line[i -1] == '\n' || line[i - 1] == '\r' ) {
								if ( line[i] != line[i - 1] )	continue;
							}
						}
						String	br = "<br>";
						if ( getAssignedTag().getAssignedPage().isXML() ) {
							br = "<br />";
						}
						buf = buf.append( br );
						continue;
					}
				}
				else {
					if ( line[i] == '\n' || line[i] == '\r' ) {
						if ( i > 0 ) {
							if ( line[i -1] == '\n' || line[i - 1] == '\r' ) {
								if ( line[i] != line[i - 1] )	continue;
							}
						}
						buf = buf.append( "\r\n" );
						continue;
					}
				}
			}
			buf = buf.append( line[i] );
		}
		return buf.toString();
	}

	/**
	 * 複製の作成。
	 * @return 複製。
	 */
	public HTMLPart getReplica() {
		return new Text( super.toString( StringMode.PLAIN ) );
	}

	/**
	 * 文字列の追加。thisを変更し、thisをリターンします。
	 * @param add 追加文字列。
	 * @return thisを返す。
	 */
	public Text append( String add ) {
		if ( add == null )	return this;
		StringBuilder	buf = new StringBuilder( super.toString( StringMode.PLAIN ) );
		buf = buf.append( add );
		super.setText( buf.toString() );
		return this;
	}

	/**
	 * 文字列の追加。thisを変更し、thisをリターンします。
	 * @param add 追加文字列。
	 * @return thisを返す。
	 */
	public Text append( PlainText add ) {
		if ( add == null )	return this;
		return append( add.toString( StringMode.PLAIN ) );
	}

	/**
	 * 文字列の追加。thisを変更し、thisをリターンします。
	 * @param add 追加文字列。
	 * @return thisを返す。
	 */
	public Text append( WebChar add ) {
		if ( add == null )	return this;
		return append( add.toString() );
	}
}


