/*
 * CINELERRA
 * Copyright (C) 2016 Adam Williams <broadcast at earthling dot net>
 * 
 * This program 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 2 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 */



// store previously scaled images here for operations 
// with multiple motion scans on the same frame.


#include "bcsignals.h"
#include "clip.h"
#include "motioncache.h"
#include "mutex.h"
#include <stdint.h>
#include "vframe.h"


MotionCacheItem::MotionCacheItem()
{
}

MotionCacheItem::~MotionCacheItem()
{
//printf("MotionCacheItem::~MotionCacheItem %d image=%p\n", __LINE__, image);
	if(image)
	{
		delete image;
	}
}



MotionCache::MotionCache()
{
	lock = new Mutex("MotionCache::lock");
}

MotionCache::~MotionCache()
{
//printf("MotionCache::~MotionCache %d this=%p\n", __LINE__, this);

	delete lock;
	clear();
}

void MotionCache::clear()
{
	images.remove_all_objects();
}



#define DOWNSAMPLE(type, temp_type, components, max) \
{ \
	temp_type r; \
	temp_type g; \
	temp_type b; \
	temp_type a; \
	type **in_rows = (type**)src->get_rows(); \
	type **out_rows = (type**)dst->get_rows(); \
 \
	for(int i = 0; i < h; i += downsample) \
	{ \
		int y1 = MAX(i, 0); \
		int y2 = MIN(i + downsample, h); \
 \
 \
		for(int j = 0; \
			j < w; \
			j += downsample) \
		{ \
			int x1 = MAX(j, 0); \
			int x2 = MIN(j + downsample, w); \
 \
			temp_type scale = (x2 - x1) * (y2 - y1); \
			if(x2 > x1 && y2 > y1) \
			{ \
 \
/* Read in values */ \
				r = 0; \
				g = 0; \
				b = 0; \
				if(components == 4) a = 0; \
 \
				for(int k = y1; k < y2; k++) \
				{ \
					type *row = in_rows[k] + x1 * components; \
					for(int l = x1; l < x2; l++) \
					{ \
						r += *row++; \
						g += *row++; \
						b += *row++; \
						if(components == 4) a += *row++; \
					} \
				} \
 \
/* Write average */ \
				r /= scale; \
				g /= scale; \
				b /= scale; \
				if(components == 4) a /= scale; \
 \
				type *row = out_rows[y1 / downsample] + \
					x1 / downsample * components; \
				*row++ = r; \
				*row++ = g; \
				*row++ = b; \
				if(components == 4) *row++ = a; \
			} \
		} \
/*printf("DOWNSAMPLE 3 %d\n", i);*/ \
	} \
}




void MotionCache::downsample_frame(VFrame *dst, 
	VFrame *src, 
	int downsample)
{
	int h = src->get_h();
	int w = src->get_w();

//PRINT_TRACE
//printf("downsample=%d w=%d h=%d dst=%d %d\n", downsample, w, h, dst->get_w(), dst->get_h());
	switch(src->get_color_model())
	{
		case BC_RGB888:
			DOWNSAMPLE(uint8_t, int64_t, 3, 0xff)
			break;
		case BC_RGB_FLOAT:
			DOWNSAMPLE(float, float, 3, 1.0)
			break;
		case BC_RGBA8888:
			DOWNSAMPLE(uint8_t, int64_t, 4, 0xff)
			break;
		case BC_RGBA_FLOAT:
			DOWNSAMPLE(float, float, 4, 1.0)
			break;
		case BC_YUV888:
			DOWNSAMPLE(uint8_t, int64_t, 3, 0xff)
			break;
		case BC_YUVA8888:
			DOWNSAMPLE(uint8_t, int64_t, 4, 0xff)
			break;
	}
//PRINT_TRACE
}

VFrame* MotionCache::get_image(int ratio, 
	int is_previous,
	int downsampled_w,
	int downsampled_h,
	VFrame *src)
{
	lock->lock("MotionCache::get_image 1");
	
	for(int i = 0; i < images.size(); i++)
	{
		if(images.get(i)->ratio == ratio &&
			images.get(i)->is_previous == is_previous)
		{
			VFrame *result = images.get(i)->image;
			lock->unlock();
			return result;
		}
	}


//PRINT_TRACE
	VFrame *result = new VFrame();
	result->set_use_shm(0);
	result->reallocate(0, 
		-1,
		0,
		0,
		0,
		downsampled_w + 1, 
		downsampled_h + 1, 
		src->get_color_model(), 
		-1);
	downsample_frame(result, 
		src, 
		ratio);
// printf("MotionCache::get_image %d ptr=%p w=%d h=%d\n", 
// __LINE__, 
// result->get_rows()[0],
// result->get_w(),
// result->get_h());

	MotionCacheItem *item = new MotionCacheItem();
	item->image = result;
	item->is_previous = is_previous;
	item->ratio = ratio;
	images.append(item);

	lock->unlock();
	
	return result;
}



















