
/*
 * CINELERRA
 * Copyright (C) 2008 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
 *
 */

#include "automation.inc"
#include "clip.h"
#include "edl.h"
#include "edlsession.h"
#include "localsession.h"
#include "maskauto.h"
#include "maskautos.h"
#include "track.h"
#include "transportque.inc"

MaskAutos::MaskAutos(EDL *edl,
	Track *track)
 : Autos(edl, track)
{
	type = AUTOMATION_TYPE_MASK;
}

MaskAutos::~MaskAutos()
{
}




void MaskAutos::update_parameter(MaskAuto *src)
{
	double selection_start = edl->local_session->get_selectionstart(0);
	double selection_end = edl->local_session->get_selectionend(0);

// Selection is always aligned to frame for masks

// Create new keyframe if auto keyframes or replace entire keyframe.
	if( !edl->session->span_keyframes ||
	    EQUIV(selection_start, selection_end) )
	{
// Search for keyframe to write to
		MaskAuto *dst = (MaskAuto*)get_auto_for_editing();

		dst->copy_data(src);
	}
	else
// Replace changed parameter in all selected keyframes.
	{
// Search all keyframes in selection but don't create a new one.
		int64_t start = track->to_units(selection_start, 0);
		int64_t end = track->to_units(selection_end, 0);
		Auto *current_auto = 0;
		MaskAuto *current = 0;
		current = (MaskAuto*)get_prev_auto(start,
			PLAY_FORWARD,
			current_auto,
			1);

// The first one determines the changed parameters since it is the one displayed
// in the GUI.
		MaskAuto *first = current;

// Update the first one last, so it is available for comparisons to the changed one.
		for(current = (MaskAuto*)NEXT;
			current && current->position < end;
			current = (MaskAuto*)NEXT)
		{
			current->update_parameter(first,
				src);
		}
		first->copy_data(src);
	}
}



void MaskAutos::get_points(MaskPoints *points,
	int submask,
	int64_t position,
	int direction)
{
	MaskAuto *begin = 0, *end = 0;
	position = (direction == PLAY_FORWARD) ? position : (position - 1);

// Get auto before and after position
	for(MaskAuto* current = (MaskAuto*)last;
		current;
		current = (MaskAuto*)PREVIOUS)
	{
		if(current->position <= position)
		{
			begin = current;
			end = NEXT ? (MaskAuto*)NEXT : current;
			break;
		}
	}

// Nothing before position found
	if(!begin)
	{
		begin = end = (MaskAuto*)first;
	}

// Nothing after position found
	if(!begin)
	{
		begin = end = (MaskAuto*)default_auto;
	}


	SubMask *mask1 = begin->get_submask(submask);
	SubMask *mask2 = end->get_submask(submask);

	points->remove_all_objects();
	int total_points = MAX(mask1->points.size(), mask2->points.size());
	for(int i = 0; i < total_points; i++)
	{
		MaskPoint *point = new MaskPoint;
		MaskPoint point1;
		int need_point1 = 1;
		MaskPoint point2;
		int need_point2 = 1;

		if(i < mask1->points.size())
		{
			point1.copy_from(*mask1->points.get(i));
			need_point1 = 0;
		}

		if(i < mask2->points.size())
		{
			point2.copy_from(*mask2->points.get(i));
			need_point2 = 0;
		}

		if(need_point1) point1.copy_from(point2);
		if(need_point2) point2.copy_from(point1);

		avg_points(point,
			&point1,
			&point2,
			position,
			begin->position,
			end->position);
		points->append(point);
	}
}


double MaskAutos::get_feather(int64_t position, int i, int direction)
{
	Auto *begin = 0, *end = 0;
	position = (direction == PLAY_FORWARD) ? position : (position - 1);
	MaskAuto*prev = (MaskAuto*)get_prev_auto(position, PLAY_FORWARD, begin, 1);
	MaskAuto*next = (MaskAuto*)get_next_auto(position, PLAY_FORWARD, end, 1);
	SubMask *prev_mask = prev->get_submask(i), *next_mask = next->get_submask(i);

	double weight = end->position == begin->position ? 0.0 :
		(double)(position - begin->position) / (end->position - begin->position);

	double result = prev_mask->feather * (1-weight) + next_mask->feather*weight;
	return result;
}

double MaskAutos::get_fader(int64_t position, int i, int direction)
{
	Auto *begin = 0, *end = 0;
	position = (direction == PLAY_FORWARD) ? position : (position - 1);
	MaskAuto*prev = (MaskAuto*)get_prev_auto(position, PLAY_FORWARD, begin, 1);
	MaskAuto*next = (MaskAuto*)get_next_auto(position, PLAY_FORWARD, end, 1);
	SubMask *prev_mask = prev->get_submask(i), *next_mask = next->get_submask(i);

	double weight = end->position == begin->position ? 0.0 :
		(double)(position - begin->position) / (end->position - begin->position);

	double result = prev_mask->fader * (1-weight) + next_mask->fader*weight;
// printf("MaskAutos::get_fader %d %d %d %f %d %f %d\n", __LINE__, i,
// ((MaskAuto*)begin)->fader, 1.0 - weight, ((MaskAuto*)end)->fader, weight, result);
	return result;
}


void MaskAutos::avg_points(MaskPoint *output, MaskPoint *input1, MaskPoint *input2,
		int64_t output_position, int64_t position1, int64_t position2)
{
	if(position2 == position1) {
		*output = *input1;
	}
	else {
		float fraction2 = (float)(output_position - position1) / (position2 - position1);
		float fraction1 = 1 - fraction2;
		output->x = input1->x * fraction1 + input2->x * fraction2;
		output->y = input1->y * fraction1 + input2->y * fraction2;
		output->control_x1 = input1->control_x1 * fraction1 + input2->control_x1 * fraction2;
		output->control_y1 = input1->control_y1 * fraction1 + input2->control_y1 * fraction2;
		output->control_x2 = input1->control_x2 * fraction1 + input2->control_x2 * fraction2;
		output->control_y2 = input1->control_y2 * fraction1 + input2->control_y2 * fraction2;
	}

}


Auto* MaskAutos::new_auto()
{
	return new MaskAuto(edl, this);
}

void MaskAutos::dump(FILE *fp)
{
	fprintf(fp, "	MaskAutos::dump %p\n", this);
	fprintf(fp, "	Default: position %jd submasks %d\n",
		default_auto->position,
		((MaskAuto*)default_auto)->masks.total);
	((MaskAuto*)default_auto)->dump(fp);
	for( Auto* current = first; current; current=NEXT ) {
		fprintf(fp, "	position %jd masks %d\n",
			current->position,
			((MaskAuto*)current)->masks.total);
		((MaskAuto*)current)->dump(fp);
	}
}

int MaskAutos::mask_exists(int64_t position, int direction)
{
	Auto *current = 0;
	position = (direction == PLAY_FORWARD) ? position : (position - 1);

	MaskAuto* keyframe = (MaskAuto*)get_prev_auto(position, direction, current);

	for( int i = 0; i < keyframe->masks.total; i++ ) {
		SubMask *mask = keyframe->get_submask(i);
		if(mask->points.total > 1)
			return 1;
	}
	return 0;
}

int MaskAutos::total_submasks(int64_t position, int direction)
{
	position = (direction == PLAY_FORWARD) ? position : (position - 1);
	for( MaskAuto* current=(MaskAuto*)last; current; current=(MaskAuto*)PREVIOUS ) {
		if( current->position <= position ) {
			return current->masks.total;
		}
	}

	return ((MaskAuto*)default_auto)->masks.total;
}


void MaskAutos::translate_masks(float translate_x, float translate_y)
{
	((MaskAuto *)default_auto)->translate_submasks(translate_x, translate_y);
	for( MaskAuto* current=(MaskAuto*)first; current; current=(MaskAuto*)NEXT ) {
		current->translate_submasks(translate_x, translate_y);
	}
}

void MaskAutos::set_proxy(int orig_scale, int new_scale)
{
	((MaskAuto *)default_auto)->scale_submasks(orig_scale, new_scale);
	for( MaskAuto* current=(MaskAuto*)first; current; current=(MaskAuto*)NEXT ) {
		current->scale_submasks(orig_scale, new_scale);
	}
}

