"""Pen calculating area, center of mass, variance and standard-deviation,
covariance and correlation, and slant, of glyph shapes."""
import math
from fontTools.pens.momentsPen import MomentsPen

__all__ = ["StatisticsPen"]


class StatisticsPen(MomentsPen):

    """Pen calculating area, center of mass, variance and
    standard-deviation, covariance and correlation, and slant,
    of glyph shapes.

    Note that all the calculated values are 'signed'. Ie. if the
    glyph shape is self-intersecting, the values are not correct
    (but well-defined). As such, area will be negative if contour
    directions are clockwise.  Moreover, variance might be negative
    if the shapes are self-intersecting in certain ways."""

    def __init__(self, glyphset=None):
        MomentsPen.__init__(self, glyphset=glyphset)
        self.__zero()

    def _closePath(self):
        MomentsPen._closePath(self)
        self.__update()

    def __zero(self):
        self.meanX = 0
        self.meanY = 0
        self.varianceX = 0
        self.varianceY = 0
        self.stddevX = 0
        self.stddevY = 0
        self.covariance = 0
        self.correlation = 0
        self.slant = 0

    def __update(self):

        area = self.area
        if not area:
            self.__zero()
            return

        # Center of mass
        # https://en.wikipedia.org/wiki/Center_of_mass#A_continuous_volume
        self.meanX = meanX = self.momentX / area
        self.meanY = meanY = self.momentY / area

        #  Var(X) = E[X^2] - E[X]^2
        self.varianceX = varianceX = self.momentXX / area - meanX**2
        self.varianceY = varianceY = self.momentYY / area - meanY**2

        self.stddevX = stddevX = math.copysign(abs(varianceX) ** 0.5, varianceX)
        self.stddevY = stddevY = math.copysign(abs(varianceY) ** 0.5, varianceY)

        #  Covariance(X,Y) = ( E[X.Y] - E[X]E[Y] )
        self.covariance = covariance = self.momentXY / area - meanX * meanY

        #  Correlation(X,Y) = Covariance(X,Y) / ( stddev(X) * stddev(Y) )
        # https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient
        if stddevX * stddevY == 0:
            correlation = float("NaN")
        else:
            correlation = covariance / (stddevX * stddevY)
        self.correlation = correlation if abs(correlation) > 1e-3 else 0

        slant = covariance / varianceY if varianceY != 0 else float("NaN")
        self.slant = slant if abs(slant) > 1e-3 else 0


def _test(glyphset, upem, glyphs):
    from fontTools.pens.transformPen import TransformPen
    from fontTools.misc.transform import Scale

    print("upem", upem)

    for glyph_name in glyphs:
        print()
        print("glyph:", glyph_name)
        glyph = glyphset[glyph_name]
        pen = StatisticsPen(glyphset=glyphset)
        transformer = TransformPen(pen, Scale(1.0 / upem))
        glyph.draw(transformer)
        for item in [
            "area",
            "momentX",
            "momentY",
            "momentXX",
            "momentYY",
            "momentXY",
            "meanX",
            "meanY",
            "varianceX",
            "varianceY",
            "stddevX",
            "stddevY",
            "covariance",
            "correlation",
            "slant",
        ]:
            print("%s: %g" % (item, getattr(pen, item)))


def main(args):
    if not args:
        return
    filename, glyphs = args[0], args[1:]
    from fontTools.ttLib import TTFont

    font = TTFont(filename)
    if not glyphs:
        glyphs = font.getGlyphOrder()
    _test(font.getGlyphSet(), font["head"].unitsPerEm, glyphs)


if __name__ == "__main__":
    import sys

    main(sys.argv[1:])
