package jp.sourceforge.nicoro;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;

/**
 * 単純なテキスト表示View
 * 
 * SDKのApiDemosのLabelViewクラスを参考に改造
 *
 */
public class VariableLabelView extends View {
    private static final int MSG_ID_DRAW_SCROLL = 0;
    private static final long INTERVAL_DRAW_SCROLL = 33L;
    
	private Paint mTextPaint;
	private StringBuilder mTextBuilder;
	private String mText;
    private float mTextAscent;
    private float mTextWidth;
    private float mTextHeight;
    
    private boolean mScroll;
    private float mScrollSpeed;
    private int mScrollDelay;
    private long mScrollBeginTime = 0L;
    private float mScrollSpace;
    
    private Handler mDrawScroll = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case MSG_ID_DRAW_SCROLL:
		        invalidate();
				sendEmptyMessageDelayed(
						MSG_ID_DRAW_SCROLL, INTERVAL_DRAW_SCROLL);
				break;
			default:
				assert false : msg.what;
				break;
			}
		}
    };

	public VariableLabelView(Context context) {
		this(context, null);
	}
	public VariableLabelView(Context context, AttributeSet attrs) {
		super(context, attrs);
		
		mTextPaint = new Paint();
		
		TypedArray a = context.obtainStyledAttributes(attrs,
				R.styleable.VariableLabelView);
		
//		String source =
//			a.getString(R.styleable.VariableLabelView_text);
//		int textColor =
//			a.getColor(R.styleable.VariableLabelView_textColor, Color.BLACK);
//		int textSize =
//			a.getDimensionPixelOffset(R.styleable.VariableLabelView_textSize, 16);
//		boolean antialias =
//			a.getBoolean(R.styleable.VariableLabelView_antialias, false);
//		int defaultCapacity =
//			a.getInt(R.styleable.VariableLabelView_defaultCapacity, 256);
		
		String source = null;
		int textColor = Color.BLACK;
		int textSize = 16;
		boolean antialias = false;
		boolean underlineText = false;
		int defaultCapacity = 256;
		boolean scroll = false;
		float scrollSpeed = 30.0f;
		int scrollDelay = 1000;
		
		final int indexCount = a.getIndexCount();
		for (int i = 0; i < indexCount; i++) {
			int attr = a.getIndex(i);
			switch (attr) {
			case R.styleable.VariableLabelView_text:
				source =
					a.getString(R.styleable.VariableLabelView_text);
				break;
			case R.styleable.VariableLabelView_textColor:
				textColor =
					a.getColor(R.styleable.VariableLabelView_textColor, Color.BLACK);
				break;
			case R.styleable.VariableLabelView_textSize:
				textSize =
					a.getDimensionPixelOffset(R.styleable.VariableLabelView_textSize, 16);
				break;
			case R.styleable.VariableLabelView_antialias:
				antialias =
					a.getBoolean(R.styleable.VariableLabelView_antialias, false);
				break;
			case R.styleable.VariableLabelView_underlineText:
				underlineText =
					a.getBoolean(R.styleable.VariableLabelView_underlineText, false);
				break;
			case R.styleable.VariableLabelView_scroll:
				scroll =
					a.getBoolean(R.styleable.VariableLabelView_scroll, false);
				break;
			case R.styleable.VariableLabelView_scrollSpeed:
				scrollSpeed =
					a.getFloat(R.styleable.VariableLabelView_scrollSpeed, 30.0f);
				break;
			case R.styleable.VariableLabelView_scrollDelay:
				scrollDelay =
					a.getInt(R.styleable.VariableLabelView_scrollDelay, 1000);
				break;
			default:
				defaultCapacity =
					a.getInt(R.styleable.VariableLabelView_defaultCapacity, 256);
				break;
			}
		}
		
		mTextPaint.setColor(textColor);
		mTextPaint.setAntiAlias(antialias);
		mTextPaint.setUnderlineText(underlineText);
		setTextSizeImpl(textSize);
		if (source == null) {
			mTextBuilder = new StringBuilder(defaultCapacity);
			mText = null;
			mTextWidth = 0.0f;
		} else {
			mTextBuilder = new StringBuilder(source);
			setTextImpl(source);
		}
		
		mScroll = scroll;
		mScrollSpeed = -scrollSpeed;
		mScrollDelay = scrollDelay;
		
		a.recycle();
	}
	
	public StringBuilder getTextBuilder() {
		mText = null;
		return mTextBuilder;
	}
	
	public void notifyUpdateText() {
		setTextImpl(mTextBuilder.toString());
        requestLayout();
        invalidate();
	}
	
    public void setText(String text) {
    	mTextBuilder.delete(0, mTextBuilder.length());
        mTextBuilder.append(text);
		setTextImpl(text);
        requestLayout();
        invalidate();
    }
    
    public void setTextSize(int size) {
    	setTextSizeImpl(size);
        if (mText != null) {
        	mTextWidth = mTextPaint.measureText(mText);
        }
        requestLayout();
        invalidate();
    }
    
    public void setTextColor(int color) {
        mTextPaint.setColor(color);
        invalidate();
    }
    
    public void setScrollEnabled(boolean enabled) {
    	mScroll = enabled;
    }
    
    public void setScrollSpeed(float speed) {
    	mScrollSpeed = -speed;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec),
                measureHeight(heightMeasureSpec));
    }

    /**
     * Determines the width of this view
     * @param measureSpec A measureSpec packed into an int
     * @return The width of the view, honoring constraints from measureSpec
     */
    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
        } else {
            // Measure the text
        	if (mText == null) {
        		setTextImpl(mTextBuilder.toString());
        	}
            result = (int) mTextWidth + getPaddingLeft()
        		+ getPaddingRight();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }

        return result;
    }

    /**
     * Determines the height of this view
     * @param measureSpec A measureSpec packed into an int
     * @return The height of the view, honoring constraints from measureSpec
     */
    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
        } else {
            // Measure the text (beware: ascent is a negative number)
            result = (int) (mTextHeight + 0.5f) + getPaddingTop()
                    + getPaddingBottom();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    /**
     * Render the text
     * 
     * @see android.view.View#onDraw(android.graphics.Canvas)
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mText != null) {
        	if (mScroll) {
        		final long current = SystemClock.elapsedRealtime();
        		if (mScrollBeginTime == 0L) {
        			mScrollBeginTime = current;
    				mDrawScroll.sendEmptyMessageDelayed(
    						MSG_ID_DRAW_SCROLL, INTERVAL_DRAW_SCROLL);
        		}
        		
        		long timeForPos = current - mScrollBeginTime - mScrollDelay;
        		final float scrollWidth = mTextWidth + mScrollSpace;
        		float pos1st;
        		if (timeForPos >= 0) {
            		pos1st = timeForPos * mScrollSpeed / 1000.0f;
            		if (pos1st < -scrollWidth) {
            			pos1st = 0.0f;
            			mScrollBeginTime = current;
            		}
        		} else {
        			pos1st = 0.0f;
        		}
        		float pos2nd = pos1st + scrollWidth;
        		
	        	canvas.drawText(mText,
	        			getPaddingLeft() + pos1st,
	        			getPaddingTop() - mTextAscent,
	        			mTextPaint);
	        	canvas.drawText(mText,
	        			getPaddingLeft() + pos2nd,
	        			getPaddingTop() - mTextAscent,
	        			mTextPaint);
        	} else {
	        	canvas.drawText(mText,
	        			getPaddingLeft(),
	        			getPaddingTop() - mTextAscent,
	        			mTextPaint);
        	}
        }
    }
    
    private void setTextImpl(String text) {
    	mText = text;
    	measureTextWidth();
    }
    
    private void setTextSizeImpl(float textSize) {
    	final float lastTextSize = mTextPaint.getTextSize();
		mTextPaint.setTextSize(textSize);
		// 共通化のためのメソッドのため、ascent等は必ず取り直す
        mTextAscent = mTextPaint.ascent();
        mTextHeight = mTextPaint.getFontMetrics(null);
        mScrollSpace = textSize * 2.0f;
        if (lastTextSize != textSize && mText != null) {
        	measureTextWidth();
        }
    }
    
    private void measureTextWidth() {
    	assert mText != null;
    	final float lastTextWidth = mTextWidth;
		mTextWidth = mTextPaint.measureText(mText);
    	if (mScroll && lastTextWidth != mTextWidth) {
    		// scroll初期化
    		if (mScrollBeginTime != 0) {
    			mScrollBeginTime = SystemClock.elapsedRealtime();
    		}
    	}
    }
}
