
// overlay.cpp - render overlay text for hairline

#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <GL/glew.h>
#ifdef __APPLE__
# include <GLUT/glut.h>
#else
# include <GL/glut.h>
# ifdef _WIN32
#  include <windows.h>  // for wglGetProcAddress
# else
#  include <GL/glx.h>  // for glXGetProcAddress
# endif
#endif

#include <vector>

#include <Cg/double.hpp>
#include <Cg/vector/xyzw.hpp>
#include <Cg/matrix/1based.hpp>
#include <Cg/matrix/rows.hpp>
#include <Cg/vector.hpp>
#include <Cg/matrix.hpp>
#include <Cg/mul.hpp>
#include <Cg/distance.hpp>
#include <Cg/dot.hpp>
#include <Cg/normalize.hpp>
#include <Cg/round.hpp>
#include <Cg/upper2x2.hpp>
#include <Cg/iostream.hpp>
#include <Cg/inverse.hpp>
#include <Cg/degrees.hpp>
#include <Cg/radians.hpp>
#include <Cg/sqrt.hpp>
#include <Cg/any.hpp>
#include <Cg/all.hpp>

#include "polar/global_polar_settings.hpp"

using namespace Cg;
using std::vector;

// Make using iostream easier.
using std::cout;
using std::endl;

#include "sRGB_math.h"
#include "countof.h"
#include "request_vsync.h"
#include "showfps.h"
#include "cg4cpp_xform.hpp"
#include "parse_fms_mode.hpp"
#include "gl_framebuffer.hpp"
#include "gl_program.hpp"
#include "gl_debug_callback.h"  // for GLRegisterDebugCallback
#include "keyinput.hpp"
#include "overlay.hpp"
#include "glcolor.hpp"

#include "sg/path.hpp"

#include "data/pr_dogs.hpp"
#include "data/pr_psi.hpp"

#include "PathContainer.hpp"

#include "CurveConfig.hpp"

#include "color_space.hpp"

#include "Paint.hpp"
#include "polar/StrokeTessellator.hpp"
#include "hairline.hpp"
#include "main_window.hpp"
#include "cmdline.hpp"
#include "glcolor.hpp"

#ifndef CDECL
#define CDECL  // CDECL is a Windows thang
#endif

void CDECL gPrintf(float x, float y, const char *fmt, ...)
{
    char buf[1024];

    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    glWindowPos2f(x, y);
    void *font = GLUT_BITMAP_8_BY_13;
    const char *s = buf;
    while (*s) {
        glutBitmapCharacter(font, *s++);
    }
}

void CDECL gDualPrintf(float x, float y,
                       const GLfloat nameColor[3], const char *name,
                       const GLfloat valueColor[3], const char *valueFormat, ...)
{
    sRGB_glColor3fv(nameColor);
    glWindowPos2f(x, y);
    void *font = GLUT_BITMAP_8_BY_13;
    const int glyph_width = 8;  // since 8x13
    int x_pixels = 0;
    while (*name) {
        glutBitmapCharacter(font, *name++);
        x_pixels += glyph_width;
    }

    char buf[1024];
    va_list args;
    va_start(args, valueFormat);
    vsnprintf(buf, sizeof(buf), valueFormat, args);
    va_end(args);

    sRGB_glColor3fv(valueColor);
    glWindowPos2f(x + x_pixels, y);
    const char *s = buf;
    while (*s) {
        glutBitmapCharacter(font, *s++);
    }
}

static int overlayRow(int i)
{
    return 5+i*13;
}

void drawOverlayStatusText(const GLFramebuffer* fb, const Paint& paint, const LinkRenderStats& stats)
{
    const GLfloat* mode_color = white;
    const GLfloat* value_color = yellow;
    if (background == 1) {
        mode_color = black;
        value_color = blue;
    }

    int right_start = getMainWindowWidth()-340;
    const Paint::JoinEnum join_style = paint.JoinStyle();
    if (joinStyleConfig[join_style].miter_mode) {
        gDualPrintf(right_start, overlayRow(6), mode_color, "Miter limit: ", value_color, "%g", paint.MiterLimit());
    }
    gDualPrintf(right_start, overlayRow(3), mode_color, "Join style:  ", value_color, "%s", joinStyleConfig[join_style].name);

    char fbmode[30];
    if (fb->isMixedSamples()) {
        sprintf(fbmode, "%d:%d", fb->CoverageSamples(), fb->ColorSamples());
    } else {
        sprintf(fbmode, "%dx", fb->ColorSamples());
    }
    if (fb->isSupersampled()) {
        strcat(fbmode, " supersampled");
    }
    gDualPrintf(right_start, overlayRow(0), mode_color, "Framebuffer: ", value_color, "%s", fbmode);
    const char *color_space_str = (color_space == CORRECTED_SRGB) ? "sRGB corrected (best quality)" : "uncorrected";
    gDualPrintf(10, overlayRow(4), mode_color, "Color space: ", value_color, "%s", color_space_str);
    const char *min_win_stroke_width_str = stroke_tessellator_settings.MinWindowStrokeRadiusClamp() ? "ON at %g" : "OFF at %g";
    gDualPrintf(right_start, overlayRow(4), mode_color, "Min window stroke width: ",
        value_color, min_win_stroke_width_str, 2*stroke_tessellator_settings.MinWindowStrokeRadius());
    gDualPrintf(right_start, overlayRow(5), mode_color, "Trivial culling: ",
        value_color, stroke_tessellator_settings.TrivialCulling() ? "ON" : "off");

#if SUPPORT_POLAR_VISUALIZE
    if (use_NV_path_rendering){
        gDualPrintf(10, overlayRow(2), mode_color, "Mode: ", value_color, "NVPR");
    }
    else if (stroke_tessellator_settings.isPolarStroking()){
        gDualPrintf(10, overlayRow(2), mode_color, "Mode: ", value_color, "POLAR");
    } else{
        assert(stroke_tessellator_settings.isUniformStroking());
        gDualPrintf(10, overlayRow(2), mode_color, "Mode: ", value_color, "UNIFORM");
    }
#endif
    if (use_NV_path_rendering){
        gDualPrintf(120, overlayRow(2), mode_color, "Quads: ", value_color, "n/a");
        gDualPrintf(230, overlayRow(2), mode_color, "Links: ", value_color, "n/a");
        gDualPrintf(10, overlayRow(1), mode_color, "Drawn on: ", value_color, "GPU with NV_path_rendering");
    } else {
        gDualPrintf(120, overlayRow(2), mode_color, "Quads: ", value_color, "%d", stats.QuadsGenerated());
        gDualPrintf(10, overlayRow(6), mode_color, "Trivially culled links: ", value_color, "%d", stats.LinksTriviallyCulled());
        gDualPrintf(250, overlayRow(6), mode_color, "Links evaluated: ", value_color, "%d", stats.LinksEvaluated());
        gDualPrintf(230, overlayRow(2), mode_color, "Links: ", value_color, "%d", simple.numPatches());
        if (stroke_tessellator_settings.isPolarStroking()) {
            const float degrees_max_turning_angle_delta = degrees(stroke_tessellator_settings.MaxTurningAngleDelta());
            gDualPrintf(10, overlayRow(0), mode_color, "Max turning angle delta: ", value_color, "%g deg", degrees_max_turning_angle_delta);
        }
#if SUPPORT_POLAR_VISUALIZE
        else if (!use_NV_path_rendering) {
            gDualPrintf(10, overlayRow(0), mode_color, "Uniform iterations: ", value_color, "%d iterations", stroke_tessellator_settings.UniformSegments());
        }
#endif
    }
}

static int overlayTopRow(int i)
{
    return fb->Height() - 5-i*13;
}

void drawOverlayGLStatsText(const GLFramebuffer* fb, const GLLinkRenderStats& stats)
{
    const GLfloat* mode_color = white;
    const GLfloat* value_color = yellow;
    if (background == 1) {
        mode_color = black;
        value_color = blue;
    }

    int left_start = 10;
    int right_start = getMainWindowWidth()-340;
    gDualPrintf(left_start, overlayTopRow(1), mode_color, "Vertices submited:                         ", value_color, "%9d", stats.GLVerticesSubmitted());
    gDualPrintf(left_start, overlayTopRow(2), mode_color, "Primitives generated:                      ", value_color, "%9d", stats.GLPrimitivesGenerated());
    gDualPrintf(left_start, overlayTopRow(3), mode_color, "Tessellation control shader patches:       ", value_color, "%9d", stats.GLTessControlShaderPatches());
    gDualPrintf(left_start, overlayTopRow(4), mode_color, "Tessellation evaluation shader instances:  ", value_color, "%9d", stats.GLTessEvaluationShaderInvocations());
    gDualPrintf(left_start, overlayTopRow(5), mode_color, "Geometry shader invocations:               ", value_color, "%9d", stats.GLGeometryShaderInvocations());
    gDualPrintf(left_start, overlayTopRow(6), mode_color, "Geometry shader primitives emitted:        ", value_color, "%9d", stats.GLGeometryPrimitivesEmitted());
    gDualPrintf(left_start, overlayTopRow(7), mode_color, "Clipping input primitives:                 ", value_color, "%9d", stats.GLClippingInputPrimitives());
    gDualPrintf(left_start, overlayTopRow(8), mode_color, "Clipping output primitives:                ", value_color, "%9d", stats.GLClippingOutputPrimitives());
}
