goog.provide('ol.style.FontSymbol');

goog.require('ol.style');
goog.require('ol.style.RegularShape');

/*	Copyright (c) 2015 Jean-Marc VIGLINO, 
 released under the CeCILL-B license (French BSD license)
 (http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt).
 
 *  Add a marker style to use with font symbols
 */
/**
 * @requires ol.style.Circle
 * @requires ol.structs.IHasChecksum
 */


/**
 * @classdesc
 * Set fontsymbol style for vector features.
 *
 * @constructor
 * @param {olx.style.FontSymbolOptions=} opt_options Options.
 * @extends {ol.style.RegularShape}
 * @implements {ol.structs.IHasChecksum}
 * @api
 */
ol.style.FontSymbol = function (opt_options) {
    options = opt_options || {};
    var strokeWidth = 0;
    if (options.stroke)
        strokeWidth = options.stroke.getWidth();
    ol.style.RegularShape.call(this, {
        radius: options.radius,
        fill: options.fill,
        rotation: options.rotation,
        rotateWithView: options.rotateWithView
    });

    this.color_ = options.color;
    this.fontSize_ = options.fontSize || 1;
    this.stroke_ = options.stroke;
    this.fill_ = options.fill;
    this.radius_ = options.radius - strokeWidth;
    this.form_ = options.form || "none";
    this.gradient_ = options.gradient;
    this.offset_ = [options.offsetX ? options.offsetX : 0, options.offsetY ? options.offsetY : 0];

    this.glyph_ = this.getGlyph(options.glyph) || "";

    this.renderMarker_();
};
ol.inherits(ol.style.FontSymbol, ol.style.RegularShape);

/** Cool stuff to get the image symbol for a style
 */
ol.style.Image.prototype.getImagePNG = function () {
    var canvas = this.getImage();
    if (canvas)
    {
        try {
            return canvas.toDataURL("image/png");
        } catch (e) {
            return false;
        }
    } else
        return false;
}

/** 
 *	Font defs
 */
ol.style.FontSymbol.prototype.defs = {fonts: {}, glyphs: {}};

/**
 *	Static function : add new font defs 
 */
ol.style.FontSymbol.addDefs = function (font, glyphs) {
    var thefont = font;
    if (typeof (font) == "string")
        thefont = {"font": font, "name": font, "copyright": ""};
    if (!thefont.font || typeof (thefont.font) != "string")
    {
        console.log("bad font def");
        return;
    }
    var fontname = thefont.font;
    ol.style.FontSymbol.prototype.defs.fonts[fontname] = thefont;
    for (var i in glyphs)
    {
        var g = glyphs[i];
        if (typeof (g) == "string" && g.length == 1)
            g = {char: g};

        ol.style.FontSymbol.prototype.defs.glyphs[i] = {
            font: thefont.font,
            char: g.char || "" + String.fromCharCode(g.code) || "",
            theme: g.theme || thefont.name,
            name: g.name || i,
            search: g.search || ""
        };
    }
}

/**
 * Get the fill style for the symbol.
 * @return {ol.style.Fill} Fill style.
 * @api
 */
ol.style.FontSymbol.prototype.getFill = function () {
    return this.fill_;
};

/**
 * Get the stroke style for the symbol.
 * @return {ol.style.Stroke} Stroke style.
 * @api
 */
ol.style.FontSymbol.prototype.getStroke = function () {
    return this.stroke_;
};

/**
 * Get the stroke style for the symbol.
 * @return {ol.style.Stroke} Stroke style.
 * @api
 */
ol.style.FontSymbol.prototype.getGlyph = function (name) {
    if (name)
        return ol.style.FontSymbol.prototype.defs.glyphs[name] || {"font": "none", "char": name.charAt(0), "theme": "none", "name": "none", "search": ""};
    else
        return this.glyph_;
};


/**
 * Get the stroke style for the symbol.
 * @return {ol.style.Stroke} Stroke style.
 * @api
 */
ol.style.FontSymbol.prototype.getFontInfo = function (glyph) {
    return ol.style.FontSymbol.prototype.defs.fonts[glyph.font];
}

ol.style.FontSymbol.prototype.renderMarker_ = function (atlasManager) {
    var strokeStyle;
    var strokeWidth = 0;

    if (this.stroke_)
    {
        strokeStyle = ol.color.asString(this.stroke_.getColor());
        strokeWidth = this.stroke_.getWidth();
    }

    // no atlas manager is used, create a new canvas
    var canvas = this.getImage();
    //console.log(this.getImage().width+" / "+(2 * (this.radius_ + strokeWidth) + 1));

    /** @type {ol.style.FontSymbol.RenderOptions} */
    var renderOptions = {
        strokeStyle: strokeStyle,
        strokeWidth: strokeWidth,
        size: canvas.width,
    };

    // draw the circle on the canvas
    var context = (canvas.getContext('2d'));
    context.clearRect(0, 0, canvas.width, canvas.height);
    this.drawMarker_(renderOptions, context, 0, 0);

    // Set Anchor
    var a = this.getAnchor();
    a[0] = canvas.width / 2 - this.offset_[0];
    a[1] = canvas.width / 2 - this.offset_[1];

    //this.createMarkerHitDetectionCanvas_(renderOptions);

};


/**
 * @private
 * @param {ol.style.FontSymbol.RenderOptions} renderOptions
 * @param {CanvasRenderingContext2D} context
 */
ol.style.FontSymbol.prototype.drawPath_ = function (renderOptions, context) {
    var s = 2 * this.radius_ + renderOptions.strokeWidth + 1;
    var w = renderOptions.strokeWidth / 2;
    var c = renderOptions.size / 2;
    // Transfo to place the glyph at the right place
    var transfo = {fac: 1, posX: renderOptions.size / 2, posY: renderOptions.size / 2};
    context.lineJoin = 'round';
    context.beginPath();

    // Draw the path with the form
    switch (this.form_)
    {
        case "none":
            transfo.fac = 1;
            break;
        case "circle":
        case "ban":
            context.arc(c, c, s / 2, 0, 2 * Math.PI, true);
            break;
        case "poi":
            context.arc(c, c - 0.4 * this.radius_, 0.6 * this.radius_, 0.15 * Math.PI, 0.85 * Math.PI, true);
            context.lineTo(c - 0.89 * 0.05 * s, (0.95 + 0.45 * 0.05) * s + w);
            context.arc(c, 0.95 * s + w, 0.05 * s, 0.85 * Math.PI, 0.15 * Math.PI, true);
            transfo = {fac: 0.45, posX: c, posY: c - 0.35 * this.radius_};
            break;
        case "bubble":
            context.arc(c, c - 0.2 * this.radius_, 0.8 * this.radius_, 0.4 * Math.PI, 0.6 * Math.PI, true);
            context.lineTo(0.5 * s + w, s + w);
            transfo = {fac: 0.7, posX: c, posY: c - 0.2 * this.radius_};
            break;
        case "marker":
            context.arc(c, c - 0.2 * this.radius_, 0.8 * this.radius_, 0.25 * Math.PI, 0.75 * Math.PI, true);
            context.lineTo(0.5 * s + w, s + w);
            transfo = {fac: 0.7, posX: c, posY: c - 0.2 * this.radius_};
            break;
        case "coma":
            /*
             context.arc( renderOptions.size / 2, renderOptions.size / 2 +0.2*this.radius_, 0.8*this.radius_, 0.5*Math.PI, 0, true);
             context.arc( renderOptions.size / 2, renderOptions.size / 2 -0.2*this.radius_, 0.8*this.radius_, 0, 0.5*Math.PI, true);
             */
            context.moveTo(c + 0.8 * this.radius_, c - 0.2 * this.radius_);
            context.quadraticCurveTo(0.95 * s + w, 0.75 * s + w, 0.5 * s + w, s + w);
            context.arc(c, c - 0.2 * this.radius_, 0.8 * this.radius_, 0.45 * Math.PI, 0, false);
            transfo = {fac: 0.7, posX: c, posY: c - 0.2 * this.radius_};
            break;
        default:
        {
            var pts;
            switch (this.form_)
            {
                case "shield":
                    pts = [0.05, 0, 0.95, 0, 0.95, 0.8, 0.5, 1, 0.05, 0.8, 0.05, 0];
                    transfo.posY = 0.45 * s + w;
                    break;
                case "blazon":
                    pts = [0.1, 0, 0.9, 0, 0.9, 0.8, 0.6, 0.8, 0.5, 1, 0.4, 0.8, 0.1, 0.8, 0.1, 0];
                    transfo.fac = 0.8;
                    transfo.posY = 0.4 * s + w;
                    break;
                case "bookmark":
                    pts = [0.05, 0, 0.95, 0, 0.95, 1, 0.5, 0.8, 0.05, 1, 0.05, 0];
                    transfo.fac = 0.9;
                    transfo.posY = 0.4 * s + w;
                    break;
                case "hexagon":
                    pts = [0.05, 0.2, 0.5, 0, 0.95, 0.2, 0.95, 0.8, 0.5, 1, 0.05, 0.8, 0.05, 0.2];
                    transfo.fac = 0.9;
                    transfo.posY = 0.5 * s + w;
                    break;
                case "diamond":
                    pts = [0.25, 0, 0.75, 0, 1, 0.2, 1, 0.4, 0.5, 1, 0, 0.4, 0, 0.2, 0.25, 0];
                    transfo.fac = 0.75;
                    transfo.posY = 0.35 * s + w;
                    break;
                case "triangle":
                    pts = [0, 0, 1, 0, 0.5, 1, 0, 0];
                    transfo.fac = 0.6;
                    transfo.posY = 0.3 * s + w;
                    break;
                case "sign":
                    pts = [0.5, 0.05, 1, 0.95, 0, 0.95, 0.5, 0.05];
                    transfo.fac = 0.7;
                    transfo.posY = 0.65 * s + w;
                    break;
                case "lozenge":
                    pts = [0.5, 0, 1, 0.5, 0.5, 1, 0, 0.5, 0.5, 0];
                    transfo.fac = 0.7;
                    break;
                case "square":
                default:
                    pts = [0, 0, 1, 0, 1, 1, 0, 1, 0, 0];
                    break;
            }
            for (var i = 0; i < pts.length; i += 2)
                context.lineTo(pts[i] * s + w, pts[i + 1] * s + w);
        }
    }

    context.closePath();
    return transfo;
}

/**
 * @private
 * @param {ol.style.FontSymbol.RenderOptions} renderOptions
 * @param {CanvasRenderingContext2D} context
 * @param {number} x The origin for the symbol (x).
 * @param {number} y The origin for the symbol (y).
 */
ol.style.FontSymbol.prototype.drawMarker_ = function (renderOptions, context, x, y) {
    var fcolor = this.fill_ ? this.fill_.getColor() : "#000";
    var scolor = this.stroke_ ? this.stroke_.getColor() : "#000";
    if (this.form_ == "none" && this.stroke_ && this.fill_)
    {
        scolor = this.fill_.getColor();
        fcolor = this.stroke_.getColor();
    }
    // reset transform
    context.setTransform(1, 0, 0, 1, 0, 0);

    // then move to (x, y)
    context.translate(x, y);

    var tr = this.drawPath_(renderOptions, context);

    if (this.fill_)
    {
        if (this.gradient_ && this.form_ != "none")
        {
            var grd = context.createLinearGradient(0, 0, renderOptions.size / 2, renderOptions.size);
            grd.addColorStop(1, ol.color.asString(fcolor));
            grd.addColorStop(0, ol.color.asString(scolor));
            context.fillStyle = grd;
        } else
            context.fillStyle = ol.color.asString(fcolor);
        context.fill();
    }
    if (this.stroke_ && renderOptions.strokeWidth) {
        context.strokeStyle = renderOptions.strokeStyle;
        context.lineWidth = renderOptions.strokeWidth;
        context.stroke();
    }

    // Draw the symbol
    if (this.glyph_.char)
    {
        context.font = (2 * tr.fac * (this.radius_) * this.fontSize_) + "px " + this.glyph_.font;
        context.strokeStyle = context.fillStyle;
        context.lineWidth = renderOptions.strokeWidth * (this.form_ == "none" ? 2 : 1);
        context.fillStyle = ol.color.asString(this.color_ || scolor);
        context.textAlign = "center";
        context.textBaseline = "middle";
        var t = this.glyph_.char;
        if (renderOptions.strokeWidth && scolor != "transparent")
            context.strokeText(t, tr.posX, tr.posY);
        context.fillText(t, tr.posX, tr.posY);
    }

    if (this.form_ == "ban" && this.stroke_ && renderOptions.strokeWidth)
    {
        context.strokeStyle = renderOptions.strokeStyle;
        context.lineWidth = renderOptions.strokeWidth;
        var r = this.radius_ + renderOptions.strokeWidth;
        var d = this.radius_ * Math.cos(Math.PI / 4);
        context.moveTo(r + d, r - d);
        context.lineTo(r - d, r + d);
        context.stroke();
    }
};

/**
 * @inheritDoc
 */
ol.style.FontSymbol.prototype.getChecksum = function () {
    var strokeChecksum = (this.stroke_ !== null) ?
            this.stroke_.getChecksum() : '-';
    var fillChecksum = (this.fill_ !== null) ?
            this.fill_.getChecksum() : '-';

    var recalculate = (this.checksums_ === null) ||
            (strokeChecksum != this.checksums_[1] ||
                    fillChecksum != this.checksums_[2] ||
                    this.radius_ != this.checksums_[3] ||
                    this.form_ + "-" + this.glyphs_ != this.checksums_[4]);

    if (recalculate) {
        var checksum = 'c' + strokeChecksum + fillChecksum
                + ((this.radius_ !== void 0) ? this.radius_.toString() : '-')
                + this.form_ + "-" + this.glyphs_;
        this.checksums_ = [checksum, strokeChecksum, fillChecksum, this.radius_, this.form_ + "-" + this.glyphs_];
    }

    return this.checksums_[0];
};
