/* -*- mia-c++ -*-
 *
 * This file is part of viewitgui - a library and program for the
 * visualization of 3D data sets. 
 *
 * Copyright (c) Leipzig, Madrid 1999-2013 Mirco Hellmann, Gert Wollny
 *
 * viewitgui is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * viewitgui 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with viewitgui; if not, see <http://www.gnu.org/licenses/>.
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <iostream>
#include <viewit/snapshot.hh>
#include <mia/2d/rgbimageio.hh>
#include <GL/gl.h>
#include <mia.hh>
#include <queue>
//#include <atomic>
#include <tbb/atomic.h>

using namespace std; 
using namespace mia; 




class CSnapshotQueue  {
public: 
	CSnapshotQueue(); 
	
	void push(CRGB2DImage *foto);
	std::shared_ptr<CRGB2DImage> top_and_pop(); 
	bool empty(); 
private: 
	queue<std::shared_ptr<CRGB2DImage> > m_queue; 
	boost::mutex m_mutex; 
	boost::condition_variable m_cond;
};

CSnapshotQueue::CSnapshotQueue()
{
}

void CSnapshotQueue::push(CRGB2DImage *foto)
{
	boost::unique_lock<boost::mutex> lock(m_mutex); 
	const bool was_empty=m_queue.empty();
	m_queue.push(std::shared_ptr<CRGB2DImage>(foto)); 
	lock.unlock(); 
	if (was_empty) {
		cvdebug() << "notify saver thread\n"; 
		m_cond.notify_one();
	}
}

std::shared_ptr<CRGB2DImage> CSnapshotQueue::top_and_pop()
{
	boost::unique_lock<boost::mutex> lock(m_mutex); 
	assert(!m_queue.empty()); 
	auto retval = m_queue.front(); 
	m_queue.pop(); 
	return retval; 

}

bool CSnapshotQueue::empty()
{
	// if the queue is empty, then we wait for some time 
	// we do not completely block the thread in order to enable it 
	// to cleanly exit 
	boost::unique_lock<boost::mutex> lock(m_mutex);
	const boost::posix_time::time_duration dt = boost::posix_time::microseconds(100); 
	if (m_queue.empty()) 
		m_cond.timed_wait(lock, dt);
	return m_queue.empty(); 
}

class CSnapshotServer  {
public: 	
	CSnapshotServer(CSnapshotQueue& queue); 

	void stop(); 
	void operator() (); 
private: 
	CSnapshotQueue& m_queue; 
	size_t m_id; 

	// this should be atomic
	tbb::atomic<bool> m_run; 
};

CSnapshotServer::CSnapshotServer(CSnapshotQueue& queue):
	m_queue(queue), 
	m_id(0)
{
        m_run = true; 
}

void CSnapshotServer::stop()
{
	cvdebug() << "stop snapshot server\n"; 
	m_run = false; 
}

void CSnapshotServer::operator() ()
{

	CThreadMsgStream thread_stream;
	cvdebug() << "start snapshot server\n"; 
	while (m_run) {
		if (m_queue.empty()) 
			continue; 
		auto image = m_queue.top_and_pop(); 
		assert(image); 
		stringstream fname ; 

		fname << "viewit-screenshot-" << m_id++ << ".png"; 
		cvdebug() << "save snapshot to " << fname.str() << "\n"; 
		save_image(fname.str(), *image);
	}
	cvdebug() << "Exit snapshot server\n"; 
}

CSnapshotClient::CSnapshotClient():
	m_queue(new CSnapshotQueue), 
	m_server(new CSnapshotServer(*m_queue)),
	m_server_thread(boost::ref(*m_server))
{
}

CSnapshotClient::~CSnapshotClient()
{
	m_server->stop(); 
	m_server_thread.join(); 
}
	
void CSnapshotClient::foto(const C2DBounds& viewport)
{ 
	cvdebug() << "Take picture of size " << viewport << "\n"; 
	
	vector<unsigned char> buffer(viewport.x * viewport.y * 3); 
        glFlush();
	
        glReadBuffer(GL_BACK);
        glPixelStorei(GL_PACK_ALIGNMENT,1);
        glReadPixels(0,0,viewport.x,viewport.y,GL_RGB,GL_UNSIGNED_BYTE, &buffer[0]);
	// flip along y
	
	auto image = new CRGB2DImage(viewport);
	auto b = buffer.begin(); 
	
	const int stride = viewport.x * 3; 
	auto p = image->pixel() + (viewport.y -1) * stride; 

	for (auto y = 0u; y < viewport.y; ++y, b += stride, p -= stride)
		copy(b, b+stride, p); 

        m_queue->push(image);
}
