code:
#include "TextRasterizer.h"
#include <string>
#include <cmath>
#include <limits>
#include <iostream>

#include "../../../environment/Environment.h"

TextRasterizer::TextRasterizer(const std::string &fontPath, const int size, const unsigned int resolution, const bool bold) {
    fontSize = size;
    FT_Init_FreeType(&lib);

    face = std::make_unique<FT_Face>();
    std::string path = Environment::getResourceDir() + "/" + fontPath;
    //std::cout << "font path [" << path << "]" << std::endl;
    if (bold) {
      path = path.substr(0, path.length() -4) + "-Bold.ttf";
    }
    //std::cout << "font path [" << path << "]" << std::endl;
    if (FT_New_Face(lib, path.c_str(), 0, face.get())) {
        std::cout << "Could not open font, bold: " << bold << std::endl;
        return;
    }
    if (!isUnicodeBMP(*face)) {
        std::cout << "Font is not Unicode BMP" << std::endl;
        return;
    }
    if (FT_Set_Char_Size(*face, 0, fontSize * 64, resolution, resolution)) {
        std::cout << "Could not set font size" << std::endl;
        return;
    }

    font = hb_ft_font_create(*face, nullptr);

    buffer = hb_buffer_create();
    if (!hb_buffer_allocation_successful(buffer)) {
        std::cout << "Could not create HarfBuzz buffer" << std::endl;
        return;
    }
}

TextRasterizer::~TextRasterizer() {
    hb_buffer_destroy(buffer);
    hb_font_destroy(font);
    FT_Done_FreeType(lib);
}

std::unique_ptr<sizeResponse> TextRasterizer::size(const rasterizationRequest &request) const {
    if (!request.availableWidth) {
        // window is likely minimized, so no output
        return nullptr;
    }
    if (request.startX == request.availableWidth) {
        std::cout << "TextRasterizer::size - x [" << static_cast<int>(request.startX) << "] matches window width [" << static_cast<int>(request.availableWidth)<< "] for text[" << request.text << "] no room to render anything" << std::endl;
        return nullptr;

    }
    if (request.startX > request.availableWidth) {
        std::cout << "TextRasterizer::size - x [" << static_cast<int>(request.startX) << "] requested outside of window width [" << static_cast<int>(request.availableWidth)<< "] for text[" << request.text << "]" << std::endl;
        return nullptr;
    }
    if (!buffer) {
        std::cout << "TextRasterizer::size - no font loaded" << std::endl;
        return nullptr;
    }

    unsigned int glyphCount;

    hb_buffer_reset(buffer);
    hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
    hb_buffer_set_language(buffer, hb_language_from_string("en", 2));

    hb_buffer_add_utf8(buffer, request.text.c_str(), request.text.length(), 0, request.text.length());

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
    const hb_feature_t KerningOn = { HB_TAG('k', 'e', 'r', 'n'), 1, 0, std::numeric_limits<unsigned int>::max() };
#pragma GCC diagnostic pop
    hb_shape(font, buffer, &KerningOn, 1);

    const hb_glyph_info_t *glyphInfo = hb_buffer_get_glyph_infos(buffer, &glyphCount);
    const hb_glyph_position_t *glyphPos = hb_buffer_get_glyph_positions(buffer, &glyphCount);

    std::unique_ptr<sizeResponse> response = std::make_unique<sizeResponse>();

    // figure out width/height
    int cx = 0;
    int cy = 0;
    int xmax = 0;
    //int y0max = 0;
    int y1max = 0;
    //int lines = 1;
    response->wrapped = false;
    int lineXStart = request.startX;
    //int leftPadding = 0;
    //int wrapToX = 0;
    int maxy0 = 0;
    for (unsigned int i = 0; i < glyphCount; i++) {
        if (FT_Load_Glyph(*face, glyphInfo[i].codepoint, FT_LOAD_DEFAULT)) {
            std::cout << "Could not load glyph" << std::endl;
            return nullptr;
        }
        const FT_GlyphSlot slot = (*face)->glyph;

        if (FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL)) {
            std::cout << "Could not render glyph" << std::endl;
            return nullptr;
        }

        // how much space does this character take
        const float xa = static_cast<float>(glyphPos[i].x_advance) / 64;
        const float ya = static_cast<float>(glyphPos[i].y_advance) / 64; //mostly 0s

        const float yo = static_cast<float>(glyphPos[i].y_offset) / 64;
        int y0 = static_cast<int>(floor(yo + slot->bitmap_top));

        //miny0 = std::min(y0, miny0);
        maxy0 = std::max(y0, maxy0);

        // do we need to padding the texture to the left for any lines
        if (cx == 0) {
            if (slot->bitmap_left < 0) {
                // figure out max amount of padding we need
                response->leftPadding = std::max(response->leftPadding, -slot->bitmap_left);
            }
        }

        // manual request to wrap (new line)
        //std::cout << "char " << request.text[i] << " codepoint: " << glyphInfo[i].codepoint << std::endl;
        if (request.text[i] == '\n' || request.text[i] == '\r'){
            //std::cout << "new line|return" << std::endl;
            response->wrapped = true;
            if (request.noWrap) {
                glyphCount = i;
                // we're done
                std::cout << "TextRasterizer::size - newline found no wrap is on [" << request.text << "]" << std::endl;
                break;
            } else {
                xmax=std::max(xmax, cx);
                cx = response->wrapToX;
                cy -= std::ceil(1.2f * fontSize); // 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
                response->lines++;
                lineXStart = response->wrapToX; // change where we start
                // don't add this character
                continue;
            }
        }

        // wrap to next line on width
        if (cx + lineXStart >= request.availableWidth) {
            response->wrapped = true;
            if (request.noWrap) {
                glyphCount = i;
            } else {
                //std::cout << "multine text: [" << text << "] new line:" << cy << " x: " << (int)x << "+ cx:" << (int)cx << std::endl;
                //std::cout << "line #" << lines << " starts at " << lineXStart << " ends at " << lineXStart + cx << std::endl;
                xmax = request.availableWidth - response->wrapToX; // the whole width of parent to the edge of windowWidth
                //std::cout << "new width: " << xmax << std::endl;
                cx = response->wrapToX; // wrapToX (was 0) (was -= xmax)
                cy -= std::ceil(1.2f * fontSize); // 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
                response->lines++;
                lineXStart = response->wrapToX; // change where we start
            }
        }

        //std::cout << "glyph:" << xa << "x" << ya << std::endl;

        // update glyph space allocation
        cx += xa;
        // note all over cy is in -
        cy += ya; // is normal for y0 at bottom

        // update glyph maxes
        const FT_Bitmap ftBitmap = slot->bitmap;
        //const float yo = static_cast<float>(glyphPos[i].y_offset) / 64;
        //int y0 = static_cast<int>(floor(yo + slot->bitmap_top));
        int y1 = y0 + static_cast<int>(ftBitmap.rows);
        response->y0max = std::max(response->y0max, y0);
        y1max=std::max(y1max, y1);

        // track new max width
        xmax=std::max(xmax, cx);

        // crop overflow
        if (request.cropHeight && cy > request.cropHeight) {
            break; // stop adding characters
        }
    }
    if (response->leftPadding) {
        xmax += response->leftPadding; // increase width;
    }
    // at least one line
    cy -= std::ceil(1.2f * fontSize); // 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
    response->height = -cy;
    response->width = xmax;
    //std::cout << "lines: " << lines << " wrapToX: " << wrapToX << " startX: " << x << " xmax: " << xmax << std::endl;
    // confirmed we aren't the same
    /*
    if (response->y0max != maxy0) {
        std::cout << "TextRasterizer::size - resp.y0max: " << response->y0max << " maxy0: " << maxy0 << std::endl;
    }
    */
    //std::cout << "y1max: " << y1max << " lines: " << response->lines << std::endl;
    int textureHeight = (y1max - response->y0max) * static_cast<int>(response->lines);
    //int textureHeight = y1max * response->lines;
    //y1max *= response->lines;
    //std::cout << "initial:" << (int)width << "x" << (int)height << std::endl;
    if (response->height < textureHeight) {
        response->height = textureHeight;
        //std::cout << "adjusted:" << (int)width << "x" << (int)height << std::endl;
    }

    // inline position where we leave off
    response->endingX = cx - request.sourceStartX;
    response->endingY = cy + 2 + request.sourceStartY;

    return response;
}

#include <GL/glew.h>

std::unique_ptr<rasterizationResponse> TextRasterizer::rasterize(const rasterizationRequest &request) const {

    if (request.startX == request.availableWidth) {
        std::cout << "TextRasterizer::rasterize - x [" << static_cast<int>(request.startX) << "] matches window width [" << static_cast<int>(request.availableWidth)<< "] for text[" << request.text << "] no room to render anything" << std::endl;
        return nullptr;

    }
    if (request.startX > request.availableWidth) {
        std::cout << "TextRasterizer::rasterize - x [" << static_cast<int>(request.startX) << "] requested outside of window width [" << static_cast<int>(request.availableWidth)<< "] for text[" << request.text << "]" << std::endl;
        return nullptr;
    }
    if (!buffer) {
        std::cout << "TextRasterizer::rasterize - no font loaded" << std::endl;
        return nullptr;
    }
    unsigned int glyphCount;

    hb_buffer_reset(buffer);
    hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
    hb_buffer_set_language(buffer, hb_language_from_string("en", 2));

    hb_buffer_add_utf8(buffer, request.text.c_str(), request.text.length(), 0, request.text.length());

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
    const hb_feature_t KerningOn = { HB_TAG('k', 'e', 'r', 'n'), 1, 0, std::numeric_limits<unsigned int>::max() };
#pragma GCC diagnostic pop
    hb_shape(font, buffer, &KerningOn, 1);

    const hb_glyph_info_t *glyphInfo = hb_buffer_get_glyph_infos(buffer, &glyphCount);
    const hb_glyph_position_t *glyphPos = hb_buffer_get_glyph_positions(buffer, &glyphCount);

    std::unique_ptr<rasterizationResponse> response = std::make_unique<rasterizationResponse>();
    std::unique_ptr<sizeResponse> sizeResponse = this->size(request);

    response->width = sizeResponse->width;
    response->height = sizeResponse->height;

    //std::cout << "after adjust:" << (int)response->width << "x" << (int)response->height << " lines" << lines << std::endl;
    // adjust sourceStart
    //std::cout << "TextRasterizer::rasterize - adjust sourceStart: " << request.sourceStartX << "," << request.sourceStartY << std::endl;
    response->width -= request.sourceStartX;
    response->height -= request.sourceStartY;
    //std::cout << "TextRasterizer::rasterize - final:" << static_cast<int>(response->width) << "x" << static_cast<int>(response->height) << std::endl;

    /*
     if (xmax==windowWidth - x) {
     std::cout << "Wrapped text[" << text << "] over " << lines << " lines " << xmax << "x" << static_cast<int>(height) << std::endl;
     }
     */
    //std::cout << "text size: " << (int)width << "x" << (int)height << std::endl;

    response->textureWidth = pow(2, ceil(log(response->width) / log(2)));
    response->textureHeight = pow(2, ceil(log(response->height) / log(2)));
    //std::cout << "text texture size:" << line->textureWidth << "x" << line->textureHeight << std::endl;
    size_t size = static_cast<size_t>(response->textureWidth * response->textureHeight); // here?
    response->textureData = std::make_unique<unsigned char[]>(size); // here?
    if (!response->textureData) {
        std::cout << "failed to create texture" << static_cast<int>(response->width) << "X" << static_cast<int>(response->height) << std::endl;
        return nullptr;
    }

    // translation information
    response->x0 = -sizeResponse->leftPadding; // wrap to element start (wrapToX (0) or x)
    response->y0 = 0;
    response->x1 = -sizeResponse->leftPadding + response->width;
    response->y1 = -response->height;
    //std::cout << "xd: " << static_cast<int>(response->x1-response->x0) << " yd: " << static_cast<int>(response->y0-response->y1) << std::endl;

    //std::cout << "Requesting texture of " << response->textureWidth << "x" << response->textureHeight << "max: " << win->maxTextureSize << std::endl;
    if (response->textureHeight > request.maxTextureSize) {
        std::cout << "Truncating text texture height to fit in your video card: " << response->textureHeight << ">" << request.maxTextureSize << std::endl;
        response->textureHeight = request.maxTextureSize;
    }

    // texture coords
    response->s0 = 0.0f;
    response->t0 = 0.0f;
    response->s1 = response->width / response->textureWidth;
    response->t1 = response->height / response->textureHeight;
    //std::cout << "s1: " << response->s1 << " t1: " << response->t1 << std::endl;

    // copy all glyphs into one single glyph
    // still neds to start at X
    int cx = response->wrapped ? request.startX : 0; // reset
    int cy = 0;
    //std::cout << "[" << request.text << "] wrapped? " << response->wrapped << " starting at: " << cx << std::endl;
    //int miny0 = 99;
    int maxy0 = 0;
    for (unsigned int i = 0; i < glyphCount; i++) {
        if (FT_Load_Glyph(*face, glyphInfo[i].codepoint, FT_LOAD_DEFAULT)) {
            std::cout << "Could not load glyph" << std::endl;
            return nullptr;
        }
        const FT_GlyphSlot slot = (*face)->glyph;

        if (FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL)) {
            std::cout << "Could not render glyph" << std::endl;
            return nullptr;
        }

        const FT_Bitmap ftBitmap = slot->bitmap;

        // figure out glyph starting point
        const float yo = static_cast<float>(glyphPos[i].y_offset) / 64;
        int y0 = static_cast<int>(floor(yo + slot->bitmap_top));

        //miny0 = std::min(y0, miny0);
        maxy0 = std::max(y0, maxy0);

        int bump = sizeResponse->y0max - y0; // Y adjust for this glyph
        const float xa = static_cast<float>(glyphPos[i].x_advance) / 64;

        if (request.text[i] == '\n' || request.text[i] == '\r'){
            //std::cout << "new line" << std::endl;
            if (request.noWrap) {
                // we're done
                //std::cout << "TextRasterize::rasterize - noWrap on, ending" << std::endl;
                break;
            } else {
                cx = sizeResponse->wrapToX;
                cy += std::ceil(1.2f * fontSize); // 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
                // no need to write new line char
                continue;
            }
        }

        // if this char is too width for this line, advance to next line
        if (!request.noWrap && cx + xa >= request.availableWidth) {
            cx = sizeResponse->wrapToX;
            cy += std::ceil(1.2f * fontSize);// 1.2 scalar from https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
            //std::cout << "textWrap - cx reset to: " <<  cx << " cy is now: " << cy << std::endl;
        }
        //std::cout << "placing glyph[" << text[i] << "] at " <<  cx << " cy is now: " << cy << std::endl;

        // crop overflow
        if (request.cropHeight && cy > request.cropHeight) {
            break; // stop adding characters
        }

        if (cx < request.sourceStartX) {
            // skip ahead
            cx += xa;
            continue;
        }
        if (cy < request.sourceStartY) {
            // skip ahead
            // seems to just an optimization
            //std::cout << "cy: " << cy << " < sourceStartY: " << request.sourceStartY << std::endl;
            continue;
        }
        //std::cout << "Writing " << request.text[i] << " to " << cx << ", " << cy << std::endl;

        // place glyph bitmap data into texture
        for (unsigned int iy = 0; iy < ftBitmap.rows; iy++) { // line by line
            // source is 0 to (0:iy:rows)
            // dest is cx+bl, (0:iy:rows)+(0:cy:height)+bump
            //std::cout << "placing glyph row at " << (cx + slot->bitmap_left) << "x" << ((iy + cy) + bump) << std::endl;
            unsigned int destPos = static_cast<unsigned int>(cx - request.sourceStartX + sizeResponse->leftPadding + slot->bitmap_left) + ((iy + static_cast<unsigned int>(cy - request.sourceStartY)) + static_cast<unsigned int>(bump)) * response->textureWidth;
            if (destPos >= size) {
                // we're done with this line
                continue;
            }
            unsigned char *src = ftBitmap.buffer + iy * static_cast<unsigned int>(ftBitmap.width);
            unsigned char *dest = response->textureData.get() + destPos;
            for(unsigned int ix = 0; ix < ftBitmap.width; ix++) { // pixel by pixel
                if (destPos + ix >= size) {
                    // we're done with this line
                    continue;
                }
                //std::cout << "putting:" << (int)src[ix] << " over " << (int)dest[ix] << std::endl;
                // probably could be or'd
                if (src[ix] && src[ix] > dest[ix]) {
                    dest[ix] = src[ix];
                }
            }
        }

        cx += xa;
    }
    //std::cout << "final size: " << (int)width << "x" << (int)height << std::endl;
    //std::cout << "at: " << (int)line->x0 << "x" << (int)line->y0 << " to: " << (int)line->x1 << "x" << (int)line->y1 <<std::endl;
    response->endingX = cx - request.sourceStartX; // maybe should be one xa less?
    //std::ceil(0.5 * 1.2f * fontSize)+2
    // should this be - request.sourceStartY?
    response->endingY = cy + maxy0 + request.sourceStartY; // definitely should be one lineheight higher
    //std::cout << "ending at " << response->endingX << "," << response->endingY << std::endl;

    return response;
}

bool TextRasterizer::isUnicodeBMP(const FT_Face &ftFace) const {
    for (int i = 0; i < ftFace->num_charmaps; i++) {
        if (((ftFace->charmaps[i]->platform_id == 0) && (ftFace->charmaps[i]->encoding_id == 3)) || ((ftFace->charmaps[i]->platform_id == 3) && (ftFace->charmaps[i]->encoding_id == 1))) {
            FT_Set_Charmap(ftFace, ftFace->charmaps[i]);
            return true;
        }
    }
    return false;
}


code:
#ifndef TEXTRASTERIZER_H
#define TEXTRASTERIZER_H

#include "hb.h"
#include "hb-ft.h"
#include <memory>
#include <string>
#include <algorithm>
#include "../renderers/glfw/Window.h"

struct Glyph {
    float x0;
    float y0;
    float x1;
    float y1;
    float s0;
    float t0;
    float s1;
    float t1;
    unsigned int textureWidth;
    unsigned int textureHeight;
    std::unique_ptr<unsigned char[]> textureData;
};

// we don't need x,y here except we do need to know how much width we have available
// we need to know x and y it left off at
// x in case we line wrap, so we know where we left off for inline
// y in case of multiline wrap, where the last line ended for inline
// and we'll need to know the starting x
// we don't need the starting y tbh
// even if windowWidth isn't a windowWidth, we still need some type of point to wrap on
struct rasterizationRequest {
    std::string text;
    int availableWidth; // (relative to 0)
    // not availableHeight because we're either going to wrap or not
    // is X relative to parent or absolute to window? well since no wrapTo, it's relative to parent
    int startX; // starting x point (relative to 0) this is currently
    //int wrapToX; // next line starts at X (was always 0)

    // scrolling basically:
    int sourceStartX = 0; // start reading text source at and place at destination 0
    int sourceStartY = 0;
    // overflow (0 means no limit)
    int cropWidth = 0;
    int cropHeight = 0;
    bool noWrap = false; // different than overflow but related
    unsigned int maxTextureSize = 0;
};

struct sizeResponse {
    float width;
    float height;
    int glyphCount;
    int endingX;
    int endingY;
    bool wrapped;
    int leftPadding = 0;
    int y0max = 0;
    int wrapToX = 0;
    unsigned int lines = 1;
};

struct rasterizationResponse {
    float width;
    float height;
    int glyphCount;
    int endingX;
    int endingY;
    bool wrapped;
    float x0;
    float y0;
    float x1;
    float y1; // calculated on other members here
    float s0;
    float t0;
    float s1; // calculated on other members here
    float t1; // calculated on other members here
    unsigned int textureWidth;
    unsigned int textureHeight;
    std::unique_ptr<unsigned char[]> textureData;
};

// container for a font at size bold/regular
class TextRasterizer {
private:
    int fontSize;
public:
    TextRasterizer(const std::string &fontPath, const int size, const unsigned int resolution, const bool bold);
    ~TextRasterizer();
    //std::unique_ptr<Glyph[]> rasterize(const std::string &text, const int x, const int windowWidth, const int wrapToX, float &width, float &height, unsigned int &glyphCount, int &endingX, int &endingY, bool &wrapped) const;
    std::unique_ptr<sizeResponse> size(const rasterizationRequest &request) const;
    std::unique_ptr<rasterizationResponse> rasterize(const rasterizationRequest &request) const;
    bool isUnicodeBMP(const FT_Face &face) const;
    FT_Library lib;
    hb_font_t *font;
    hb_buffer_t *buffer;
    std::unique_ptr<FT_Face> face;
};

#endif

repo_name: vaartis/netrunner

license: mit


code:
#include "TextRasterizerCache.h"
#include <iostream>

TextRasterizerCache *rasterizerCache=new TextRasterizerCache;

// reduces this:
// Updated DOM in: 6.787870 seconds
// to this:
// Updated DOM in: 6.104453 seconds
std::shared_ptr<TextRasterizer> TextRasterizerCache::loadFont(const unsigned int size, const bool bold) {
    if (bold) {
        if (fontSizes_bold.find(size) == fontSizes_bold.end()) {
            //std::cout << "Making bold font" << std::endl;
            fontSizes_bold[size]=std::make_shared<TextRasterizer>("DejaVuSerif.ttf", size, 72, bold);
        }
        return fontSizes_bold[size];
    }
    else {
        if (fontSizes_notbold.find(size) == fontSizes_notbold.end()) {
            //std::cout << "Making font" << std::endl;
            fontSizes_notbold[size]=std::make_shared<TextRasterizer>("DejaVuSerif.ttf", size, 72, bold);
        }
        return fontSizes_notbold[size];
    }
}


code:
#ifndef TEXTRASTERIZERCACHE_H
#define TEXTRASTERIZERCACHE_H

#include "TextRasterizer.h"
#include <map>

class TextRasterizerCache {
private:
    std::map<unsigned int, std::shared_ptr<TextRasterizer> > fontSizes_bold;
    std::map<unsigned int, std::shared_ptr<TextRasterizer> > fontSizes_notbold;
public:
    std::shared_ptr<TextRasterizer> loadFont(const unsigned int size, const bool bold);
};

#endif


code:
#include "Shader.h"
#include <iostream>

void Shader::bind() const {
	glUseProgram(handle);
}

void Shader::release() const {
	glUseProgram(0);
}

int Shader::uniform(const std::string &name) {
	// Is it already in the map?
	auto it = locationMap.find(name);
	if (it != locationMap.end()) {
		return it->second;
	}

	// It isn't, get the uniform location and store it
	GLint location = glGetUniformLocation(handle, name.c_str());
    GLenum glErr=glGetError();
    if(glErr != GL_NO_ERROR) {
        std::cout << "Shader::uniform " << name << " - glGetUniformLocation - not ok: " << glErr << std::endl;
    }
    locationMap.insert(std::make_pair(name, location));

	return location;
}

int Shader::attribute(const std::string &name) {
	// Is it already in the map?
	auto it = locationMap.find(name);
	if (it != locationMap.end()) {
		return it->second;
	}

	// It isn't, get the attribute location and store it
	GLint location = glGetAttribLocation(handle, name.c_str());
	locationMap.insert(std::make_pair(name, location));

	return location;
}


code:
#ifndef SHADER_H
#define SHADER_H

#include <GL/glew.h>
#include <string>
#include <map>

struct VertexShader {
	VertexShader(std::string n) : name(n) { }
	std::string name;
};

struct FragmentShader {
	FragmentShader(std::string n) : name(n) { }
	std::string name;
};

class Shader {
public:
	enum Type {
		Vertex,
		Fragment
	};

	Shader(unsigned int h) : handle(h) { }
	void bind() const;
	void release() const;

	unsigned int handle;

	int uniform(const std::string &name);
	int attribute(const std::string &name);
private:
	std::map<std::string, int> locationMap;
};

#endif


code:
#include "ShaderLoader.h"

#include <stdlib.h>
#include <fstream>
#include <iostream>

#include "Shader.h"
#include "../../../../Log.h"
#include "../../../../environment/Environment.h"
#include "../../../../environment/Path.h"

constexpr GLint maxLogSize = 1024 * 1024 * 2; //2 MiB

GLuint ShaderLoader::loadShader(const std::string &shaderName, Shader::Type type) {
	GLenum glType = getGlShaderType(type);
	if (!glType) {
		logError() << "Unknown shader type: " << static_cast<int>(type) << std::endl;
		return 0;
	}
	GLuint shader = glCreateShader(glType);

	std::string path = Path::fromUnixPath(Environment::getResourceDir() + "/shaders/" + shaderName);
	std::ifstream file(path, std::ifstream::ate);
	if (!file.is_open()) {
		logError() << "Could not open shader " << shaderName << std::endl;
		return 0;
	}

	long fileSize = file.tellg();
	file.seekg(std::ifstream::beg);
	char *buffer = static_cast<char *>(malloc(static_cast<unsigned int>(fileSize) + 1));
	file.read(buffer, fileSize);
	buffer[fileSize] = 0;
	glShaderSource(shader, 1, &buffer, nullptr);
	glCompileShader(shader);

	free(buffer);

	if(checkShader(shader, shaderName, type))
		return 0;

	return shader;
}

GLuint ShaderLoader::createProgram(const std::string &shader1Name, Shader::Type shader1Type,
	   	const std::string &shader2Name, Shader::Type shader2Type) {
	GLuint shader1 = loadShader(shader1Name, shader1Type);
	GLuint shader2 = loadShader(shader2Name, shader2Type);

	if (!shader1 || !shader2)
		return 0;

	GLuint program = glCreateProgram();
	glAttachShader(program, shader1);
	glAttachShader(program, shader2);

	glLinkProgram(program);
	if (checkProgram(program, GL_LINK_STATUS, shader1Name))
		return 0;
	/* glValidateProgram(program);
	if (checkProgram(program, GL_VALIDATE_STATUS, shader1Name))
		return 0; */

	return program;
}

Shader *ShaderLoader::getShader(VertexShader vertexShader, FragmentShader fragmentShader) {
	// concatenate strings to create unique key for the shader
    //  + "_" + std::to_string(windowId)
    std::string key = vertexShader.name + "_" + fragmentShader.name;

	// has the shader been cached?
	auto it = shaderCache.find(key);
	if (it != shaderCache.end())
		return it->second;

	// Ok. It isn't. Let's create the shader Q.Q
	GLuint program = createProgram(vertexShader.name, Shader::Type::Vertex,
		   	fragmentShader.name, Shader::Type::Fragment);
	if (!program) //reee it failed
		return nullptr;

	Shader *shader = new Shader(program);
	shaderCache.insert(std::make_pair(key, shader));
	return shader;
}

int ShaderLoader::checkShader(GLuint shader, const std::string &shaderName, Shader::Type type) {
	GLint success = 0;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &success);

	if (success == GL_TRUE) {
		//logDebug() << "Shader " << shaderName << " compiled successfully!" << std::endl;
	} else {
		GLint logSize = 0;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logSize);
		if (logSize > maxLogSize) {
			logError() << "Shader " << shaderName << " failed to compile but \
				the log is too long to show." << std::endl;
			return 1;
		}
		char *log = new char[logSize];
		glGetShaderInfoLog(shader, logSize, &logSize, log);
		logError() << "Shader " << shaderName << " failed to compile. GL log output: "
			<< log << std::endl;
		delete[] log;
		glDeleteShader(shader);
		return 1;
	}
	return 0;
}

int ShaderLoader::checkProgram(GLuint program, GLenum pname, const std::string &shaderName) {
	GLint success = 0;
	glGetProgramiv(program, pname, &success);
	if (success == GL_TRUE) {
		//logDebug() << "Program with shader1 " << shaderName << " " << getProgramStatusString(pname) << std::endl;
	} else {
		GLint logSize = 0;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logSize);
		if (logSize > maxLogSize) {
			logError() << "Program with shader1 " << shaderName << " failed to be "
				<< getProgramStatusString(pname) << " but the log is too long to show" << std::endl;
			return 1;
		}
		char *log = new char[logSize];
		glGetProgramInfoLog(program, logSize, &logSize, log);
		logError() << "Program with shader1 " << shaderName << " failed to be "
			<< getProgramStatusString(pname) << ". GL log output: " << log << std::endl;
		delete[] log;
		glDeleteProgram(program);
		return 1;
	}
	return 0;
}

const char *ShaderLoader::getProgramStatusString(GLenum pname) {
	switch (pname) {
		case GL_LINK_STATUS:
			return "linked";
		case GL_VALIDATE_STATUS:
			return "validated";
		default:
		break;
	}
	return "";
}

GLenum ShaderLoader::getGlShaderType(const Shader::Type type) {
	switch (type) {
		case Shader::Vertex:
			return GL_VERTEX_SHADER;
		case Shader::Fragment:
			return GL_FRAGMENT_SHADER;
		default:
		break;
	}
	return 0;
}


code:
#ifndef SHADERLOADER_H
#define SHADERLOADER_H

#include <GL/glew.h>
#include <string>
#include <map>

#include "Shader.h"

class ShaderLoader {
public:
	using ShaderCache = std::map<std::string, Shader*>;

	// Get a shader from the shader cache and load it from disk it isn't there
	Shader *getShader(VertexShader vertexShader, FragmentShader fragmentShader);

	// Load a shader from disk and return the its handle
	GLuint loadShader(const std::string &shader, Shader::Type shaderType);

	GLuint createProgram(const std::string &shader1Name, Shader::Type shader1Type,
			const std::string &shader2Name, Shader::Type shader2Type);
private:
	ShaderCache shaderCache;

	int checkProgram(GLuint program, GLenum programName,
			const std::string &shaderName);

	int checkShader(GLuint shader, const std::string &shaderName, Shader::Type shaderType);

	GLenum getGlShaderType(const Shader::Type type);

	const char *getProgramStatusString(GLenum pname);
};
#endif


code:
#include "../../../../URL.h"
#include "Window.h"
#include "../../../../parsers/markup/TagNode.h"
#include "../../../../parsers/markup/TextNode.h"

#include "../../../../networking/HTTPRequest.h"
#include "../../../../parsers/markup/html/HTMLParser.h"
#include "../../../../StringUtils.h"
#include "../../../../URL.h"
#include "../../../../Log.h"
#include "../../../components/BoxComponent.h"
#include "../../../components/ImageComponent.h"
#include "../../../components/DocumentComponent.h"
#include "../../../components/TabbedComponent.h"
#include "../../../components/TextComponent.h"
#include "../../../components/TabbedComponent.h"
#include "../../../components/InputComponent.h"
#include "Shader.h"
#include "../../../../browser.h"

#include <cmath>
#include <ctime>
#include <iostream>

extern std::unique_ptr<Browser> browser;

Window::~Window() {
    glfwTerminate();
}

bool Window::init() {

    Rect windowPosSize;
    windowPosSize.x = 0;
    windowPosSize.y = 0;
    // these are set before init is called
    windowPosSize.w = this->windowWidth;
    windowPosSize.h = this->windowHeight;

    OpenGL *pRenderer = dynamic_cast<OpenGL *>(renderer.get());
    if (!pRenderer) {
        //WindowHandle *tWin = renderer->createWindow("NeTRunner", &windowPosSize, 0);
        //this->window = tWin->window;
        std::cout << "Window::init - we only support OpenGL renderer" << std::endl;
        return false;
    }
    this->openglWindow = pRenderer->createWindow("NeTRunner", &windowPosSize, 0);
    this->window = this->openglWindow->window;

    //std::cout << "OpenGL is set up" << std::endl;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &this->maxTextureSize);
    std::cout << "Max texture size: " << this->maxTextureSize << std::endl;

    // set up our UI with a new window
    this->ui = std::make_unique<MultiComponent>(0, 0, this->windowWidth, this->windowHeight, this->windowWidth, this->windowHeight);
    this->ui->windowed = true;
    this->ui->win = shared_from_this();

    this->cursorHand = pRenderer->cursorHand;
    this->cursorArrow = pRenderer->cursorArrow;
    this->cursorIbeam = pRenderer->cursorIbeam;

    // relay events to our ui
    // window
    //this->openglWindow->onResize = this->ui->onResize;
    // use our delayed resizer
    this->openglWindow->onResize=[&](int w, int h) {
        this->onResize(w, h);
    };
    // mouse
    this->openglWindow->onMouseMove = this->ui->onMousemove;
    this->openglWindow->onMouseDown = this->ui->onMousedown;
    this->openglWindow->onMouseUp = this->ui->onMouseup;
    this->openglWindow->onWheel = this->ui->onWheel;
    // keyboard
    this->openglWindow->onKeyUp = [&](int key, int scancode, int mods) {
        this->ui->onKeyUp(key, scancode, 0, mods);
    };
    return true;
}

bool Window::initGLFW() {
    if (!glfwInit()) {
        std::cout << "Could not initialize GLFW" << std::endl;
        return false;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);

    glfwSetErrorCallback([](int error, const char* description) {
        std::cout << "glfw error [" << error << "]:" << description << std::endl;
    });

    GLFWwindow *context = nullptr;
    if (browser->windows.size()) {
        context = browser->windows.front().get()->window;
    }
    // after title, it's monitor
    window = glfwCreateWindow(windowWidth, windowHeight, "NetRunner", nullptr, context);
    if (!window) {
        glfwTerminate();
        std::cout << "Could not create window" << std::endl;
        return false;
    }
    // replace first parameter of all these callbacks with our window object instead of a GLFWwindow
    glfwSetWindowUserPointer(window, this);
    // set window w/h
    //glfwGetFramebufferSize(window, &windowWidth, &windowHeight); // in pixels
    glfwGetWindowSize(window, &windowWidth, &windowHeight); // use screen coordinates (not pixels) more retina friendly

    // set up event callbacks
    glfwSetFramebufferSizeCallback(window, [](GLFWwindow *win, int width, int height) {
        glViewport(0, 0, width, height);
    });
    glfwSetWindowSizeCallback(window, [](GLFWwindow *win, int width, int height) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        thiz->onResize(width, height);
    });
    cursorHand = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
    cursorArrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
    cursorIbeam = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
    glfwSetCursorPosCallback(window, [](GLFWwindow *win, double xPos, double yPos) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        thiz->cursorX = xPos;
        thiz->cursorY = yPos;
        //std::cout << "Window::Window:onMousemove - at " << static_cast<int>(xPos) << "," << static_cast<int>(yPos) << std::endl;
        if (xPos < 0 || yPos < 0) return;
        if (xPos > thiz->windowWidth || yPos > thiz->windowHeight) return;
        // p. much worthless on double
        /*
        static double lx = 0;
        static double ly = 0;
        if (lx == xPos && ly == yPos) {
            return;
        }
        lx = xPos;
        ly = yPos;
        std::cout << "Window::Window:onMousemove - noCache" << std::endl;
        */
        //std::cout << "Window::Window:onMousemove - begin search" << std::endl;
        //std::cout << "Window::Window:onMousemove - windowHeight: " << thiz->windowHeight << " cursorY: " << thiz->cursorY << " scrollY: " << thiz->transformMatrix[13] << std::endl;
        thiz->ui->onMousemove(thiz->cursorX, thiz->cursorY);

    });
    glfwSetScrollCallback(window, [](GLFWwindow *win, double xOffset, double yOffset) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        //std::cout << "glfwSetScrollCallback " << yOffset << " int" << (int)(yOffset * 10) << std::endl;
        thiz->ui->onWheel(xOffset * 10, yOffset * 10);
    });
    glfwSetMouseButtonCallback(window, [](GLFWwindow *win, int button, int action, int mods) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
            //std::cout << "left press" << std::endl;
            thiz->ui->onMousedown(thiz->cursorX, thiz->cursorY);
        }
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {
            //std::cout << "left release" << std::endl;
            thiz->ui->onMouseup(thiz->cursorX, thiz->cursorY);
        }
    });

    // works with utf-32 and os keyboard layout but we'll lkeep the low level for now
    glfwSetCharCallback(window, [](GLFWwindow* win, unsigned int codepoint) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        //std::cout << "Window::glfwSetCharCallback - codepoint: " << codepoint << std::endl;

        // should be used for inputComponent
    });

    glfwSetKeyCallback(window, [](GLFWwindow *win, int key, int scancode, int action, int mods) {
        Window *thiz = reinterpret_cast<Window*>(glfwGetWindowUserPointer(win));
        //std::cout << "Key " << key << " action: " << action << "mods: " << mods << std::endl;

// command for OSX, otherwise control
#if defined(__APPLE__) && defined(__MACH__)
        int attentionMod = GLFW_MOD_SUPER;
#else
        int attentionMod = GLFW_MOD_CONTROL;
#endif
        if (key == GLFW_KEY_N && action == GLFW_RELEASE && mods == attentionMod) {
            browser->addWindow();
            return;
        }
        if (key == GLFW_KEY_L && action == GLFW_RELEASE && mods == attentionMod) {
            if (thiz->ui->focusedComponent && thiz->ui->focusedComponent->onBlur) {
                thiz->ui->focusedComponent->onBlur();
            }
            if (thiz->addressComponent && thiz->addressComponent->onFocus) {
                thiz->addressComponent->onFocus();
            }
            thiz->ui->focusedComponent = thiz->addressComponent;
            return;
        }
        if (key == GLFW_KEY_T && action == GLFW_RELEASE && mods == attentionMod) {
            TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(thiz->tabComponent.get());
            if (p_tabComponent) {
                p_tabComponent->selectTab(p_tabComponent->addTab("New Tab"));
            }
            return;
        }
        if (action == GLFW_RELEASE) {
            thiz->ui->onKeyUp(key, scancode, action, mods);
        }
        if (action == GLFW_REPEAT) {
            //thiz->ui->onKeyRepeat(key, scancode, mods);
            //thiz->ui->onKeyPress(key, scancode, action, mods);
        }
        if (action == GLFW_PRESS) {
            //thiz->ui->onKeyDown(key, scancode, mods);
            //thiz->ui->onKeyPress(key, scancode, action, mods);
        }
    });

    glfwMakeContextCurrent(window);

    return true;
}

void Window::onResize(int passedWidth, int passedHeight) {
    this->windowWidth = passedWidth;
    this->windowHeight = passedHeight;
    this->delayResize = 1;
}

void Window::scrollDocument(int y) {
    //TabbedComponent *p_TabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
    //if (p_TabComponent) {
        // dynamic_cast<DocumentComponent*>(p_TabComponent->documentComponent.get());
        DocumentComponent *p_DocComponent = this->getActiveDocumentComponent().get();
        if (p_DocComponent) {
            p_DocComponent->transformMatrix[13] += y;
            p_DocComponent->textureTransformMatrix[13] += y;
            p_DocComponent->transformMatrixDirty = true;
            this->renderDirty = true;
            //this->transformMatrixDirty = true;
        } else {
            std::cout << "Window::scrollDocument - no active docComponent" << std::endl;
        }
    //} else {
        //std::cout << "Window::scrollDocument - no tabComponent" << std::endl;
    //}
}

void Window::resize() {
    ui->resize(this->windowWidth, this->windowHeight);
    renderDirty = true;
}

void Window::render() {
    if (delayResize) {
        delayResize--;
        if (delayResize) {
            return;
        }
        //std::cout << "Window::render - restarting drawing" << std::endl;
        this->resize();
    }
    if (renderDirty) {

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        ui->render();
        glfwSwapBuffers(window);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        ui->render();
        glfwSwapBuffers(window);

        renderDirty = false;
    }
    glfwSwapBuffers(window);
    glfwPollEvents();
}

std::shared_ptr<DocumentComponent> Window::getActiveDocumentComponent() {
    // need to cast it first
    TabbedComponent *p_tabComponent = dynamic_cast<TabbedComponent*>(this->tabComponent.get());
    if (!p_tabComponent) {
        std::cout << "Window::getActiveDocumentComponent - No active tab" << std::endl;
        return nullptr;
    }
    std::shared_ptr<DocumentComponent> docComponent = std::static_pointer_cast<DocumentComponent>(p_tabComponent->documentComponent);
    if (!docComponent) {
        std::cout << "Window::getActiveDocumentComponent - No active document" << std::endl;
        return nullptr;
    }
    return docComponent;
}


code:
#ifndef WINDOW_H
#define WINDOW_H

#include "../../../components/ComponentBuilder.h"
#include "../../../components/MultiComponent.h"
//#include "../components/Component.h"
#include "ShaderLoader.h"
#include "../../../../parsers/markup/Node.h"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <memory>
#include <vector>
#include <algorithm>
#include "../../../../networking/HTTPResponse.h"
#include "../../../../URL.h"
#include "opengl.h"

class DocumentComponent;

extern std::unique_ptr<BaseRenderer> renderer;

// we don't want to extend WindowHandle because this is an API for the system in general
// so we'll embed it as a member, and we can add the calls
class Window : public std::enable_shared_from_this<Window> {
private:
public:
    ~Window();
    bool init();
    bool initGLFW();

    void render();
    //void setDOM(const std::shared_ptr<Node> rootNode);
    //void createComponentTree(const std::shared_ptr<Node> rootNode, const std::shared_ptr<Component> &parentComponent);
    //void renderComponentType(std::string str, std::shared_ptr<Component> component);

    void onResize(int passedWidth, int passedHeight);
    void checkForResize();
    void resize();
    void scrollDocument(int y);

    //void resizeComponentTree(const std::shared_ptr<Component> &component, const int windowWidth, const int windowHeight);
    //std::shared_ptr<Component> searchComponentTree(const std::shared_ptr<Component> &component, const int x, const int y);

    //void navTo(std::string url);

    // properties
    int maxTextureSize = 0;
    unsigned int oglActiveShader = 0; // cache to minimize opengl state changes

    // just a default matrix to reset UI since it won't scroll
    float textureTransformMatrix[16] = {
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
    };

    // text uses a different one, since we have a different clamp on it
    float transformMatrix[16] = {
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 2, 0, 1
    };
    //bool transformMatrixDirty = true;
    GLFWwindow *window;
    OpenGLWindowHandle *openglWindow;
    int windowWidth;
    int windowHeight;
    //std::shared_ptr<Node> domRootNode = nullptr;
    //bool domDirty = false;
    bool renderDirty = true;

    //ComponentBuilder componentBuilder;
    //std::shared_ptr<Component> rootComponent = std::make_shared<Component>();

    // could break these out in some sort of cursor class
    // to minimize dependencies
    //std::shared_ptr<Component> focusedComponent = nullptr;
    //std::shared_ptr<Component> hoverComponent = nullptr;
    double cursorX = 0;
    double cursorY = 0;
    unsigned int delayResize = 0;
    GLFWcursor* cursorHand;
    GLFWcursor* cursorArrow;
    GLFWcursor* cursorIbeam;

    // why is this a pointer? I think we thought it'd make moving the UI between windows easier
    // open to opinions on changing this to a not-pointer
    std::unique_ptr<MultiComponent> ui = nullptr;

    ShaderLoader shaderLoader;

    // is there only one selection per window? (probably not)
    int highlightStartX = 0;
    int highlightStartY = 0;
    std::vector<std::shared_ptr<Component>> selectionList;
    // multiwindow support
    size_t id; // key for shader caches
    unsigned int clearColor = 0xCCCCCCCC;

    // I hate doing this but we currently require this
    // FIXME: move these out into browser somehow
    std::shared_ptr<DocumentComponent> getActiveDocumentComponent();
    std::shared_ptr<Component> tabComponent = nullptr;
    std::shared_ptr<Component> addressComponent = nullptr;
};

//bool setWindowContent(URL const& url);
//void handleRequest(const HTTPResponse &response);
//extern const std::unique_ptr<Window> window;

#endif


code:
#include "opengl.h"
#include "../../../../browser.h"

#include <iostream>
#include <cmath>
#include <cstring> // for memset

// for creating new windows
extern std::unique_ptr<Browser> browser;

inline void checkGLState(std::string whereWhat) {
    GLenum glErr=glGetError();
    if(glErr != GL_NO_ERROR) {
        std::cout << whereWhat << " - not ok: " << glErr << std::endl;
    }
}

// maybe change to return std::pair<GLuint, GLuint>
// with the renderer and version?
bool initGL() {
    const GLubyte *brenderer = glGetString(GL_RENDERER);
    const GLubyte *version = glGetString(GL_VERSION);
    std::cout << "Renderer: " << brenderer << std::endl;
    std::cout << "Version: " << version << std::endl;

    checkGLState("opengl::initGL - start");

    glEnable(GL_BLEND); checkGLState("opengl::initGL - glEnable blend");
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); checkGLState("opengl::initGL - glBlendFunc");

    unsigned int clearColor = 0xCCCCCCCC;
    float r = (static_cast<float>((clearColor >> 24) & 0xFF)) / 255;
    float g = (static_cast<float>((clearColor >> 16) & 0xFF)) / 255;
    float b = (static_cast<float>((clearColor >>  8) & 0xFF)) / 255;
    float a = (static_cast<float>((clearColor >>  0) & 0xFF)) / 255;

    //glClearColor(0.8f, 0.8f, 0.8f, 0.8f);
    glClearColor(r, g, b, a); checkGLState("opengl::initGL - glClearColor");
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); checkGLState("opengl::initGL - glTexParameteri min");
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); checkGLState("opengl::initGL - glTexParameteri mag");

    //std::cout << "OpenGL is set up" << std::endl;

    return true;
}

bool initGLEW() {
    glewExperimental = GL_TRUE;
    const GLenum err = glewInit();
    if (err != GLEW_OK) {
        std::cout << "Could not initialize GLEW: " << glewGetErrorString(err) << std::endl;
        return false;
    }
    return true;
}

// only called once
bool OpenGL::initialize() {
    if (!glfwInit()) {
        std::cout << "Could not initialize GLFW" << std::endl;
        return false;
    }

    cursorHand = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
    cursorArrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
    cursorIbeam = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
    glfwSetErrorCallback([](int error, const char* description) {
        std::cout << "glfw error [" << error << "]:" << description << std::endl;
    });

    // I don't think you can init GLEW without a window
    return true;
}

OpenGLWindowHandle * OpenGL::createWindow(std::string title, Rect *position, unsigned int flags) {
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    int windowWidth = position->w;
    int windowHeight = position->h;
    OpenGLWindowHandle *twin = new OpenGLWindowHandle();
    twin->width = position->w;
    twin->height = position->h;
    GLFWwindow *window = glfwCreateWindow(windowWidth, windowHeight, title.c_str(), nullptr, nullptr);
    twin->window = window;

    // replace first parameter of all these callbacks with our window object instead of a GLFWwindow
    glfwSetWindowUserPointer(window, this);
    // probably should set up all the hooks here
    glfwMakeContextCurrent(window);

    // init GLEW
    if (!initGLEW()) {
        // FIXME: un do glfw create window
	// can't delete without a non-virtual deconstrcutor
        //delete win;
        return nullptr;
    }
    // init shaders?
    // we only want the box (texture shader)
    // I don't think you can init GLEW without a window
    initGL();

    // load the shaders
    twin->textureShader = twin->shaderLoader.getShader(VertexShader("TextureShader.vert"),
                                                       FragmentShader("TextureShader.frag"));
    twin->fontShader = twin->shaderLoader.getShader(VertexShader("FontShader.vert"),
                                                    FragmentShader("FontShader.frag"));

    // configure opengl coordinates
    //std::cout << "position: " << position->w << "x" << position->h << std::endl;
    glViewport(0, 0, static_cast<GLsizei>(position->w), static_cast<GLsizei>(position->h)); checkGLState("OpenGL::createWindow - glViewport");

    // replace first parameter of all these callbacks with our window object instead of a GLFWwindow
    glfwSetWindowUserPointer(twin->window, twin);

    // set up event callbacks

    // window events
    glfwSetFramebufferSizeCallback(window, [](GLFWwindow *glfwwin, int width, int height) {
        glViewport(0, 0, width, height);
    });
    glfwSetWindowSizeCallback(window, [](GLFWwindow *glfwwin, int width, int height) {
        OpenGLWindowHandle *thiz = reinterpret_cast<OpenGLWindowHandle*>(glfwGetWindowUserPointer(glfwwin));
        if (thiz->onResize) {
            thiz->onResize(width, height);
        }
    });

    // mouse events
    glfwSetCursorPosCallback(window, [](GLFWwindow *glfwwin, double xPos, double yPos) {
        OpenGLWindowHandle *thiz = reinterpret_cast<OpenGLWindowHandle*>(glfwGetWindowUserPointer(glfwwin));
        //std::cout << "Window::Window:onMousemove - at " << static_cast<int>(xPos) << "," << static_cast<int>(yPos) << std::endl;
        thiz->cursorX = xPos;
        thiz->cursorY = yPos;
        if (xPos < 0 || yPos < 0) return;
        if (xPos > thiz->width || yPos > thiz->height) return;
        // p. much worthless on double
        /*
         static double lx = 0;
         static double ly = 0;
         if (lx == xPos && ly == yPos) {
         return;
         }
         lx = xPos;
         ly = yPos;
         std::cout << "Window::Window:onMousemove - noCache" << std::endl;
         */
        //std::cout << "Window::Window:onMousemove - onWheel" << std::endl;
        if (thiz->onMouseMove) {
            thiz->onMouseMove(xPos, yPos);
        }
    });
    glfwSetScrollCallback(window, [](GLFWwindow *glfwwin, double xOffset, double yOffset) {
        OpenGLWindowHandle *thiz = reinterpret_cast<OpenGLWindowHandle*>(glfwGetWindowUserPointer(glfwwin));
        //std::cout << "glfwSetScrollCallback " << yOffset << " int" << (int)(yOffset * 10) << std::endl;
        if (thiz->onWheel) {
            thiz->onWheel(xOffset * 10, yOffset * 10);
        }
    });
    glfwSetMouseButtonCallback(window, [](GLFWwindow *glfwwin, int button, int action, int mods) {
        OpenGLWindowHandle *thiz = reinterpret_cast<OpenGLWindowHandle*>(glfwGetWindowUserPointer(glfwwin));
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
            //std::cout << "left press" << std::endl;
            if (thiz->onMouseUp) {
                thiz->onMouseDown(thiz->cursorX, thiz->cursorY);
            }
        }
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {
            //std::cout << "left release" << std::endl;
            if (thiz->onMouseUp) {
                thiz->onMouseUp(thiz->cursorX, thiz->cursorY);
            }
        }
    });

    // keyboard events

    // works with utf-32 and os keyboard layout but we'll lkeep the low level for now
    glfwSetCharCallback(window, [](GLFWwindow* glfwwin, unsigned int codepoint) {
        OpenGLWindowHandle *thiz = reinterpret_cast<OpenGLWindowHandle*>(glfwGetWindowUserPointer(glfwwin));
        //std::cout << "OpenGL::glfwSetCharCallback - codepoint: " << codepoint << std::endl;

        // should be used for inputComponent
        if (thiz->onKeyPress) {
            thiz->onKeyPress(codepoint);
        }
    });

    glfwSetKeyCallback(window, [](GLFWwindow *glfwwin, int key, int scancode, int action, int mods) {
        OpenGLWindowHandle *thiz = reinterpret_cast<OpenGLWindowHandle*>(glfwGetWindowUserPointer(glfwwin));
        if (action == GLFW_RELEASE) {
            if (thiz->onKeyUp) {
                thiz->onKeyUp(key, scancode, mods);
            }
        }
        if (action == GLFW_REPEAT) {
            //thiz->ui->onKeyRepeat(key, scancode, mods);
            //thiz->ui->onKeyPress(key, scancode, action, mods);
            if (thiz->onKeyRepeat) {
                thiz->onKeyRepeat(key, scancode, mods);
            }
        }
        if (action == GLFW_PRESS) {
            //thiz->ui->onKeyDown(key, scancode, mods);
            //thiz->ui->onKeyPress(key, scancode, action, mods);
            if (thiz->onKeyRepeat) {
                thiz->onKeyRepeat(key, scancode, mods);
            }
        }
    });

    /*
    // Change to the projection matrix, reset the matrix and set up orthagonal projection (i.e. 2D)
    glMatrixMode(GL_PROJECTION);
    glErr=glGetError();
    if(glErr != GL_NO_ERROR) {
        std::cout << "OpenGL::createWindow - glMatrixMode - not ok: " << gluErrorString(glErr) << std::endl;
    }
    glLoadIdentity();
    glErr=glGetError();
    if(glErr != GL_NO_ERROR) {
        std::cout << "OpenGL::createWindow - glLoadIdentity - not ok: " << gluErrorString(glErr) << std::endl;
    }
    glOrtho(0, position->w, position->h, 0, 0, 1); // Paramters: left, right, bottom, top, near, far
    glErr=glGetError();
    if(glErr != GL_NO_ERROR) {
        std::cout << "OpenGL::createWindow - glOrtho - not ok: " << gluErrorString(glErr) << std::endl;
    }
    */

    // set up box shader
    GLuint elementBufferObject = 0;

    glGenVertexArrays(1, &twin->vertexArrayObjectBox); checkGLState("OpenGL::createWindow - genVAOBox");
    glBindVertexArray(twin->vertexArrayObjectBox); checkGLState("OpenGL::createWindow - bindVAOBox");
    glGenBuffers(1, &elementBufferObject); checkGLState("OpenGL::createWindow - genEBO");

    // we may need the VBO here...
    glGenBuffers(1, &twin->vertexBufferObjectBox); checkGLState("OpenGL::createWindow - genVBOBox");
    float vertices[20] = {
        0.0f, 0.0f, 0.0f,    0.0f, 1.0f,
        0.0f, 0.0f, 0.0f,    1.0f, 1.0f,
        0.0f, 0.0f, 0.0f,    1.0f, 0.0f,
        0.0f, 0.0f, 0.0f,    0.0f, 0.0f
    };

    // select buffer
    glBindBuffer(GL_ARRAY_BUFFER, twin->vertexBufferObjectBox); checkGLState("OpenGL::createWindow - bindVBO");
    // write buffer
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); checkGLState("OpenGL::createWindow - bufVBO");
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferObject); checkGLState("OpenGL::createWindow - bindEBO");
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(this->indices), this->indices, GL_STATIC_DRAW); checkGLState("OpenGL::createWindow - buffEBO");

    // attribute 0 = 3 floats
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), nullptr); checkGLState("OpenGL::createWindow - glVertexAttribPointer0");
    glEnableVertexAttribArray(0); checkGLState("OpenGL::createWindow - glEnableVertexAttribArray0");

    // attribute 1 = 2 floats, at 3
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(3 * sizeof(float))); checkGLState("OpenGL::createWindow - glVertexAttribPointer1");
    glEnableVertexAttribArray(1); checkGLState("OpenGL::createWindow - glEnableVertexAttribArray1");

    //glBindVertexArray(0);

    // set up text shader
    glGenVertexArrays(1, &twin->vertexArrayObjectText); checkGLState("OpenGL::createWindow - genVAOText");
    glBindVertexArray(twin->vertexArrayObjectText); checkGLState("OpenGL::createWindow - bindVBOText");

    // vBO
    glGenBuffers(1, &twin->vertexBufferObjectText); checkGLState("OpenGL::createWindow - genVBOText");
    glBindBuffer(GL_ARRAY_BUFFER, twin->vertexBufferObjectText); checkGLState("OpenGL::createWindow - bindVBOText");
    std::unique_ptr<float[]> vertices2 = std::make_unique<float[]>(36); // upload garbage for now
    glBufferData(GL_ARRAY_BUFFER, ((3 + 4 + 2) * 4) * sizeof(float), vertices2.get(), GL_STATIC_DRAW); checkGLState("OpenGL::createWindow - buffVBOText");

    // eBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferObject); checkGLState("OpenGL::createWindow - bindVBOText");
    //glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, (3 + 4 + 2) * sizeof(float), nullptr); checkGLState("OpenGL::createWindow - glVertexAttribPointerText0");
    glEnableVertexAttribArray(0); checkGLState("OpenGL::createWindow - glEnableVertexAttribArrayText0");

    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, (3 + 4 + 2) * sizeof(float), reinterpret_cast<void*>(3 * sizeof(float))); checkGLState("opengl::createWindow - glVertexAttribPointerText1");
    glEnableVertexAttribArray(1); checkGLState("OpenGL::createWindow - glEnableVertexAttribArrayText1");

    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, (3 + 4 + 2) * sizeof(float), reinterpret_cast<void*>(7 * sizeof(float))); checkGLState("opengl::createWindow - glVertexAttribPointerText2");
    glEnableVertexAttribArray(2); checkGLState("OpenGL::createWindow - glEnableVertexAttribArrayText2");

    glBindVertexArray(0);

    return twin;
}

void OpenGLWindowHandle::clear() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); checkGLState("OpenGLWindowHandle::clear");
}

void OpenGLWindowHandle::swapBuffers() {
    glfwSwapBuffers(this->window); checkGLState("OpenGLWindowHandle::swapBuffers");
}

OpenGLTexture* OpenGLWindowHandle::createSprite(unsigned char* texture, GLsizei w, GLsizei h) {
    OpenGLTexture *handle = new OpenGLTexture;
    glGenTextures(1, &handle->number);

    // make sure texture size is a power of two
    size_t potw = pow(2, ceil(log(w) / log(2)));
    size_t poth = pow(2, ceil(log(h) / log(2)));
    // FIXME: need to dynamically allocate for c++
    //unsigned char data[16384][16384][4];
    //unsigned char ***image = allocate_dynamic_bitmap(poth, potw, 4);
    unsigned char *data = reinterpret_cast<unsigned char *>(malloc(sizeof(unsigned char) * poth * potw * 4));
    //memset(data, 0, poth * potw * 4);
    //memset(image, 0, poth * potw * 4);
    // unflip texture
    unsigned int loadWidth = static_cast<unsigned int>(w);
    unsigned int loadHeight = static_cast<unsigned int>(h);
    for (unsigned int py = 0; py < loadHeight; py++) {
        for (unsigned int px = 0; px < loadWidth; px++) {
            for (unsigned int i = 0; i < 4; i++) {
                //data[1023 - py][px][i] = anime.pixel_data[((px * 4) + (py * 4 * 1024)) + i];
                size_t read = ((px * 4) + (py * 4 * loadWidth)) + i;
                unsigned char val = texture[read];
                size_t ypos = potw - 1 - py; // flip y
                size_t write = ((px * 4) + (ypos * 4 * potw)) + i;
                data[write] = val;
                //data[ypos][px][i] = val;
            }
        }
    }

    // upload to video card
    // select
    glBindTexture(GL_TEXTURE_2D, handle->number); checkGLState("OpenGLWindowHandle::createSprite - bind");
    // upload
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast<int>(potw), static_cast<int>(poth), 0, GL_RGBA, GL_UNSIGNED_BYTE, data); checkGLState("OpenGLWindowHandle::createSprite - upload");
    // process
    glGenerateMipmap(GL_TEXTURE_2D); checkGLState("OpenGLWindowHandle::createSprite - process");
    // unselect
    glBindTexture(GL_TEXTURE_2D, 0); checkGLState("OpenGLWindowHandle::createSprite - unselect");

    return handle;
}

OpenGLTexture* OpenGLWindowHandle::createTextSprite(unsigned char *texture, GLsizei w, GLsizei h, textureMap &textureMap) {
    OpenGLTexture *handle = new OpenGLTexture;
    glGenTextures(1, &handle->number); checkGLState("OpenGLWindowHandle::createTextureFromColor - glGenTextures");

    // upload
    glBindTexture(GL_TEXTURE_2D, handle->number); checkGLState("OpenGLWindowHandle::createTextureFromColor - bindTexture");
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, texture); checkGLState("OpenGLWindowHandle::createTextureFromColor - glTexImage2D");
    glGenerateMipmap(GL_TEXTURE_2D); checkGLState("OpenGLWindowHandle::createTextureFromColor - glGenerateMipmap");
    glBindTexture(GL_TEXTURE_2D, 0); checkGLState("OpenGLWindowHandle::createTextureFromColor - unBindTex");

    // I flipped this maybe due to coords...
    handle->s0 = textureMap.map[0];
    handle->t0 = textureMap.map[1];
    handle->s1 = textureMap.map[2];
    handle->t1 = textureMap.map[3];

    return handle;
}

OpenGLTexture* OpenGLWindowHandle::createSpriteFromColor(const unsigned int hexColor) {
    //std::cout << "createTextureFromColor: " << hexColor << std::endl;
    unsigned char texture[1][1][4];
    texture[0][0][0]=(hexColor >> 24) & 0xFF;
    texture[0][0][1]=(hexColor >> 16) & 0xFF;
    texture[0][0][2]=(hexColor >>  8) & 0xFF;
    texture[0][0][3]=(hexColor >>  0) & 0xFF;
    //std::cout << "opengl::createTextureFromColor R: " << (int)texture[0][0][0] << " G: " << (int)texture[0][0][1] << " B: " << (int)texture[0][0][2] << " A: " << (int)texture[0][0][3] << std::endl;
    // I can't figure out how to pass data to opengl::createTexture
    // so we'll just do this for now
    OpenGLTexture *handle=new OpenGLTexture;

    glGenTextures(1, &handle->number); checkGLState("OpenGLWindowHandle::createTextureFromColor - glGenTextures");
    glBindTexture(GL_TEXTURE_2D, handle->number); checkGLState("OpenGLWindowHandle::createTextureFromColor - glBindTexture");
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture); checkGLState("OpenGLWindowHandle::createTextureFromColor - glTexImage2D");
    glGenerateMipmap(GL_TEXTURE_2D); checkGLState("OpenGLWindowHandle::createTextureFromColor - glGenerateMipmap");
    glBindTexture(GL_TEXTURE_2D, 0); checkGLState("OpenGLWindowHandle::createTextureFromColor - unselect");

    return handle;
}

#include <cmath>

// converts 0-1 to screen
// but centered
void pointToViewport(float &rawX, float &rawY, size_t windowWidth, size_t windowHeight) {
    //std::cout << "Component::pointToViewport - notBoundToPage converting from " << static_cast<int>(rawX) << "," << static_cast<int>(rawY) << std::endl;
    //std::cout << "BoundToPage using " << screenWidth << "x" << screenHeight << std::endl;
    rawX = ((rawX / windowWidth) * 2) - 1;
    rawY = ((rawY / windowHeight) * 2) - 1;
    //std::cout << "Component::pointToViewport - BoundToPage using " << static_cast<int>(rawX) << "x" << static_cast<int>(rawY) << std::endl;
    //std::cout << "Component::pointToViewport - BoundToPage converted to " << rawX << "," << rawY << std::endl;
}

void pointToViewportText(float &rawX, float &rawY, size_t windowWidth, size_t windowHeight);
void pointToViewportText(float &rawX, float &rawY, size_t windowWidth, size_t windowHeight) {
    //std::cout << "notBoundToPage using " << screenWidth << "x" << screenHeight << std::endl;
    //std::cout << "Component::pointToViewport - notBoundToPage converting from " << static_cast<int>(rawX) << "," << static_cast<int>(rawY) << std::endl;
    if (rawX < 0) {
        rawX += windowWidth;
    }
    if (rawY < 0) {
        rawY += windowHeight;
    }
    if (rawX > 1) {
        rawX /= windowWidth;
    }
    if (rawY > 1) {
        rawY /= windowHeight;
    }
    rawX = (rawX * 2) - 1;
    rawY = (rawY * 2) - 1;
    //std::cout << "Component::pointToViewport - notBoundToPage converted to " << rawX << "," << rawY << std::endl;
}

// keeps 0-1 (and *2 to convert to screen)
// but also takes pixels (and converts to screen)
// anchors to upperleft
void distanceToViewport(float &rawX, float &rawY, size_t windowWidth, size_t windowHeight) {
    if (std::abs(rawX) > 1) {
        rawX /= windowWidth;
    }
    if (std::abs(rawY) > 1) {
        rawY /= windowHeight;
    }
    rawX *= 2;
    rawY *= 2;
}

void OpenGLWindowHandle::drawSpriteBox(OpenGLTexture *texture, Rect *position) {
    std::cout << "Window size: " << this->width << "," << this->height << std::endl;
    // figure out new vertices
    float vx = position->x;
    // 320, 320 is mid to top
    // 0, 320 to bottom to top
    //float vy = (int)position->h - (int)position->y - (int)this->height;
    float vy = static_cast<int>(position->h) - position->y;
    float vWidth = position->w;
    float vHeight = position->h;
    //std::cout << "placing box at " << (int)vx << "x" << (int)vy << " size: " << (int)vWidth << "x" << (int)vHeight << std::endl;
    pointToViewport(vx, vy, this->width, this->height);

    //std::cout << "vWidth before: " << (int)vWidth << std::endl;
    distanceToViewport(vWidth, vHeight, this->width, this->height);
    //std::cout << "vWidth after: " << (int)vWidth << std::endl;

    //std::cout << "placing box at GL " << (int)vx << "x" << (int)vy << " size: " << (int)(vWidth*10000) << "x" << (int)(vHeight*10000) << std::endl;
    float vertices[20] = {
        0.0f, 0.0f, 0.0f,    0.0f, 1.0f,
        0.0f, 0.0f, 0.0f,    1.0f, 1.0f,
        0.0f, 0.0f, 0.0f,    1.0f, 0.0f,
        0.0f, 0.0f, 0.0f,    0.0f, 0.0f
    };
    vertices[(0 * 5) + 0] = vx;
    vertices[(0 * 5) + 1] = vy + vHeight;
    vertices[(1 * 5) + 0] = vx + vWidth;
    vertices[(1 * 5) + 1] = vy + vHeight;
    vertices[(2 * 5) + 0] = vx + vWidth;
    vertices[(2 * 5) + 1] = vy;
    vertices[(3 * 5) + 0] = vx;
    vertices[(3 * 5) + 1] = vy;

    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjectBox); checkGLState("OpenGLWindowHandle::drawSpriteBox - unselect");
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); checkGLState("OpenGLWindowHandle::drawSpriteBox - glBufferData");
    float textureTransformMatrix[16] = {
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
    };
    textureShader->bind();
    GLint transformLocation = textureShader->uniform("transform");
    glUniformMatrix4fv(transformLocation, 1, GL_FALSE, textureTransformMatrix); checkGLState("OpenGLWindowHandle::drawSpriteBox - glUniformMatrix4fv");

    glBindVertexArray(vertexArrayObjectBox); checkGLState("OpenGLWindowHandle::drawSpriteBox - glBindVertexArray");
    glBindTexture(GL_TEXTURE_2D, texture->number); checkGLState("OpenGLWindowHandle::drawSpriteBox - glBindTexture");
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); checkGLState("OpenGLWindowHandle::drawSpriteBox - glDrawElements");
}

inline void setVerticesColor(std::unique_ptr<float[]> &vertices, int p, unsigned int color);

#define posMac(p) (p * (3 + 4 + 2)) // 3 positions + 4 color channels + 2 S&T (texture mapping)

inline void setVerticesColor(std::unique_ptr<float[]> &vertices, int p, unsigned int color) {
    vertices[static_cast<size_t>(posMac(p) + 2)] = 0.0f;
    vertices[static_cast<size_t>(posMac(p) + 3)] = (static_cast<float>((color >> 24) & 0xFF)) / 255;
    vertices[static_cast<size_t>(posMac(p) + 4)] = (static_cast<float>((color >> 16) & 0xFF)) / 255;
    vertices[static_cast<size_t>(posMac(p) + 5)] = (static_cast<float>((color >>  8) & 0xFF)) / 255;
    vertices[static_cast<size_t>(posMac(p) + 6)] = (static_cast<float>((color >>  0) & 0xFF)) / 255;
}

void OpenGLWindowHandle::drawSpriteText(OpenGLTexture *texture, unsigned int hexColor, Rect *position) {
    float vx0 = position->x;
    float vy0 = static_cast<int>(this->height) - position->y + position->h;
    float vx1 = position->x + position->w;
    float vy1 = static_cast<int>(this->height) - position->y;
    pointToViewportText(vx0, vy0, this->width, this->height);
    pointToViewportText(vx1, vy1, this->width, this->height);

    std::unique_ptr<float[]> vertices = std::make_unique<float[]>(36);
    vertices[posMac(0) + 0] = vx0;
    vertices[posMac(0) + 1] = vy0;
    setVerticesColor(vertices, 0, hexColor);
    vertices[posMac(0) + 7] = texture->s0;
    vertices[posMac(0) + 8] = texture->t0;

    vertices[posMac(1) + 0] = vx0;
    vertices[posMac(1) + 1] = vy1;
    setVerticesColor(vertices, 1, hexColor);
    vertices[posMac(1) + 7] = texture->s0;
    vertices[posMac(1) + 8] = texture->t1;

    vertices[posMac(2) + 0] = vx1;
    vertices[posMac(2) + 1] = vy1;
    setVerticesColor(vertices, 2, hexColor);
    vertices[posMac(2) + 7] = texture->s1;
    vertices[posMac(2) + 8] = texture->t1;

    vertices[posMac(3) + 0] = vx1;
    vertices[posMac(3) + 1] = vy0;
    setVerticesColor(vertices, 3, hexColor);
    vertices[posMac(3) + 7] = texture->s1;
    vertices[posMac(3) + 8] = texture->t0;

    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjectText); checkGLState("OpenGLWindowHandle::drawSpriteText - glBindBuffer");
    glBufferData(GL_ARRAY_BUFFER, ((3 + 4 + 2) * 4) * sizeof(float), vertices.get(), GL_STATIC_DRAW); checkGLState("OpenGLWindowHandle::drawSpriteText - glBufferData");

    float transformMatrix[16] = {
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
    };
    fontShader->bind();
    GLint transformLocation = fontShader->uniform("transform");
    glUniformMatrix4fv(transformLocation, 1, GL_FALSE, transformMatrix); checkGLState("OpenGLWindowHandle::drawSpriteText - glUniformMatrix4fv");

    // load vertices
    glBindVertexArray(vertexArrayObjectText); checkGLState("OpenGLWindowHandle::drawSpriteText - glBindVertexArray");
    // load texture
    glBindTexture(GL_TEXTURE_2D, texture->number); checkGLState("OpenGLWindowHandle::drawSpriteText - glBindTexture");
    // draw primitives using vertices and texture
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); checkGLState("OpenGLWindowHandle::drawSpriteText - glDrawElements");

}


code:
#ifndef OPENGL_H
#define OPENGL_H

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <memory> // for unique_ptr
#include <vector>
#include <string>
#include "ShaderLoader.h"
#include "../renderer.h"

struct quadVector {
    GLfloat vector[4][3];
};

class TextureAtlas {
public:
    GLuint texture;
    sizes w, h; // texture width/height
    sizes mx, my; // max x,y currently available
    std::vector<Rect*> used; // small struct to loop on vs. unused
    std::vector<Rect*> available; // have to generate it, might as well cache it
    bool smooth;
    unsigned char bpp; // 0-4

    ~TextureAtlas();
    void updateMaxAvailArea();
    void markAreaUsed(Rect *it);
    void rebuildAvailable();
};

class OpenGLTexture : public Sprite {
public:
    // future
    TextureAtlas *mp_atlas;
    quadVector m_texureVector;
    // temp for now
    GLuint number;
    GLfloat s0;
    GLfloat t0;
    GLfloat s1;
    GLfloat t1;
};

// unlike a texture more like a sprite
class OpenGLTexturedQuad {
public:
    bool fontShader;
    sizes width;
    sizes height;
    // vertices
    coordinates x0;
    coordinates y0;
    coordinates x1;
    coordinates y1;
    // texture map
    float s0;
    float t0;
    float s1;
    float t1;
    sizes textureWidth;
    sizes textureHeight;
    std::unique_ptr<unsigned char[]> textureData;
};

class opengl; // few declr

bool initGL();
bool initGLEW();

class OpenGLWindowHandle : public WindowHandle {
public:
    void clear();
    void swapBuffers();
    OpenGLTexture* createSprite(unsigned char* texture, GLsizei w, GLsizei h);
    OpenGLTexture* createTextSprite(unsigned char *texture, GLsizei w, GLsizei h, textureMap &textureMap);
    OpenGLTexture* createSpriteFromColor(const unsigned int hexColor);
    using WindowHandle::drawSpriteBox;
    void drawSpriteBox(OpenGLTexture *texture, Rect *position);
    using WindowHandle::drawSpriteText;
    void drawSpriteText(OpenGLTexture *texture, unsigned int hexColor, Rect *position);
    // property
    GLFWwindow *window;
    opengl *renderer;
    // our singular vao
    GLuint vertexArrayObjectBox = 0;
    GLuint vertexArrayObjectText = 0;
    // our singular vbo
    GLuint vertexBufferObjectBox = 0;
    GLuint vertexBufferObjectText = 0;
    ShaderLoader shaderLoader;
    Shader *textureShader;
    Shader *fontShader;
    // needed for mouse down/up events
    double cursorX = 0;
    double cursorY = 0;
};

// singleton, there should only be one of these in the system
class OpenGL : public BaseRenderer {
public:
    bool initialize();
    OpenGLWindowHandle *createWindow(std::string title, Rect *position, unsigned int flags);
    // need texture/sprite functions (for elements that are going to be used across windows)
    // maybe take in opengl_window_handle*, nullptr meaning all
    // properties
    const unsigned int indices[6] = {
        0, 1, 2,
        0, 2, 3
    };
    GLFWcursor* cursorHand;
    GLFWcursor* cursorArrow;
    GLFWcursor* cursorIbeam;
};

void pointToViewport(float&, float&, size_t, size_t);
void distanceToViewport(float&, float&, size_t, size_t);

#endif


code: #include "renderer.h"


code:
#ifndef RENDERER_H
#define RENDERER_H

#include <memory> // for unique_ptr
#include <vector>
#include <string>
#include <functional>

#ifdef FLOATCOORDS
typedef double coordinates;
typedef double sizes;
#else
typedef short coordinates;
typedef unsigned short sizes;
#endif

struct textureMap {
    // was GLfloat
    float map[4];
};

typedef struct{
    coordinates x, y;
    sizes w, h;
} Rect;

class Sprite {
};

class WindowHandle {
public:
    virtual void clear() { }
    virtual void swapBuffers() { }
    // can we const texture?
    virtual Sprite* createSprite(unsigned char *texture, int w, int h) { return nullptr; }
    virtual Sprite* createTextSprite(unsigned char *texture, int w, int h, textureMap &textureMap) { return nullptr; }
    virtual Sprite* createSpriteFromColor(const unsigned int hexColor) { return nullptr; }
    virtual void drawSpriteBox(Sprite *texture, Rect *position) {}
    virtual void drawSpriteText(Sprite *texture, unsigned int hexColor, Rect *position) {}
    //
    size_t width;
    size_t height;
    // window functions
    std::function<void(int w, int h)> onResize = nullptr;
    // mouse functions
    std::function<void(int x, int y)> onMouseDown = nullptr;
    std::function<void(int x, int y)> onMouseUp = nullptr;
    std::function<void(int x, int y)> onMouseMove = nullptr;
    std::function<void(int x, int y)> onWheel = nullptr;
    // keyboard functions
    std::function<void(int key, int scancode, int mods)> onKeyUp = nullptr;
    std::function<void(int key, int scancode, int mods)> onKeyRepeat = nullptr;
    std::function<void(int key, int scancode, int mods)> onKeyDown = nullptr;
    std::function<void(unsigned int codepoint)> onKeyPress = nullptr;
};

// maybe convert to interface?
class BaseRenderer {
public:
    virtual bool initialize() { return true; }
    virtual WindowHandle *createWindow(std::string title, Rect *position, unsigned int flags) { return nullptr; }
};

#endif


code:
#include "CommandLineParams.h"
#include "interfaces/components/TabbedComponent.h"
#include "interfaces/components/InputComponent.h"
#include "environment/Environment.h"
#include "Log.h"
#include "URL.h"
#include "WebResource.h"
#include "tlsf.h"
#include "CFGFileParser.h"
#include "browser.h"

#include "interfaces/graphical/renderers/glfw/opengl.h"
#include "parsers/images/netpbm/pnm.h"

#include <ctime>
#include <iostream>
#include <sys/stat.h>
#include <string.h>

#ifdef _WIN32
extern "C"{
    void init_heap();
}
#endif

#if defined(_WIN32) && !defined(_WIN64)
#define PLATFORM "i686-pc-winnt"
#endif
#ifdef _WIN64
#define PLATFORM "amd64-pc-winnt"
#endif

// global
// to remove this
// window uses this to make windows and get window count (can be just be passed in when creating a window)
// AElement uses it to getActiveDocumentComponent and navTo on that
//
std::unique_ptr<Browser> browser = nullptr;
std::unique_ptr<BaseRenderer> renderer = nullptr;

// forward declaration
bool isAbsolutePath(const std::string s);
bool fileExists(const std::string s);

bool isAbsolutePath(const std::string s) {
    return (s.length() > 0 && s[0] == '/');
}

bool fileExists(const std::string s) {
    struct stat buf;
    return stat(s.c_str(), &buf) != -1;
}

extern TextRasterizerCache *rasterizerCache;

int main(int argc, char *argv[]) {
    // show help msg when "--help" appears
    if (argc > 1) {
        // has to be set up for logging before first logging call
        initCLParams(argc, argv);
        if (strcmp(argv[1], "--help")==0) {
            std::cout << "./netrunner [http://host.tld/|/path/to/file.html] [-log <error|warning|notice|info|debug>]" << std::endl;
            return 1;
        }
    }
#ifdef _WIN32
    init_heap(); // the NT port requires it. We do it at startup now, to allow 2LSF to run at any time
#endif

    std::cout \
    << "#     #                 ######" << std::endl \
    << "##    #  ######   ##### #     #  #    #  #    #  #    #  ######  #####" << std::endl \
    << "# #   #  #          #   #     #  #    #  ##   #  ##   #  #       #    #" << std::endl \
    << "#  #  #  #####      #   ######   #    #  # #  #  # #  #  #####   #    #" << std::endl \
    << "#   # #  #          #   #   #    #    #  #  # #  #  # #  #       #####" << std::endl \
    << "#    ##  #          #   #    #   #    #  #   ##  #   ##  #       #   #" << std::endl \
    << "#     #  ######     #   #     #   ####   #    #  #    #  ######  #    #" << std::endl;

#if defined(VERSION) && defined(PLATFORM)
    std::cout << "/g/ntr - NetRunner build " << __DATE__ << ": rev-" << VERSION << " for " << PLATFORM << std::endl;
#else
    std::cout << "/g/ntr - NetRunner build " << __DATE__ << ": internal-dev non-Makefile build" << std::endl;
#endif

    Environment::init();
    CFGFileParser *parser = new CFGFileParser("res/netrunner.cfg");
    BrowserConfiguration *config = new BrowserConfiguration();
    if (!parser) {
        std::cout << "no parser!" << std::endl;
    }
    if (parser->ParseText()){
        parser->WriteConfig(*config);
    }
    delete parser;
    #ifdef DEBUG
    std::cout << "Global Settings contains, in no particular order:\n";
    for ( auto it = config->Settings.begin(); it != config->Settings.end(); ++it ){
        std::cout << it->first << ":" << it->second << std::endl;
    }
    std::cout << std::endl;
    #endif

    renderer = std::make_unique<OpenGL>();

	//Environment::init();

    renderer->initialize();

    /*
    Rect pos;
    pos.x=0;
    pos.y=0;
    pos.w=1024;
    pos.h=640;
    OpenGLWindowHandle *win = renderer.createWindow("NTR", &pos, 0);
    if (!win) {
        std::cout << "Couldn't create window" << std::endl;
        return 0;
    }

    OpenGLTexture *blue = win->createSpriteFromColor(0x0000FFFF);
    if (!blue) {
        std::cout << "Couldn't create blue" << std::endl;
        return 0;
    }
    Rect dPos = { 0, 0, 512, 320 };
     */

    /*
    const std::shared_ptr<TextRasterizer> textRasterizer = rasterizerCache->loadFont(12, false);
    rasterizationRequest request;
    request.text = "Hello World";
    request.startX = 512;
    request.availableWidth = 1024;
    request.maxTextureSize = 16384;
    request.noWrap = true;
    //std::cout << "rasterizing [" << text << "] @" << rawX << " availableWidth: " << availableWidth << " sourceStartX: " << rasterStartX << " noWrap: " << noWrap << std::endl;
    std::shared_ptr<rasterizationResponse> response = textRasterizer->rasterize(request);

    textureMap textureMap;
    textureMap.map[0] = response->s0;
    textureMap.map[1] = response->t0;
    textureMap.map[2] = response->s1;
    textureMap.map[3] = response->t1;

    OpenGLTexture *text = win->createTextSprite(response->textureData.get(), response->textureWidth, response->textureHeight, textureMap);
    Rect dPos3 = { 512, 320, 512, 320};
     */

    /*
    RGBAPNMObject *anime = readPPM("kon32_661.pam");
    if (!anime) {
        std::cout << "Couldn't load image" << std::endl;
        return 0;
    }
     opengl_texture_handle *red = win->createSprite(anime->m_Ptr, anime->width, anime->height);
     Rect dPos2 = { 512, 320, 512, 320};
    */

    /*
    win->clear();
    win->drawSpriteBox(blue, &dPos);
    win->drawSpriteText(text, 0x000000FF, &dPos3);
    //win->drawSpriteBox(red, &dPos2);
    win->swapBuffers();

    while(1) {
        glfwWaitEvents(); // block until something changes
    }

    return 0;
    */

    // we need to set up OGL before we can setDOM (because component can't be constructed (currently) without OGL)
    // but should be after CommandLineParams incase we need to change some type of window config
    browser = std::make_unique<Browser>();
    browser->addWindow();
    if (!browser->windows.front().get()->window) {
        return 1;
    }

    //std::cout << "argc " << argc << std::endl;
    if (argc > 1) {
        // this isn't going to work
        std::string rawUrl = getCLParamByIndex(1);
        if (rawUrl != "-log") {
            // if we do this here, shouldn't we do this in parseUri too?
            if (rawUrl.find("://") == rawUrl.npos) {
                // Path should always be absolute for file://
                if (isAbsolutePath(rawUrl)) {
                    rawUrl = "file://" + rawUrl;
                } else {
                    auto absolutePath = std::string(getenv("PWD")) + '/' + rawUrl;
                    if (fileExists(absolutePath)) {
                        rawUrl = "file://" + absolutePath;
                    } else {
                        // Default to http if the file wasn't found
                        rawUrl = "http://" + rawUrl;
                    }
                }
            }
            //logDebug() << "pre URL parse [" << url << "]" << std::endl;
            //window->currentURL = URL(rawUrl);
            logDebug() << "loading [" << rawUrl << "]" << std::endl;
            TabbedComponent *tabComponent = dynamic_cast<TabbedComponent*>(browser->windows.front().get()->tabComponent.get());
            if (tabComponent) {
                tabComponent->addTab("Start Tab, Loading...");
                tabComponent->selectTab(tabComponent->tabs.front());
                // render the loading label, before we block for loading
                browser->render();
            }
            /*
            if (!setWindowContent(window->currentURL)) {
                return 1;
            }
            */
            std::shared_ptr<DocumentComponent> docComponent = browser->getActiveDocumentComponent();
            if (docComponent) {
                // now tell it to navigate somewhere
                docComponent->navTo(rawUrl);
            }
        } else {
            // be nice if we could put http:// in the address bar
        }
    }

    browser->loop();
    return 0;
}


code:
// ©2008-2017 despair <despair@netrunner.cc>
#ifndef NETRUNNER_H
#define NETRUNNER_H
#include "Murmur3.h"
#include <string>
#include <unordered_map>
#include <utility>
#include <algorithm>
#include <limits>
#include <iostream>
#include <cstring>
#include "tlsf.h"

// let's try fast strings
namespace ntr{

    template <class T>
    class TLSFAlloc {
    public:
      typedef T value_type;
      typedef T* pointer;
      typedef const T* const_pointer;
      typedef T& reference;
      typedef const T& const_reference;
      typedef std::size_t size_type;
      typedef std::ptrdiff_t difference_type;

      template <class U>
      struct rebind {
        typedef TLSFAlloc<U> other;
      };

      pointer address (reference value) const {
        return &value;
      }
      const_pointer address (const_reference value) const {
        return &value;
      }

      // these are the constructors for same: had I not ported sbrk(2) to winnt,
      // the constructor would have generated a memory pool from above.
      TLSFAlloc() throw() {}
      TLSFAlloc(const TLSFAlloc&) throw() {}
      template <class U> TLSFAlloc(const TLSFAlloc<U>&) throw() {}
      ~TLSFAlloc() throw() {}

      // placement new
      static void* operator new (std::size_t size, void*) {
          // because this is a static, it can't access this->allocate
          // so I've jsut copied allocate here
  #ifdef DEBUG_TLSF_CPP
          // print message and allocate memory with 2LSF
          std::cerr << "allocate " << size << " element(s)" << " of size " << sizeof(T) << std::endl;
          pointer ret = static_cast<pointer>(tlsf_malloc(size * sizeof(T)));
          std::cerr << " allocated at: " << (void*)ret << std::endl;
  #else
          pointer ret = static_cast<pointer>(tlsf_malloc(size * sizeof(T)));
  #endif
          return ret;
      }

      // destroy elements of initialized storage p
      void destroy (pointer p) {
        p->~T();
      }

      // return maximum number of elements that can be allocated
      size_type max_size() const throw() {
        return std::numeric_limits<std::size_t>::max() / sizeof(T);
      }

      // allocate but don't initialize num elements of type T
      pointer allocate(size_type num, const void* = 0) {
  #ifdef DEBUG_TLSF_CPP
        // print message and allocate memory with 2LSF
        std::cerr << "allocate " << num << " element(s)" << " of size " << sizeof(T) << std::endl;
        pointer ret = (pointer)(tlsf_malloc(num*sizeof(T)));
        std::cerr << " allocated at: " << (void*)ret << std::endl; return ret;
  #else
        pointer ret = static_cast<pointer>(tlsf_malloc(num*sizeof(T)));
        return ret;
  #endif
      }
      // initialize elements of allocated storage p with value value
      // using placement new (defined here)
      void construct (pointer p, const T& value) {
        new (reinterpret_cast<void*>(p))T(value);
      }

      // deallocate storage p of deleted elements
      void deallocate(pointer p, size_type num) {
  #ifdef DEBUG_TLSF_CPP
         // print message and deallocate memory with global delete
         std::cerr << "deallocate " << num << " element(s)"  << " of size " << sizeof(T) << " at: " << (void*)p << std::endl;
  #endif
         tlsf_free(reinterpret_cast<void*>(p));
       }
    };

     // return that all specializations of this allocator are interchangeable
     template <class T1, class T2> bool operator== (const TLSFAlloc<T1>&,const TLSFAlloc<T2>&) throw() { return true; }
     template <class T1, class T2> bool operator!= (const TLSFAlloc<T1>&, const TLSFAlloc<T2>&) throw() { return false; }

     template <class CharT>
     using fast_basic_string = std::basic_string<CharT, std::char_traits<CharT>, TLSFAlloc<CharT>>;
     using fast_string = fast_basic_string<char>;

     // NT uses UTF-16
     using fast_hexstring = fast_basic_string<wchar_t>;

     // POSIX demands 32-bit Unicode
     using fast_u32string = fast_basic_string<char32_t>;

     // Depending on yer environment, these may or may not be meaningful
     using fast_octstring = fast_basic_string<unsigned char>;
     using fast_sepstring = fast_basic_string<signed char>;

    class StringHash{
    public:
        std::size_t operator() (const fast_string& s) const {
            size_t out = 0;
            // 32-bit hash only, all others overflow. RIP AMD64 users...
            MurmurHash3_x86_32 ( &s, sizeof(s), 7904542L, &out );
            return out;
        }
    };
    class StringComparison{
    public:
        bool operator ()(const fast_string &a, const fast_string &b) const {
            int i = strcmp(a.c_str(), b.c_str());
            if (!i){
                return true;
            }
            else{
                return false;
            }
        }
    };

    typedef std::unordered_map<fast_string,fast_string,StringHash,StringComparison,TLSFAlloc<std::pair<const fast_string, fast_string>>> stringmap;
}
#endif


code:
#ifndef HTTPCOMMON_H
#define HTTPCOMMON_H
enum class Version {
    HTTP10,
    HTTP11
};

enum class Method {
    GET,
    POST
};
#endif


code:
#include "HTTPRequest.h"
#include <errno.h>
#include <iostream>
#include <string.h>

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN 1
#include <winsock2.h>
#include <windows.h>
#include <WS2tcpip.h>
#include <wspiapi.h>
#define ssize_t intptr_t
#else
// unix includes here
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#endif

#ifdef WIN32
// Initialize Winsock
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif

HTTPRequest::HTTPRequest(const std::shared_ptr<URL> u) {
#ifdef WIN32
	if (iResult != 0) {
		std::cout << "WSAStartup failed: " << iResult << std::endl;
		return;
	}
#endif
    uri = u;
    version = Version::HTTP10;
    method = Method::GET;
    userAgent = "NetRunner";
}

bool HTTPRequest::sendRequest(std::function<void(const HTTPResponse&)> responseCallback, std::unique_ptr<std::string> ptrPostBody) const {
    struct addrinfo hints;
    struct addrinfo *serverInfo = nullptr;
    std::string host = uri->host;
    std::string document = uri->path;
    if (uri->query != "") {
        document += "?" + uri->query;
    }
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    const int res = getaddrinfo(uri->host.c_str(), std::to_string(uri->port).c_str(), &hints, &serverInfo);
    if (res != 0) {
        std::cout << "Could not lookup " << host << ": " << res << std::endl;
        freeaddrinfo(serverInfo);
        return false;
    }

    const int sock = socket(serverInfo->ai_family, serverInfo->ai_socktype, serverInfo->ai_protocol);
    if (sock == -1) {
        std::cout << "Could not create socket: " << errno << std::endl;
        freeaddrinfo(serverInfo);
        return false;
    }

    if (connect(sock, serverInfo->ai_addr, serverInfo->ai_addrlen) == -1) {
        std::cout << "Could not connect to: " << host << ":" << errno << std::endl;
        freeaddrinfo(serverInfo);
        return false;
    }
    std::string postBody = "";
    if (ptrPostBody) {
        // The moral of the story is, if you have binary (non-alphanumeric) data (or a significantly sized payload) to transmit, use multipart/form-data
        std::string fixedPostBody(*ptrPostBody);
        auto search = fixedPostBody.find(" ");
        if (search != std::string::npos) {
            fixedPostBody.replace(search, 1, "+");
        }
        // close userAgent
        postBody = "\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: " + std::to_string(fixedPostBody.size())+ "\r\n\r\n" + fixedPostBody;
    }

    const std::string request = methodToString(method) + std::string(" ") + document + std::string(" ") + versionToString(version) + std::string("\r\nHost: ") + host + std::string("\r\nUser-Agent: ") + userAgent + postBody + std::string("\r\n\r\n");
    std::cout << "HTTP Request: " << request << std::endl;
    const ssize_t sent = send(sock, request.c_str(), request.length(), 0);
    if (sent == -1) {
        std::cout << "Could not send \"" << request << "\": " << errno << std::endl;
        freeaddrinfo(serverInfo);
        return false;
    }

    std::string response;
    char buffer[512];
    ssize_t received;
    while ((received = recv(sock, buffer, sizeof(buffer), 0)) != 0) {
        response += std::string(buffer, static_cast<unsigned int>(received));
    }

    responseCallback(HTTPResponse(response));

    freeaddrinfo(serverInfo);
    return true;
}

const std::string HTTPRequest::versionToString(const Version ver) const {
    switch (ver) {
        case Version::HTTP10:
            return "HTTP/1.0";
        case Version::HTTP11:
            return "HTTP/1.1";
        default:
            return "ERROR";
    }
}

const std::string HTTPRequest::methodToString(const Method meth) const {
    switch (meth) {
        case Method::GET:
            return "GET";
        case Method::POST:
            return "POST";
        default:
            return "ERROR";
    }
}


code:
#ifndef HTTPREQUEST_H
#define HTTPREQUEST_H

#include "HTTPCommon.h"
#include "HTTPResponse.h"
#include "../URL.h"
#include <functional>
#include <string>

class HTTPRequest {
public:
    HTTPRequest(const std::shared_ptr<URL> u);
    bool sendRequest(std::function<void(const HTTPResponse&)> responseCallback, std::unique_ptr<std::string> ptrPostBody) const;
    const std::string versionToString(const Version version) const;
    const std::string methodToString(const Method method) const;
    Method method;
    Version version;
private:
    std::string userAgent;
    std::shared_ptr<URL> uri;
};

#endif


code:
#include "HTTPResponse.h"
#include <iostream>

HTTPResponse::HTTPResponse(const std::string &rawResponse) {
    int state = 0;
    unsigned int cursor = 0;
    unsigned int start = 0;
    std::string propertyKey;
    for (cursor = 0; cursor < rawResponse.length(); cursor++) {
        if (state % 2 == 0 && rawResponse[cursor] == ' ') {
            if (state == 0) {
                version = rawResponse.substr(0, cursor - start);
            }
            else {
                propertyKey = rawResponse.substr(start, cursor - start - 1);
            }
            start = cursor + 1;
            state++;
        }
        else if (state % 2 == 1 && rawResponse[cursor] == '\r') {
            if (state == 1) {
                status = rawResponse.substr(start, cursor - start);
            }
            else {
                properties.insert(std::pair<std::string, std::string>(propertyKey, rawResponse.substr(start, cursor - start)));
            }
            start = cursor + 2; // start after new line
            state++;
        }
        else if (state % 2 == 0 && rawResponse[cursor] == '\r') {
            cursor += 2; // skip over the rest of the new lines
            break;
        }
    }
    body = rawResponse.substr(cursor, rawResponse.length() - cursor - 1);
    //std::cout << "Body is [" << body << "]" << std::endl;
    statusCode = 0;
    if (status != "") { // if timeout?
        statusCode = stoi(status);
    }
    std::cout << "HTTPResponse::HTTPResponse - Status is " << status << std::endl;
}


code:
#ifndef HTTPRESPONSE_H
#define HTTPRESPONSE_H

#include <map>
#include <string>

class HTTPResponse {
public:
    HTTPResponse(const std::string &rawRequest);
    std::string version;
    std::string status;
    std::map<std::string, std::string> properties;
    std::string body;
    int statusCode;
private:
};

#endif