
/*
 * CINELERRA
 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
 * Copyright (C) 2003-2016 Cinelerra CV contributors
 *
 * 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 "clip.h"
#include "edl.h"
#include "edlsession.h"
#include "filexml.h"
#include "labels.h"
#include "mwindow.h"
#include "mwindowgui.h"
#include "patchbay.h"
#include "recordlabel.h"
#include "mainsession.h"
#include "theme.h"
#include "timebar.h"
#include <string.h>



Labels::Labels(EDL *edl, const char *xml_tag)
 : List<Label>()
{
	this->edl = edl;
	this->xml_tag = xml_tag;
}

Labels::~Labels()
{
	delete_all();
}

void Labels::dump(FILE *fp)
{
	for(Label *current = first; current; current = NEXT)
	{
		fprintf(fp, "  label: %f '%s'\n", current->position, current->textstr);
	}
}

void Labels::insert_labels(Labels *labels, double start, double length, int paste_silence)
{
	Label *new_label;
	Label *old_label;


//printf("Labels::insert_labels 1 %f\n", start);

// Insert silence in old labels
	if(paste_silence)
	{
		for(old_label = first; old_label; old_label = old_label->next)
		{
			if(old_label->position > start ||
				edl->equivalent(old_label->position, start))
				old_label->position += length;
		}
	}


// Insert one new label at a time
	for(new_label = labels->first; new_label; new_label = new_label->next)
	{
		int exists = 0;
//printf("Labels::insert_labels 2 %f\n", new_label->position + start);

// Check every old label for existence
		for(old_label = first; old_label; old_label = old_label->next)
		{
			if(edl->equivalent(old_label->position, new_label->position + start))
			{
				exists = 1;
				break;
			}
			else
			if(old_label->position > new_label->position + start)
				break;
		}

		if(!exists)
		{
			if(old_label)
				insert_before(old_label, new Label(edl, this, new_label->position + start, new_label->textstr));
			else
				append(new Label(edl, this, new_label->position + start, new_label->textstr));
		}
	}
}


void Labels::insert_label(double position)
{
	int exists = 0;
	Label *old_label = 0;

// Check every old label for existence
	for(old_label = first; old_label; old_label = old_label->next)
	{
		if(edl->equivalent(old_label->position, position))
		{
			exists = 1;
			break;
		}
		else
		if(old_label->position > position)
			break;
	}

	if(!exists)
	{
		if(old_label)
			insert_before(old_label, new Label(edl, this, position));
		else
			append(new Label(edl, this, position));
	}
}


int Labels::toggle_label(double start, double end)
{
	Label *current;
//printf("Labels::toggle_label 1 %f %f\n", start, end);

// handle selection start
// find label the selectionstart is after
	for(current = first;
		current && current->position < start && !edl->equivalent(current->position, start);
		current = NEXT)
	{
//printf("Labels::toggle_label 2 %f %f %f\n", start, end, current->position);
		;
	}

	if(current)
	{
//printf("Labels::toggle_label 3 %f %f %f\n", start, end, current->position);
		if(edl->equivalent(current->position, start))
		{        // remove it
//printf("Labels::toggle_label 1\n");
			remove(current);
		}
		else
		{        // insert before it
			current = insert_before(current, new Label(edl, this, start, ""));
		}
	}
	else
	{           // insert after last
//printf("Labels::toggle_label 1\n");
		current = append(new Label(edl, this, start, ""));
	}

// handle selection end
	if(!EQUIV(start, end))
	{
//printf("Labels::toggle_label 2 %.16e %.16e\n", start, end);
// find label the selectionend is after
		for(current = first;
			current && current->position < end && !edl->equivalent(current->position, end);
			current = NEXT)
		{
			;
		}

		if(current)
		{
			if(edl->equivalent(current->position, end))
			{
				remove(current);
			}
			else
			{
				current = insert_before(current, new Label(edl, this, end, ""));
			}
		}
		else
		{
			current = append(new Label(edl, this, end, ""));
		}
	}
	return 0;
}

Label *Labels::add_label(double position)
{
	Label *current;
	for(current = first;
		current && current->position < position;
		current = NEXT)
	{
		if( edl->equivalent(current->position, position) ) return 0;
	}

	if(current)
	{
		current = insert_before(current, new Label(edl, this, position));
	}
	else
	{
		current = append(new Label(edl, this, position));
	}
	return current;
}

int Labels::delete_all()
{
	while(last)
		remove(last);
	return 0;
}

int Labels::copy(double start, double end, FileXML *xml)
{
	xml->tag.set_title("LABELS");
	xml->append_tag();
	xml->append_newline();

	Label *current;
	for(current = label_of(start);
		current && current->position <= end;
		current = NEXT)
	{
		xml->tag.set_title("LABEL");
		xml->tag.set_property("TIME", (double)current->position - start);
		xml->tag.set_property("TEXTSTR", current->textstr);
//printf("Labels::copy %f\n", current->position - start);
		xml->append_tag();
		xml->tag.set_title("/LABEL");
		xml->append_tag();
		xml->append_newline();
	}

	xml->tag.set_title("/LABELS");
	xml->append_tag();
	xml->append_newline();
	xml->append_newline();
	return 0;
}

int Labels::copy_length(long start, long end) // return number of Labels in selection
{
	int result = 0;
	Label *current;

	for(current = label_of(start); current && current->position <= end; current = NEXT)
	{
		result++;
	}
	return result;
}

void Labels::copy_from(Labels *labels)
{
	while(last) delete last;

	for(Label *current = labels->first; current; current = NEXT)
	{
		Label *new_label = new Label(edl, this, current->position, current->textstr);
		new_label->orig_id = current->orig_id;
		append(new_label);
	}
}


Labels& Labels::operator=(Labels &that)
{
	copy_from(&that);
printf("Labels::operator= 1\n");
	return *this;
}


int Labels::save(FileXML *xml)
// Note: Normally the saving of Labels is done by Labels::copy()
{
	xml->tag.set_title("LABELS");
	xml->append_tag();
	xml->append_newline();

	Label *current;

	for(current = first; current; current = NEXT)
	{
		xml->tag.set_title("LABEL");
		xml->tag.set_property("TIME", (double)current->position);
		xml->tag.set_property("TEXTSTR", current->textstr);
		xml->append_tag();
		xml->tag.set_title("/LABEL");
		xml->append_tag();
		xml->append_newline();
	}

	xml->append_newline();
	xml->tag.set_title("/LABELS");
	xml->append_tag();
	xml->append_newline();
	xml->append_newline();
	return 0;
}

int Labels::load(FileXML *xml, uint32_t load_flags)
{
	int result = 0;
	char string1[BCTEXTLEN], string2[BCTEXTLEN];

	sprintf(string1, "/%s", xml_tag);
	strcpy(string2, xml_tag);
	string2[strlen(string2) - 1] = 0;

	do{
		result = xml->read_tag();
		if(!result)
		{
			if(xml->tag.title_is(string1))
			{
				result = 1;
			}
			else
			if(xml->tag.title_is(string2))
			{
				double position = xml->tag.get_property("TIME", (double)-1);
				if(position < 0)
					position = xml->tag.get_property("SAMPLE", (double)-1);
//printf("Labels::load %f\n", position);
				if(position > -1)
				{
					Label *current = label_of(position);
					current = insert_before(current, new Label(edl, this, position, ""));
					xml->tag.get_property("TEXTSTR", current->textstr);
				}
			}
			else
			if(xml->tag.title_is("INPOINT"))
			{
				double position = xml->tag.get_property("TIME", (double)-1);
				if(position < 0)
					position = xml->tag.get_property("SAMPLE", (double)-1);
				if(position > -1)
				{
					;
				}
			}
			else
			if(xml->tag.title_is("OUTPOINT"))
			{
				double position = xml->tag.get_property("TIME", (double)-1);
				if(position < 0)
					position = xml->tag.get_property("SAMPLE", (double)-1);
				if(position > -1)
				{
					;
				}
			}
		}
	}while(!result);
	return 0;
}



int Labels::clear(double start, double end, int follow)
{
	Label *current;
	Label *next;

//printf("Labels::clear 1\n");
	current = label_of(start);
//printf("Labels::clear 2\n");
// remove selected labels
	while(current && current->position < end)
	{
		next = NEXT;
		delete current;
		current = next;
	}
// Shift later labels
//printf("Labels::clear 3\n");
	if(follow)
	{
		while(current)
		{
			current->position -= end - start;   // shift labels forward
			current = NEXT;
		}
//printf("Labels::clear 4\n");
		optimize();
//printf("Labels::clear 5\n");
	}

	return 0;
}


Label* Labels::prev_label(double position)
{
	Label *current;

// Test for label under cursor position
	for(current = first;
		current && !edl->equivalent(current->position, position);
		current = NEXT)
		;

// Test for label after cursor position
	if(!current)
		for(current = first;
			current && current->position < position;
			current = NEXT)
			;

// Test for label before cursor position
	if(!current)
		current = last;
	else
// Get previous label
		current = PREVIOUS;

	return current;
}

Label* Labels::next_label(double position)
{
	Label *current;

// Test for label under cursor position
	for(current = first;
		current && !edl->equivalent(current->position, position);
		current = NEXT)
		;

// Test for label before cursor position
	if(!current)
		for(current = last;
			current && current->position > position;
			current = PREVIOUS)
			;

// Test for label after cursor position
	if(!current)
		current = first;
	else
// Get next label
		current = NEXT;

	return current;
}

Label* Labels::get_label(int id)
{
	Label *current = first;
	while( current && current->orig_id != id ) current = NEXT;
	return current;
}

int Labels::insert(double start, double length)
{      // shift every label including the first one back
	Label *current;

	for(current = label_of(start); current; current = NEXT)
	{
		current->position += length;
	}
	return 0;
}

int Labels::paste_silence(double start, double end)
{
	insert(start, end - start);
	optimize();
	return 0;
}

int Labels::modify_handles(double oldposition, double newposition, int currentend)
{
	if( !currentend ) {	// left handle
		if( newposition < oldposition )
			insert(oldposition, oldposition - newposition);    // shift all labels right
		else
			clear(oldposition, newposition);   // clear selection
	}
	else {			// right handle
		if( newposition < oldposition )
			clear(newposition, oldposition);
		else
			insert(oldposition, newposition - oldposition);
	}
	return 0;
}

int Labels::optimize()
{
	int result = 1;
	Label *current;

	while(result)
	{
		result = 0;

		for(current = first; current && NEXT && !result;)
		{
			Label *next = NEXT;
			if(current->position == next->position)
			{
				delete current;
				result  = 1;
			}
			current = next;
		}
	}
	return 0;
}

Label* Labels::label_of(double position)
{
	Label *current;

	for(current = first; current; current = NEXT)
	{
		if(current->position >= position) return current;
	}
	return 0;
}










Label::Label()
 : ListItem<Label>()
{
	id = EDL::next_id();
	orig_id = id;
}

Label::Label(EDL *edl, Labels *labels, double position, const char *textstr)
 : ListItem<Label>()
{
	this->edl = edl;
	this->labels = labels;
	this->position = position;
	strcpy(this->textstr, textstr ? textstr : "");
	id = EDL::next_id();
	orig_id = id;
}


Label::~Label()
{
//	if(toggle) delete toggle;
}

LabelToggle::LabelToggle(MWindow *mwindow,
	Label *label,
	int x,
	int y,
	long position)
 : BC_Label(x, y, 0)
{
	this->mwindow = mwindow;
	this->label = label;
}

LabelToggle::~LabelToggle() { }

int LabelToggle::handle_event()
{
	return 0;
}

