package flare.display
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.geom.Rectangle;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;

    /**
     * A Sprite representing a text label.
     * TextSprites support multiple forms of text representation: bitmapped
     * text, embedded font text, and standard (device font) text. This allows
     * flexibility in how text labels are handled. For example, by default,
     * text fields using device fonts do not support alpha blending or
     * rotation. By using a TextSprite in BITMAP mode, the text is rendered
     * out to a bitmap which can then be alpha blended.
     */
    public class TextSprite extends DirtySprite
    {
        // vertical anchors
        /**
         * Constant for vertically aligning the top of the text field
         * to a TextSprite's y-coordinate.
         */
        public static const TOP:int = 0;
        /**
         * Constant for vertically aligning the middle of the text field
         * to a TextSprite's y-coordinate.
         */
        public static const MIDDLE:int = 1;
        /**
         * Constant for vertically aligning the bottom of the text field
         * to a TextSprite's y-coordinate.
         */
        public static const BOTTOM:int = 2;

        // horizontal anchors
        /**
         * Constant for horizontally aligning the left of the text field
         * to a TextSprite's y-coordinate.
         */
        public static const LEFT:int = 0;
        /**
         * Constant for horizontally aligning the center of the text field
         * to a TextSprite's y-coordinate.
         */
        public static const CENTER:int = 1;
        /**
         * Constant for horizontally aligning the right of the text field
         * to a TextSprite's y-coordinate.
         */
        public static const RIGHT:int = 2;
        
        // text handling modes
        /**
         * Constant indicating that text should be rendered using a TextField
         * instance using device fonts.
         */
        public static const DEVICE:uint = 0;
        /**
         * Constant indicating that text should be rendered using a TextField
         * instance using embedded fonts. For this mode to work, the fonts
         * used must be embedded in your application SWF file.
         */
        public static const EMBED:uint = 1;
        /**
         * Constant indicating that text should be rendered into a Bitmap
         * instance.
         */
        public static const BITMAP:uint = 2;
        
        protected var _mode:int = -1;
        protected var _bmap:Bitmap;
        protected var _tf:TextField;
        protected var _fmt:TextFormat;
        
        protected var _hAnchor:int = LEFT;
        protected var _vAnchor:int = TOP;
        
        /** The TextField instance backing this TextSprite. */
        public function get textField():TextField { return _tf; }
        
        /** The bitmap of the text, if in BITMAP mode. */
        public function get bitmap():Bitmap { return _bmap; }
        
        /**
         * The text rendering mode for this TextSprite, one of BITMAP,
         * DEVICE, or EMBED.
         */
        public function get textMode():int { return _mode; }
        public function set textMode(mode:int):void {
            setMode(mode);
        }
        
        /** The default text format for this text sprite. */
        public function get textFormat():TextFormat { return _fmt; }
        public function set textFormat(fmt:TextFormat):void {
            _tf.defaultTextFormat = (_fmt = fmt);
            _tf.setTextFormat(_fmt);
            if (_mode==BITMAP) dirty();
        }
        
        /**
         * The text shown by this TextSprite.
         */
        public function get text():String { return _tf.text; }
        public function set text(txt:String):void {
            if (_tf.text != txt) {
                _tf.text = (txt==null ? "" : txt);
                if (_fmt!=null) _tf.setTextFormat(_fmt);
                dirty();
            }
        }
        
        /**
         * The html text shown by this TextSprite.
         */
        public function get htmlText():String { return _tf.htmlText; }
        public function set htmlText(txt:String):void {
            if (_tf.htmlText != txt) {
                _tf.htmlText = (txt==null ? "" : txt);
                dirty();
            }
        }
        
        /**
         * The font to the text.
         */
        public function get font():String { return String(_fmt.font); }
        public function set font(f:String):void {
            _fmt.font = f;
            _tf.setTextFormat(_fmt);
            if (_mode==BITMAP) dirty();
        }
        
        /**
         * The color of the text.
         */
        public function get color():uint { return uint(_fmt.color); }
        public function set color(c:uint):void {
            _fmt.color = c;
            _tf.setTextFormat(_fmt);
            if (_mode==BITMAP) dirty();
        }
        
        /**
         * The size of the text.
         */
        public function get size():Number { return Number(_fmt.size); }
        public function set size(s:Number):void {
            _fmt.size = s;
            _tf.setTextFormat(_fmt);
            if (_mode==BITMAP) dirty();
        }
        
        /**
         * The boldness of the text.
         */
        public function get bold():Boolean { return Boolean(_fmt.bold); }
        public function set bold(b:Boolean):void {
            _fmt.bold = b;
            _tf.setTextFormat(_fmt);
            if (_mode==BITMAP) dirty();
        }
        
        /**
         * The italics of the text.
         */
        public function get italic():Boolean { return Boolean(_fmt.italic); }
        public function set italic(b:Boolean):void {
            _fmt.italic = b;
            _tf.setTextFormat(_fmt);
            if (_mode==BITMAP) dirty();
        }
        
        /**
         * The underline of the text.
         */
        public function get underline():Boolean { return Boolean(_fmt.underline); }
        public function set underline(b:Boolean):void {
            _fmt.underline = b;
            _tf.setTextFormat(_fmt);
            if (_mode==BITMAP) dirty();
        }
        
        /**
         * The kerning of the text.
         */
        public function get kerning():Boolean { return Boolean(_fmt.kerning); }
        public function set kerning(b:Boolean):void {
            _fmt.kerning = b;
            _tf.setTextFormat(_fmt);
            if (_mode==BITMAP) dirty();
        }
        
        /**
         * The letter-spacing of the text.
         */
        public function get letterSpacing():int { return int(_fmt.letterSpacing); }
        public function set letterSpacing(s:int):void {
            _fmt.letterSpacing = s;
            _tf.setTextFormat(_fmt);
            if (_mode==BITMAP) dirty();
        }
        
        /**
         * The horizontal anchor for the text, one of LEFT, RIGHT, or CENTER.
         * This setting determines how the text is horizontally aligned with
         * respect to this TextSprite's (x,y) location.
         */
        public function get horizontalAnchor():int { return _hAnchor; }
        public function set horizontalAnchor(a:int):void { 
            if (_hAnchor != a) { _hAnchor = a; layout(); }
        }
        
        /**
         * The vertical anchor for the text, one of TOP, BOTTOM, or MIDDLE.
         * This setting determines how the text is vertically aligned with
         * respect to this TextSprite's (x,y) location.
         */
        public function get verticalAnchor():int { return _vAnchor; }
        public function set verticalAnchor(a:int):void {
            if (_vAnchor != a) { _vAnchor = a; layout(); }
        }
        
        // --------------------------------------------------------------------
        
        /**
         * Creates a new TextSprite instance.
         * @param text the text string for this label
         * @param format the TextFormat determining font family, size, and style
         * @param mode the text rendering mode to use (BITMAP by default)
         */
        public function TextSprite(text:String=null, format:TextFormat=null, mode:int=BITMAP) {
            _tf = new TextField();
            _tf.selectable = false; // not selectable by default
            _tf.autoSize = TextFieldAutoSize.LEFT;
            _tf.defaultTextFormat = (_fmt = format ? format : new TextFormat());
            if (text != null) _tf.text = text;
            _bmap = new Bitmap();
            setMode(mode);
            dirty();
        }
        
        protected function setMode(mode:int):void
        {
            if (mode == _mode) return; // nothing to do
            
            switch (_mode) {
                case BITMAP:
                    _bmap.bitmapData = null;
                    removeChild(_bmap);
                    break;
                case EMBED:
                    _tf.embedFonts = false;
                case DEVICE:
                    removeChild(_tf);
                    break;
            }
            switch (mode) {
                case BITMAP:
                    rasterize();
                    addChild(_bmap);
                    break;
                case EMBED:
                    _tf.embedFonts = true;
                case DEVICE:
                    addChild(_tf);
                    break;
            }
            _mode = mode;
        }
        
        /**
         * Applies the settings of the input text format to this sprite's
         * internal text format. This method makes a shallow copy of the
         * input format, it does not save a reference to it.
         * @param fmt the text format to apply
         */
        public function applyFormat(fmt:TextFormat):void {
            _fmt.align = fmt.align;
            _fmt.blockIndent = fmt.blockIndent;
            _fmt.bold = fmt.bold;
            _fmt.bullet = fmt.bullet;
            _fmt.color = fmt.color;
            _fmt.display = fmt.display;
            _fmt.font = fmt.font;
            _fmt.indent = fmt.indent;
            _fmt.italic = fmt.italic;
            _fmt.kerning = fmt.kerning;
            _fmt.leading = fmt.leading;
            _fmt.leftMargin = fmt.leftMargin;
            _fmt.letterSpacing = fmt.letterSpacing;
            _fmt.rightMargin = fmt.rightMargin;
            _fmt.size = fmt.size;
            _fmt.tabStops = fmt.tabStops;
            _fmt.target = fmt.target;
            _fmt.underline = fmt.underline;
            _fmt.url = fmt.url;
            _tf.setTextFormat(_fmt);
            if (_mode==BITMAP) dirty();
        }
        
        /** @inheritDoc */
        public override function render():void
        {
            if (_mode == BITMAP) {
                rasterize();
            }
            layout();
        }
        
        /** @protected */
        protected function layout():void
        {
            var d:DisplayObject = (_mode==BITMAP ? _bmap : _tf);
            
            // horizontal anchor
            switch (_hAnchor) {
                case LEFT:   d.x = 0; break;
                case CENTER: d.x = -d.width / 2; break;
                case RIGHT:  d.x = -d.width; break;
            }
            // vertical anchor
            switch (_vAnchor) {
                case TOP:    d.y = 0; break;
                case MIDDLE: d.y = -d.height / 2; break;
                case BOTTOM: d.y = -d.height; break;
            }
        }
        
        /** @protected */
        protected function rasterize():void
        {
            var tw:Number = _tf.width + 1;
            var th:Number = _tf.height + 1;
            var bd:BitmapData = _bmap.bitmapData;
            if (bd == null || bd.width != tw || bd.height != th) {
                bd = new BitmapData(tw, th, true, 0x00ffffff);
                _bmap.bitmapData = bd;
            } else {
                bd.fillRect(new Rectangle(0,0,tw,th), 0x00ffffff);
            }
            bd.draw(_tf);
        }
        
    } // end of class TextSprite
}