
/*
 * 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 "bcsignals.h"
#include "clip.h"
#include "condition.h"
#include "edl.h"
#include "edlsession.h"
#include "localsession.h"
#include "playbackengine.h"
#include "preferences.h"
#include "tracks.h"
#include "transportque.h"


TransportCommand::TransportCommand(Preferences *preferences)
{
// In rendering we want a master EDL so settings don't get clobbered
// in the middle of a job.
	edl = new EDL;
	edl->create_objects();
	command = 0;
	change_type = 0;
	this->preferences = preferences;
	reset();
}

TransportCommand::~TransportCommand()
{
	edl->Garbage::remove_user();
}

void TransportCommand::reset()
{
	command = COMMAND_NONE;
	change_type = 0;
	playbackstart = 0;
	start_position = 0;
	end_position = 0;
	infinite = 0;
	realtime = 0;
	resume = 0;
	locked = 0;
	toggle_audio = 0;
	loop_play = 0;
	displacement = 0;
	speed = 0;
}

EDL* TransportCommand::get_edl()
{
	return edl;
}

void TransportCommand::delete_edl()
{
	edl->Garbage::remove_user();
	edl = 0;
}

void TransportCommand::new_edl()
{
	edl = new EDL;
	edl->create_objects();
}


void TransportCommand::copy_from(TransportCommand *command)
{
	this->command = command->command;
	this->change_type = command->change_type;
	this->edl->copy_all(command->edl);
	this->start_position = command->start_position;
	this->end_position = command->end_position;
	this->playbackstart = command->playbackstart;
	this->realtime = command->realtime;
	this->resume = command->resume;
	this->locked = command->locked;
	this->toggle_audio = command->toggle_audio;
	this->loop_play = command->loop_play;
	this->displacement = command->displacement;
	this->speed = command->speed;
}

TransportCommand& TransportCommand::operator=(TransportCommand &command)
{
	copy_from(&command);
	return *this;
}

int TransportCommand::single_frame(int command)
{
	return (command == SINGLE_FRAME_FWD || command == SINGLE_FRAME_REWIND ||
		command == CURRENT_FRAME || command == LAST_FRAME);
}

int TransportCommand::get_direction(int command)
{
	switch(command) {
	case SINGLE_FRAME_FWD:
	case NORMAL_FWD:
	case FAST_FWD:
	case SLOW_FWD:
	case CURRENT_FRAME:
		return PLAY_FORWARD;

	case SINGLE_FRAME_REWIND:
	case NORMAL_REWIND:
	case FAST_REWIND:
	case SLOW_REWIND:
	case LAST_FRAME:
		return PLAY_REVERSE;

	default:
		break;
	}
	return PLAY_FORWARD;
}

float TransportCommand::get_speed(int command, float speed)
{
// fast = 2.0, slow = 0.5
// float my_fast_speed = 2.0;
// float my_slow_speed = 0.5;
float my_fast_speed = preferences->fast_speed;
float my_slow_speed = preferences->slow_speed; 

	switch(command) {
	case SLOW_FWD:
	case SLOW_REWIND:
		return speed ? speed : my_slow_speed;

	case NORMAL_FWD:
	case NORMAL_REWIND:
	case SINGLE_FRAME_FWD:
	case SINGLE_FRAME_REWIND:
	case CURRENT_FRAME:
	case LAST_FRAME:
		return 1.;

	case FAST_FWD:
	case FAST_REWIND:
		return speed ? speed : my_fast_speed;
	}

	return 0.;
}

// Assume starting without pause
void TransportCommand::set_playback_range(EDL *edl, int use_inout, int do_displacement)
{
	if( !edl ) edl = this->edl;
	double length = edl->tracks->total_playable_length();
	double frame_period = 1.0 / edl->session->frame_rate;
	displacement = 0;

	start_position = use_inout && edl->local_session->inpoint_valid() ?
		edl->local_session->get_inpoint() :
		!loop_play ? edl->local_session->get_selectionstart(1) : 0;
	end_position = use_inout && edl->local_session->outpoint_valid() ?
		edl->local_session->get_outpoint() :
		!loop_play ? edl->local_session->get_selectionend(1) : length;
	if( start_position >= length )
		length = edl->tracks->total_length();

	if( command == REWIND ) {
		start_position = end_position = 0;
		command = CURRENT_FRAME;
	}
	else if( command == GOTO_END ) {
		start_position = end_position = length;
		command = LAST_FRAME;
	}
	else if( !use_inout && EQUIV(start_position, end_position) ) {
// starting play at or past end_position, play to end_position of media (for mixers)
		switch( command ) {
		case SLOW_FWD:
		case FAST_FWD:
		case NORMAL_FWD: {
			end_position = length;
// this prevents a crash if start_position position is after the loop when playing forwards
			if( edl->local_session->loop_playback &&
			    start_position > edl->local_session->loop_end ) {
				start_position = edl->local_session->loop_start;
			}
			displacement = realtime && do_displacement ? frame_period : 0;
			break; }

		case SLOW_REWIND:
		case FAST_REWIND:
		case NORMAL_REWIND:
			start_position = 0;
// this prevents a crash if start_position position is before the loop when playing backwards
			if( edl->local_session->loop_playback &&
			    end_position <= edl->local_session->loop_start ) {
					end_position = edl->local_session->loop_end;
			}
			break;

		case SINGLE_FRAME_FWD:
			displacement = realtime && do_displacement ? frame_period : 0;
		case CURRENT_FRAME:
		case LAST_FRAME:
			end_position = start_position + frame_period;
			break;

		case SINGLE_FRAME_REWIND:
			start_position = end_position - frame_period;
			break;
		}
		start_position += displacement;
		end_position += displacement;
	}
//	if( start_position < 0 )
//		start_position = 0;
//	if( end_position > length )
//		end_position = length;
	if( end_position < start_position )
		end_position = start_position;

	playbackstart = get_direction() == PLAY_FORWARD ?
		start_position : end_position;
}

void TransportCommand::playback_range_adjust_inout()
{
	if(edl->local_session->inpoint_valid() ||
		edl->local_session->outpoint_valid())
	{
		playback_range_inout();
	}
}

void TransportCommand::playback_range_inout()
{
	if(edl->local_session->inpoint_valid())
		start_position = edl->local_session->get_inpoint();
	else
		start_position = 0;

	if(edl->local_session->outpoint_valid())
		end_position = edl->local_session->get_outpoint();
	else {
		end_position = edl->tracks->total_playable_length();
		if( start_position >= end_position )
			end_position = edl->tracks->total_length();
	}
}

void TransportCommand::playback_range_project()
{
	start_position = 0;
	end_position = edl->tracks->total_playable_length();
}

void TransportCommand::playback_range_1frame()
{
	start_position = end_position = edl->local_session->get_selectionstart(1);
	if( edl->session->frame_rate > 0 ) end_position += 1./edl->session->frame_rate;
}


