//This is based on the code in lsnes' cscd.cpp file
//A few modifications were done to improve compression speed
#include "stdafx.h"
#include <cstring>
#include "CamstudioCodec.h"
#include "miniz.h"

CamstudioCodec::~CamstudioCodec()
{
	if(_prevFrame) {
		delete[] _prevFrame;
		_prevFrame = nullptr;
	}

	if(_currentFrame) {
		delete[] _currentFrame;
		_currentFrame = nullptr;
	}

	if(_buffer) {
		delete[] _buffer;
		_buffer = nullptr;
	}

	if(_compressBuffer) {
		delete[] _compressBuffer;
		_compressBuffer = nullptr;
	}

	deflateEnd(&_compressor);
}

bool CamstudioCodec::SetupCompress(int width, int height, uint32_t compressionLevel)
{
	_compressionLevel = compressionLevel;
	_orgWidth = width;

	if(width % 4 != 0) {
		_rowStride = ((int)((width * 24 + 31) / 32 * 4));
	} else {
		_rowStride = width*3;
	}
	_height = height;

	_prevFrame = new uint8_t[_rowStride*_height]; //24-bit RGB
	_currentFrame = new uint8_t[_rowStride*_height]; //24-bit RGB
	_buffer = new uint8_t[_rowStride*_height]; //24-bit RGB

	_compressBufferLength = compressBound(_rowStride*_height) + 2;
	_compressBuffer = new uint8_t[_compressBufferLength];
	
	memset(_prevFrame, 0, _rowStride * _height);
	memset(_currentFrame, 0, _rowStride * _height);
	memset(_buffer, 0, _rowStride * _height);
	memset(_compressBuffer, 0, _compressBufferLength);
	
	deflateInit(&_compressor, compressionLevel);

	return true;
}

void CamstudioCodec::LoadRow(uint8_t* inPointer, uint8_t* outPointer)
{
	for(int x = 0; x < _orgWidth; x++) {
		outPointer[0] = inPointer[0];
		outPointer[1] = inPointer[1];
		outPointer[2] = inPointer[2];
		outPointer += 3;
		inPointer += 4;
	}
}

int CamstudioCodec::CompressFrame(bool isKeyFrame, uint8_t *frameData, uint8_t** compressedData)
{
	deflateReset(&_compressor);

	_compressor.next_out = _compressBuffer + 2;
	_compressor.avail_out = _compressBufferLength - 2;

	_compressBuffer[0] = (isKeyFrame ? 0x03 : 0x02) | (_compressionLevel << 4);
	_compressBuffer[1] = 8; //8-bit per color

	uint8_t* rowBuffer = _currentFrame;
	for(int y = 0; y < _height; y++) {
		LoadRow(frameData + (_height - y - 1) * _orgWidth * 4, rowBuffer);
		rowBuffer += _rowStride;
	}

	if(isKeyFrame) {
		_compressor.next_in = _currentFrame;
	} else {
		for(int i = 0, len = _rowStride * _height; i < len; i++) {
			_buffer[i] = _currentFrame[i] - _prevFrame[i];
		}
		_compressor.next_in = _buffer;
	}

	memcpy(_prevFrame, _currentFrame, _rowStride*_height);
	
	_compressor.avail_in = _height * _rowStride;
	deflate(&_compressor, MZ_FINISH);
	
	*compressedData = _compressBuffer;
	return _compressor.total_out + 2;
}

const char* CamstudioCodec::GetFourCC()
{
	return "CSCD";
}