/*
    QGLView
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "glframebuffer.h"


GLFramebuffer::GLFramebuffer(int colorAttachments_, bool withDepth, GLenum colorFormat_, GLenum target__) {
	is_depth     = withDepth;
	color_format = colorFormat_;
	target_      = target__;
	colors.fill(0, colorAttachments_);
	fbo = drbo = 0;
	tex_d      = 0;
	wid = hei  = 0;
	is_changed = false;
}


GLFramebuffer::~GLFramebuffer() {
	deleteGLFramebuffer(fbo);
	deleteGLRenderbuffer(drbo);
	for (int i = 0; i < colors.size(); ++i)
		deleteGLTexture(colors[i]);
	deleteGLTexture(tex_d);
}


void GLFramebuffer::resize(int width, int height, bool force) {
	if ((wid == width) && (hei == height) && !force) return;
	initializeOpenGLFunctions();
	wid = width;
	hei = height;
	deleteGLFramebuffer(fbo);
	glGenFramebuffers(1, &fbo);
	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
	for (int i = 0; i < colors.size(); ++i) {
		deleteGLTexture(colors[i]);
		createGLTexture(colors[i], width, height, color_format, target_);
		glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, 4);
		glTexParameteri(target_, GL_GENERATE_MIPMAP_SGIS, GL_FALSE);
		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, target_, colors[i], 0);
	}
	if (is_depth) {
		deleteGLTexture(tex_d);
		deleteGLRenderbuffer(drbo);
		glGenRenderbuffers(1, &drbo);
		glBindRenderbuffer(GL_RENDERBUFFER, drbo);
		glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, drbo);
		createGLTexture(tex_d, width, height, GL_DEPTH_COMPONENT);
		glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexParameteri(target_, GL_GENERATE_MIPMAP_SGIS, GL_FALSE);
		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, target_, tex_d, 0);
	}
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	is_changed = false;
}


QImage GLFramebuffer::grab() const {
	// glReadPixels(0, 0, wid, hei, GL_RGBA, );
	// QImage ret();
	return QImage();
}


void GLFramebuffer::bind() {
	if (is_changed) resize(wid, hei);
	if (fbo == 0) return;
	initializeOpenGLFunctions();
	// glFlush();
	glGetIntegerv(GL_VIEWPORT, prev_view);
	// glClearError();
	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
	// qDebug() << QString::number(glGetError(), 16);
	QVector<GLenum> buffers;
	for (int i = 0; i < colors.size(); ++i)
		buffers << GL_COLOR_ATTACHMENT0 + i;
	glDrawBuffers(buffers.size(), buffers.constData());
	glReadBuffer(GL_COLOR_ATTACHMENT0);
	// glDrawBuffer(GL_COLOR_ATTACHMENT0);
	glViewport(0, 0, wid, hei);
}


void GLFramebuffer::release() {
	is_changed = false;
	if (fbo == 0) return;
	// glFlush();
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	glViewport(prev_view[0], prev_view[1], prev_view[2], prev_view[3]);
}


void GLFramebuffer::setWriteBuffer(int index) {
	// QVector<GLenum> buffers; buffers << GL_COLOR_ATTACHMENT0 + index;
	GLenum e = GL_COLOR_ATTACHMENT0 + index;
	glDrawBuffer(e);
	// glDrawBuffers(1, &e);
}


void GLFramebuffer::setWriteBuffers(int * indeces, int count) {
	QVector<GLenum> buffers;
	for (int i = 0; i < count; ++i)
		buffers << GL_COLOR_ATTACHMENT0 + indeces[i];
	glDrawBuffers(buffers.size(), buffers.constData());
}


void GLFramebuffer::bindColorTextures() {
	for (int i = colors.size() - 1; i >= 0; --i) {
		glActiveTexture(GL_TEXTURE0 + i);
		glBindTexture(GL_TEXTURE_2D, colors[i]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	}
}


void GLFramebuffer::bindDepthTexture(int channel) {
	glActiveTexture(GL_TEXTURE0 + channel);
	glBindTexture(GL_TEXTURE_2D, tex_d);
}
