var EventEmitter = require('eventemitter3'),
    CONST = require('../const'),
    utils = require('../utils');

/**
 * A TextStyle Object decorates a Text Object. It acts as an event emitter, and can be shared between
 * multiple Text objects.
 *
 * @class
 * @extends EventEmitter
 * @memberof PIXI
 * @param [style] {object} The style parameters
 * @param [style.align='left'] {string} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text
 * @param [style.breakWords=false] {boolean} Indicates if lines can be wrapped within words, it needs wordWrap to be set to true
 * @param [style.dropShadow=false] {boolean} Set a drop shadow for the text
 * @param [style.dropShadowAngle=Math.PI/6] {number} Set a angle of the drop shadow
 * @param [style.dropShadowBlur=0] {number} Set a shadow blur radius
 * @param [style.dropShadowColor='#000000'] {string} A fill style to be used on the dropshadow e.g 'red', '#00FF00'
 * @param [style.dropShadowDistance=5] {number} Set a distance of the drop shadow
 * @param [style.fill='black'] {string|string[]|number|number[]|CanvasGradient|CanvasPattern} A canvas fillstyle that will be used on the
 *      text e.g 'red', '#00FF00'. Can be an array to create a gradient eg ['#000000','#FFFFFF'] @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN}
 * @param [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] {number} If fills styles are supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values
 * @param [style.fontFamily='Arial'] {string} The font family
 * @param [style.fontSize=26] {number|string} The font size (as a number it converts to px, but as a string, equivalents are '26px','20pt','160%' or '1.6em')
 * @param [style.fontStyle='normal'] {string} The font style ('normal', 'italic' or 'oblique')
 * @param [style.fontVariant='normal'] {string} The font variant ('normal' or 'small-caps')
 * @param [style.fontWeight='normal'] {string} The font weight ('normal', 'bold', 'bolder', 'lighter' and '100', '200', '300', '400', '500', '600', '700', 800' or '900')
 * @param [style.letterSpacing=0] {number} The amount of spacing between letters, default is 0
 * @param [style.lineHeight] {number} The line height, a number that represents the vertical space that a letter uses
 * @param [style.lineJoin='miter'] {string} The lineJoin property sets the type of corner created, it can resolve
 *      spiked text issues. Default is 'miter' (creates a sharp corner).
 * @param [style.miterLimit=10] {number} The miter limit to use when using the 'miter' lineJoin mode. This can reduce
 *      or increase the spikiness of rendered text.
 * @param [style.padding=0] {number} Occasionally some fonts are cropped on top or bottom. Adding some padding will
 *      prevent this from happening by adding padding to the top and bottom of text height.
 * @param [style.stroke='black'] {string|number} A canvas fillstyle that will be used on the text stroke e.g 'blue', '#FCFF00'
 * @param [style.strokeThickness=0] {number} A number that represents the thickness of the stroke. Default is 0 (no stroke)
 * @param [style.textBaseline='alphabetic'] {string} The baseline of the text that is rendered.
 * @param [style.wordWrap=false] {boolean} Indicates if word wrap should be used
 * @param [style.wordWrapWidth=100] {number} The width at which text will wrap, it needs wordWrap to be set to true
 */
function TextStyle(style)
{
    EventEmitter.call(this);
    Object.assign(this, this._defaults, style);
}

TextStyle.prototype = Object.create(EventEmitter.prototype);
TextStyle.prototype.constructor = TextStyle;
module.exports = TextStyle;

// Default settings. Explained in the constructor.
TextStyle.prototype._defaults = {
    align: 'left',
    breakWords: false,
    dropShadow: false,
    dropShadowAngle: Math.PI / 6,
    dropShadowBlur: 0,
    dropShadowColor: '#000000',
    dropShadowDistance: 5,
    fill: 'black',
    fillGradientType: CONST.TEXT_GRADIENT.LINEAR_VERTICAL,
    fontFamily: 'Arial',
    fontSize: 26,
    fontStyle: 'normal',
    fontVariant: 'normal',
    fontWeight: 'normal',
    letterSpacing: 0,
    lineHeight: 0,
    lineJoin: 'miter',
    miterLimit: 10,
    padding: 0,
    stroke: 'black',
    strokeThickness: 0,
    textBaseline: 'alphabetic',
    wordWrap: false,
    wordWrapWidth: 100
};

/**
 * Creates a new TextStyle object with the same values as this one.
 * Note that the only the properties of the object are cloned, not its event emitter.
 *
 * @return {PIXI.TextStyle} New cloned TextStyle object
 */
TextStyle.prototype.clone = function ()
{
    var clonedProperties = {};
    for (var key in this._defaults)
    {
        clonedProperties[key] = this[key];
    }
    return new TextStyle(clonedProperties);
};

/**
 * Resets all properties to the defaults specified in TextStyle.prototype._default
 */
TextStyle.prototype.reset = function ()
{
    Object.assign(this, this._defaults);
};

/**
 * Create setters and getters for each of the style properties. Converts colors where necessary.
 * Any set operation will emit a styleChanged event.
 */
Object.defineProperties(TextStyle.prototype, {
     align: {
        get: function ()
        {
            return this._align;
        },
        set: function (align)
        {
            if (this._align !== align)
            {
                this._align = align;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    breakWords: {
        get: function ()
        {
            return this._breakWords;
        },
        set: function (breakWords)
        {
            if (this._breakWords !== breakWords)
            {
                this._breakWords = breakWords;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    dropShadow: {
        get: function ()
        {
            return this._dropShadow;
        },
        set: function (dropShadow)
        {
            if (this._dropShadow !== dropShadow)
            {
                this._dropShadow = dropShadow;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    dropShadowAngle: {
        get: function ()
        {
            return this._dropShadowAngle;
        },
        set: function (dropShadowAngle)
        {
            if (this._dropShadowAngle !== dropShadowAngle)
            {
                this._dropShadowAngle = dropShadowAngle;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    dropShadowBlur: {
        get: function ()
        {
            return this._dropShadowBlur;
        },
        set: function (dropShadowBlur)
        {
            if (this._dropShadowBlur !== dropShadowBlur)
            {
                this._dropShadowBlur = dropShadowBlur;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    dropShadowColor: {
        get: function ()
        {
            return this._dropShadowColor;
        },
        set: function (dropShadowColor)
        {
            var outputColor = getColor(dropShadowColor);
            if (this._dropShadowColor !== outputColor)
            {
                this._dropShadowColor = outputColor;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    dropShadowDistance: {
        get: function ()
        {
            return this._dropShadowDistance;
        },
        set: function (dropShadowDistance)
        {
            if (this._dropShadowDistance !== dropShadowDistance)
            {
                this._dropShadowDistance = dropShadowDistance;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    fill: {
        get: function ()
        {
            return this._fill;
        },
        set: function (fill)
        {
            var outputColor = getColor(fill);
            if (this._fill !== outputColor)
            {
                this._fill = outputColor;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    fillGradientType: {
        get: function ()
        {
            return this._fillGradientType;
        },
        set: function (fillGradientType)
        {
            if (this._fillGradientType !== fillGradientType)
            {
                this._fillGradientType = fillGradientType;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    fontFamily: {
        get: function ()
        {
            return this._fontFamily;
        },
        set: function (fontFamily)
        {
            if (this.fontFamily !== fontFamily)
            {
                this._fontFamily = fontFamily;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    fontSize: {
        get: function ()
        {
            return this._fontSize;
        },
        set: function (fontSize)
        {
            if (this._fontSize !== fontSize)
            {
                this._fontSize = fontSize;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    fontStyle: {
        get: function ()
        {
            return this._fontStyle;
        },
        set: function (fontStyle)
        {
            if (this._fontStyle !== fontStyle)
            {
                this._fontStyle = fontStyle;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    fontVariant: {
        get: function ()
        {
            return this._fontVariant;
        },
        set: function (fontVariant)
        {
            if (this._fontVariant !== fontVariant)
            {
                this._fontVariant = fontVariant;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    fontWeight: {
        get: function ()
        {
            return this._fontWeight;
        },
        set: function (fontWeight)
        {
            if (this._fontWeight !== fontWeight)
            {
                this._fontWeight = fontWeight;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    letterSpacing: {
        get: function ()
        {
            return this._letterSpacing;
        },
        set: function (letterSpacing)
        {
            if (this._letterSpacing !== letterSpacing)
            {
                this._letterSpacing = letterSpacing;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    lineHeight: {
        get: function ()
        {
            return this._lineHeight;
        },
        set: function (lineHeight)
        {
            if (this._lineHeight !== lineHeight)
            {
                this._lineHeight = lineHeight;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    lineJoin: {
        get: function ()
        {
            return this._lineJoin;
        },
        set: function (lineJoin)
        {
            if (this._lineJoin !== lineJoin)
            {
                this._lineJoin = lineJoin;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    miterLimit: {
        get: function ()
        {
            return this._miterLimit;
        },
        set: function (miterLimit)
        {
            if (this._miterLimit !== miterLimit)
            {
                this._miterLimit = miterLimit;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    padding: {
        get: function ()
        {
            return this._padding;
        },
        set: function (padding)
        {
            if (this._padding !== padding)
            {
                this._padding = padding;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    stroke: {
        get: function ()
        {
            return this._stroke;
        },
        set: function (stroke)
        {
            var outputColor = getColor(stroke);
            if (this._stroke !== outputColor)
            {
                this._stroke = outputColor;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    strokeThickness: {
        get: function ()
        {
            return this._strokeThickness;
        },
        set: function (strokeThickness)
        {
            if (this._strokeThickness !== strokeThickness)
            {
                this._strokeThickness = strokeThickness;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    textBaseline: {
        get: function ()
        {
            return this._textBaseline;
        },
        set: function (textBaseline)
        {
            if (this._textBaseline !== textBaseline)
            {
                this._textBaseline = textBaseline;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    wordWrap: {
        get: function ()
        {
            return this._wordWrap;
        },
        set: function (wordWrap)
        {
            if (this._wordWrap !== wordWrap)
            {
                this._wordWrap = wordWrap;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    },

    wordWrapWidth: {
        get: function ()
        {
            return this._wordWrapWidth;
        },
        set: function (wordWrapWidth)
        {
            if (this._wordWrapWidth !== wordWrapWidth)
            {
                this._wordWrapWidth = wordWrapWidth;
                this.emit(CONST.TEXT_STYLE_CHANGED);
            }
        }
    }
});

/**
 * Utility function to convert hexadecimal colors to strings, and simply return the color if it's a string.
 *
 * @return {string} The color as a string.
 */
function getColor(color)
{
    if (typeof color === 'number')
    {
        return utils.hex2string(color);
    }
    else if (Array.isArray(color))
    {
        for (var i = 0; i < color.length; ++i)
        {
            if (typeof color[i] === 'number')
            {
                color[i] = utils.hex2string(color[i]);
            }
        }
    }

    return color;
}
