/*
 * CINELERRA
 * Copyright (C) 2011 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 "asset.h"
#include "bcdialog.h"
#include "bcsignals.h"
#include "xfer/xfer.h"
#include "channelpicker.h"
#include "condition.h"
#include "cursors.h"
#include "devicedvbinput.h"
#ifdef HAVE_DV
#include "libdv.h"
#endif
#include "edl.h"
#include "edlsession.h"
#include "keys.h"
#include "language.h"
#include "mainsession.h"
#include "meterpanel.h"
#include "mwindow.h"
#include "playbackconfig.h"
#include "preferences.h"
#include "record.h"
#include "recordconfig.h"
#include "recordgui.h"
#include "recordscopes.h"
#include "recordtransport.h"
#include "recordmonitor.h"
#include "signalstatus.h"
#include "theme.h"
#include "videodevice.inc"
#include "vframe.h"
#include "videodevice.h"
#include "vdevicedvb.h"


RecordMonitor::RecordMonitor(MWindow *mwindow, Record *record)
 : Thread(1, 0, 0)
{
	this->mwindow = mwindow;
	this->record = record;
	device = 0;
	thread = 0;
	scope_thread = 0;
}


RecordMonitor::~RecordMonitor()
{
	delete thread;
	window->set_done(0);
	Thread::join();
	if( device ) {
		device->close_all();
		delete device;
	}
	delete scope_thread;
	delete window;
}

void RecordMonitor::create_objects()
{
	int min_w = xS(150);

	if( !record->default_asset->video_data )
		min_w = MeterPanel::get_meters_width(mwindow->theme,
			record->default_asset->channels, 1);

	window = new RecordMonitorGUI(mwindow, record, this, min_w);
	window->create_objects();

	if( record->default_asset->video_data ) {
// Configure the output for record monitoring
		VideoOutConfig config;
		device = new VideoDevice;

// Override default device for X11 drivers
		if(mwindow->edl->session->playback_config->vconfig->driver ==
			PLAYBACK_X11_XV) config.driver = PLAYBACK_X11_XV;
		config.x11_use_fields = 0;
		device->open_output(&config,
			record->default_asset->frame_rate,
			record->default_asset->width,
			record->default_asset->height,
			window->canvas, 0);

		scope_thread = new RecordScopeThread(mwindow, this);

		if(mwindow->session->record_scope) {
			scope_thread->start();
		}

		thread = new RecordMonitorThread(mwindow, record, this);
		thread->start_playback();
	}

	Thread::start();
}


void RecordMonitor::run()
{
	window->run_window();
	close_threads();
}

void RecordMonitor::close_threads()
{
	if(window->channel_picker) window->channel_picker->close_threads();
}

int RecordMonitor::update(VFrame *vframe)
{
	return thread->write_frame(vframe);
}

void RecordMonitor::update_channel(char *text)
{
	if( window->channel_picker )
		window->channel_picker->channel_text->update(text);
}

int RecordMonitor::get_mbuttons_height()
{
	return RECBUTTON_HEIGHT;
}

void RecordMonitor::fix_size(int &w, int &h, int width_given, float aspect_ratio)
{
	w = width_given;
	h = (int)((float)width_given / aspect_ratio);
}

float RecordMonitor::get_scale(int w)
{
	if( mwindow->edl->get_aspect_ratio() >
		(float)record->frame_w / record->frame_h ) {
		return (float)w /
			((float)record->frame_h *
			mwindow->edl->get_aspect_ratio());
	}
	else {
		return (float)w / record->frame_w;
	}
}

int RecordMonitor::get_canvas_height()
{
	return window->get_h() - get_mbuttons_height();
}

int RecordMonitor::get_channel_x()
{
//	return 240;
	return xS(5);
}

int RecordMonitor::get_channel_y()
{
	return yS(2);
}

void RecordMonitor::stop_playback()
{
	if( !thread || thread->finished() ) return;
	window->enable_signal_status(0);
	if( thread ) {
		thread->stop_playback();
	}
}

void RecordMonitor::start_playback()
{
	if( thread ) {
		thread->output_lock->reset();
		thread->start_playback();
	}
	window->enable_signal_status(1);
}


void RecordMonitor::reconfig()
{
	stop_playback();
	VideoOutConfig config = *device->out_config;
	device->close_all();
	device->open_output(&config,
		record->default_asset->frame_rate,
		record->default_asset->width,
		record->default_asset->height,
		window->canvas, 0);
	start_playback();
	redraw();
}

void RecordMonitor::redraw()
{
	if( thread && window && thread->record->video_window_open )
		window->redraw();
}

void RecordMonitor::display_vframe(VFrame *in, int x, int y,
		int alpha, double secs, double scale)
{
	if( !thread ) return;
	thread->display_vframe(in, x, y, alpha, secs, scale);
}

void RecordMonitor::undisplay_vframe()
{
	if( !thread ) return;
	thread->undisplay_vframe();
}

RecordMonitorGUI::RecordMonitorGUI(MWindow *mwindow,
	Record *record, RecordMonitor *thread, int min_w)
 : BC_Window(_(PROGRAM_NAME ": Video in"),
 			mwindow->session->rmonitor_x,
			mwindow->session->rmonitor_y,
 			mwindow->session->rmonitor_w,
 			mwindow->session->rmonitor_h,
			min_w, yS(50), 1, 1, 1, -1,
			mwindow->get_cwindow_display())
{
//printf("%d %d\n", mwindow->session->rmonitor_w, mwindow->theme->rmonitor_meter_x);
	this->mwindow = mwindow;
	this->thread = thread;
	this->record = record;
#ifdef HAVE_FIREWIRE
	avc = 0;
	avc1394_transport = 0;
	avc1394transport_title = 0;
	avc1394transport_timecode = 0;
	avc1394transport_thread = 0;
#endif
	bitmap = 0;
	channel_picker = 0;
	reverse_interlace = 0;
	meters = 0;
	canvas = 0;
	cursor_toggle = 0;
	big_cursor_toggle = 0;
	current_operation = MONITOR_NONE;
	signal_status = 0;
// *** CONTEXT_HELP ***
	context_help_set_keyword("Capturing and Recording Media");
}

RecordMonitorGUI::~RecordMonitorGUI()
{
	lock_window("RecordMonitorGUI::~RecordMonitorGUI");
#ifdef HAVE_DVB
	delete signal_status;
#endif
	delete canvas;
	delete cursor_toggle;
	delete big_cursor_toggle;
	delete bitmap;
	if( channel_picker ) delete channel_picker;
#ifdef HAVE_FIREWIRE
	delete avc1394transport_thread;
	delete avc;
	delete avc1394_transport;
	delete avc1394transport_title;
#endif
	delete meters;
	unlock_window();
}

void RecordMonitorGUI::create_objects()
{
// y offset for video canvas if we have the transport controls
	lock_window("RecordMonitorGUI::create_objects");
	int driver = mwindow->edl->session->vconfig_in->driver;
	int do_channel = (driver == CAPTURE_DVB ||
			driver == VIDEO4LINUX2 ||
			driver == VIDEO4LINUX2JPEG ||
			driver == VIDEO4LINUX2MPEG ||
			driver == CAPTURE_JPEG_WEBCAM ||
			driver == CAPTURE_YUYV_WEBCAM);
	int do_cursor = driver == SCREENCAPTURE;
	int do_scopes = do_channel || driver == SCREENCAPTURE;
	int do_interlace = driver == VIDEO4LINUX2JPEG;
	int background_done = 0;
	int do_audio = record->default_asset->audio_data;
	int do_video = record->default_asset->video_data;
	int do_meters = record->metering_audio;
	int channels = !do_meters ? 0 : record->default_asset->channels;

	mwindow->theme->get_rmonitor_sizes(do_meters, do_video,
		do_channel || do_scopes, do_interlace, 0, channels);


	if(do_video) {
#ifdef HAVE_FIREWIRE
		if( driver == CAPTURE_FIREWIRE || driver == CAPTURE_IEC61883 ) {
			avc = new AVC1394Control;
			if( avc->device > -1 ) {
				mwindow->theme->get_rmonitor_sizes(do_meters, do_video,
					 do_channel, do_interlace, 1, channels);
				mwindow->theme->draw_rmonitor_bg(this);
				background_done = 1;

				avc1394_transport = new AVC1394Transport(mwindow,
					avc,
					this,
					mwindow->theme->rmonitor_tx_x,
					mwindow->theme->rmonitor_tx_y);
				avc1394_transport->create_objects();

				add_subwindow(avc1394transport_timecode =
					new BC_Title(avc1394_transport->x_end,
						mwindow->theme->rmonitor_tx_y + yS(10),
						"00:00:00:00",
						MEDIUM_7SEGMENT,
						BLACK));

				avc1394transport_thread =
					new AVC1394TransportThread(avc1394transport_timecode,
						avc);

				avc1394transport_thread->start();

			}
		}
#endif

		if( !background_done ) {
			mwindow->theme->draw_rmonitor_bg(this);
			background_done = 1;
		}

		mwindow->theme->rmonitor_canvas_w = MAX(xS(10), mwindow->theme->rmonitor_canvas_w);
		mwindow->theme->rmonitor_canvas_h = MAX(yS(10), mwindow->theme->rmonitor_canvas_h);
		canvas = new RecordMonitorCanvas(mwindow, this, record,
			mwindow->theme->rmonitor_canvas_x,
			mwindow->theme->rmonitor_canvas_y,
			mwindow->theme->rmonitor_canvas_w,
			mwindow->theme->rmonitor_canvas_h);
		canvas->create_objects(0);

#ifdef HAVE_DVB
		if( driver == CAPTURE_DVB ) {
			int ssw = SignalStatus::calculate_w(this);
			signal_status = new SignalStatus(this, get_w()-ssw-xS(3), 0);
			add_subwindow(signal_status);
			signal_status->create_objects();
		}
#endif

		int x = mwindow->theme->widget_border;
		int y = mwindow->theme->widget_border;
		if( do_channel ) {
			channel_picker = new RecordChannelPicker(mwindow,
				record, thread, this, record->channeldb,
				mwindow->theme->rmonitor_channel_x,
				mwindow->theme->rmonitor_channel_y);
			channel_picker->create_objects();
			x += channel_picker->get_w() + mwindow->theme->widget_border;
		}
		if( driver == VIDEO4LINUX2JPEG ) {
			add_subwindow(reverse_interlace = new ReverseInterlace(record,
				mwindow->theme->rmonitor_interlace_x,
				mwindow->theme->rmonitor_interlace_y));
			x += reverse_interlace->get_w() + mwindow->theme->widget_border;
		}

		if( do_scopes ) {
			scope_toggle = new ScopeEnable(mwindow, thread, x, y);
			add_subwindow(scope_toggle);
			x += scope_toggle->get_w() + mwindow->theme->widget_border;
		}

		if( do_cursor ) {
			add_subwindow(cursor_toggle = new DoCursor(record,
				x, 
				y));
			x += cursor_toggle->get_w() + mwindow->theme->widget_border;
			add_subwindow(big_cursor_toggle = new DoBigCursor(record,
				x, 
				y));
			x += big_cursor_toggle->get_w() + mwindow->theme->widget_border;
		}

		add_subwindow(monitor_menu = new BC_PopupMenu(0, 0, 0, "", 0));
		monitor_menu->add_item(new RecordMonitorFullsize(mwindow, this));
	}


	if( !background_done ) {
		mwindow->theme->draw_rmonitor_bg(this);
		background_done = 1;
	}

	if( do_audio ) {
		meters = new MeterPanel(mwindow,
			this,
			mwindow->theme->rmonitor_meter_x,
			mwindow->theme->rmonitor_meter_y,
			record->default_asset->video_data ? -1 : mwindow->theme->rmonitor_meter_w,
			mwindow->theme->rmonitor_meter_h,
			channels, do_meters, 1, 0);
		meters->create_objects();
	}
	unlock_window();
}

int RecordMonitorGUI::button_press_event()
{
	if( canvas && canvas->get_fullscreen() && canvas->get_canvas())
		return canvas->button_press_event_base(canvas->get_canvas());

	if( get_buttonpress() == 2 ) {
		return 0;
	}
	else
// Right button
	if( get_buttonpress() == 3 ) {
		monitor_menu->activate_menu();
		return 1;
	}
	return 0;
}

int RecordMonitorGUI::cursor_leave_event()
{
	if(canvas && canvas->get_canvas())
		return canvas->cursor_leave_event_base(canvas->get_canvas());
	return 0;
}

int RecordMonitorGUI::cursor_enter_event()
{
	if( canvas && canvas->get_canvas() )
		return canvas->cursor_enter_event_base(canvas->get_canvas());
	return 0;
}

int RecordMonitorGUI::button_release_event()
{
	if( canvas && canvas->get_canvas() )
		return canvas->button_release_event();
	return 0;
}

int RecordMonitorGUI::cursor_motion_event()
{
	if( canvas && canvas->get_canvas() ) {
		canvas->get_canvas()->unhide_cursor();
		return canvas->cursor_motion_event();
	}
	return 0;
}

int RecordMonitorGUI::keypress_event()
{
	int result = 0;

	switch(get_keypress()) {
	case LEFT:
		if( !ctrl_down() ) {
			record->record_gui->set_translation(--(record->video_x), record->video_y, record->video_zoom);
		}
		else {
			record->video_zoom -= 0.1;
			record->record_gui->set_translation(record->video_x, record->video_y, record->video_zoom);
		}
		result = 1;
		break;
	case RIGHT:
		if( !ctrl_down() ) {
			record->record_gui->set_translation(++(record->video_x), record->video_y, record->video_zoom);
		}
		else {
			record->video_zoom += 0.1;
			record->record_gui->set_translation(record->video_x, record->video_y, record->video_zoom);
		}
		result = 1;
		break;
	case UP:
		if( !ctrl_down() ) {
			record->record_gui->set_translation(record->video_x, --(record->video_y), record->video_zoom);
		}
		else {
			record->video_zoom -= 0.1;
			record->record_gui->set_translation(record->video_x, record->video_y, record->video_zoom);
		}
		result = 1;
		break;
	case DOWN:
		if( !ctrl_down() ) {
			record->record_gui->set_translation(record->video_x, ++(record->video_y), record->video_zoom);
		}
		else {
			record->video_zoom += 0.1;
			record->record_gui->set_translation(record->video_x, record->video_y, record->video_zoom);
		}
		result = 1;
		break;
	case 'w':
		close_event();
		break;

	default:
		if(canvas) result = canvas->keypress_event(this);
#ifdef HAVE_FIREWIRE
		if(!result && avc1394_transport)
			result = avc1394_transport->keypress_event(get_keypress());
#endif
		break;
	}

	if( !result )
		result = context_help_check_and_show();

	return result;
}


int RecordMonitorGUI::translation_event()
{
//printf("MWindowGUI::translation_event 1 %d %d\n", get_x(), get_y());
	mwindow->session->rmonitor_x = get_x();
	mwindow->session->rmonitor_y = get_y();
	return 0;
}

int RecordMonitorGUI::resize_event(int w, int h)
{
	int driver = mwindow->edl->session->vconfig_in->driver;
	int do_channel = (driver == CAPTURE_DVB ||
			driver == VIDEO4LINUX2 ||
			driver == VIDEO4LINUX2JPEG ||
			driver == VIDEO4LINUX2MPEG ||
			driver == CAPTURE_JPEG_WEBCAM ||
			driver == CAPTURE_YUYV_WEBCAM);
	int do_scopes = do_channel || driver == SCREENCAPTURE;
	int do_interlace = (driver == VIDEO4LINUX2JPEG);
	int do_avc = 0;
#ifdef HAVE_FIREWIRE
	do_avc = avc1394_transport ? 1 : 0;
#endif
	int do_meters = meters && record->default_asset->audio_data &&
		record->metering_audio;
	int do_video = record->default_asset->video_data;

	mwindow->session->rmonitor_x = get_x();
	mwindow->session->rmonitor_y = get_y();
	mwindow->session->rmonitor_w = w;
	mwindow->session->rmonitor_h = h;

	mwindow->theme->get_rmonitor_sizes(do_meters, do_video,
		do_channel || do_scopes, do_interlace, do_avc,
		record->default_asset->channels);
	mwindow->theme->draw_rmonitor_bg(this);


// 	record_transport->reposition_window(mwindow->theme->rmonitor_tx_x,
// 		mwindow->theme->rmonitor_tx_y);
#ifdef HAVE_FIREWIRE
	if(avc1394_transport)
	{
		avc1394_transport->reposition_window(mwindow->theme->rmonitor_tx_x,
			mwindow->theme->rmonitor_tx_y);
	}
#endif

	
	if( channel_picker ) {
		channel_picker->reposition();
	}
	if( reverse_interlace ) {
		reverse_interlace->reposition_window(
			reverse_interlace->get_x(),
			reverse_interlace->get_y());
	}
	if( cursor_toggle ) {
		cursor_toggle->reposition_window(
			cursor_toggle->get_x(),
			cursor_toggle->get_y());
		big_cursor_toggle->reposition_window(
			big_cursor_toggle->get_x(),
			big_cursor_toggle->get_y());
	}

	if( canvas && do_video ) {
		canvas->reposition_window(0,
			mwindow->theme->rmonitor_canvas_x,
			mwindow->theme->rmonitor_canvas_y,
			mwindow->theme->rmonitor_canvas_w,
			mwindow->theme->rmonitor_canvas_h);
	}

	if( do_meters ) {
		meters->reposition_window(mwindow->theme->rmonitor_meter_x,
			mwindow->theme->rmonitor_meter_y,
			do_video ? -1 : mwindow->theme->rmonitor_meter_w,
			mwindow->theme->rmonitor_meter_h);
		meters->set_meters(record->default_asset->channels,1);
	}
	else if( meters ) {
		meters->set_meters(0,0);
	}

	set_title();
	BC_WindowBase::resize_event(w, h);
	flash();
	return 1;
}

int RecordMonitorGUI::redraw()
{
	lock_window("RecordMonitorGUI::redraw");
	int w = mwindow->session->rmonitor_w;
	int h = mwindow->session->rmonitor_h;
	int result = resize_event(w, h);
	unlock_window();
	return result;
}

int RecordMonitorGUI::set_title()
{
return 0;
	char string[1024];
	int scale;

	scale = (int)(thread->get_scale(thread->record->video_window_w) * 100 + 0.5);

	sprintf(string, _(PROGRAM_NAME ": Video in %d%%"), scale);
	BC_Window::set_title(string);
	return 0;
}

int RecordMonitorGUI::close_event()
{
	thread->record->set_video_monitoring(0);
	thread->record->set_audio_monitoring(0);
	thread->record->video_window_open = 0;
	unlock_window();
	lock_window("RecordMonitorGUI::close_event");
	hide_window();
	return 0;
}

int RecordMonitorGUI::create_bitmap()
{
	if(bitmap && (bitmap->get_w() != get_w() ||
			bitmap->get_h() != thread->get_canvas_height())) {
		delete bitmap;
		bitmap = 0;
	}

	if( !bitmap && canvas ) {
//		bitmap = canvas->new_bitmap(get_w(), thread->get_canvas_height());
	}
	return 0;
}


DoCursor::DoCursor(Record *record, int x, int y)
 : BC_CheckBox(x, y, record->do_cursor, _("Record cursor"))
{
	this->record = record;
}

DoCursor::~DoCursor()
{
}

int DoCursor::handle_event()
{
	record->do_cursor = get_value();
	return 0;
}


DoBigCursor::DoBigCursor(Record *record, int x, int y)
 : BC_CheckBox(x, y, record->do_big_cursor, _("Big cursor"))
{
	this->record = record;
}

DoBigCursor::~DoBigCursor()
{
}

int DoBigCursor::handle_event()
{
	record->do_big_cursor = get_value();
	return 0;
}


void RecordMonitorGUI::enable_signal_status(int enable)
{
#ifdef HAVE_DVB
	if( !signal_status ) return;
	signal_status->lock_window("RecordMonitorGUI::enable_signal_status");
	if( !enable )
		signal_status->hide_window();
	else
		signal_status->show_window();
	signal_status->unlock_window();
	DeviceDVBInput *dvb_input = record->dvb_device();
	if( dvb_input )
		dvb_input->set_signal_status(!enable ? 0 : signal_status);
#endif
}

void RecordMonitorGUI::
display_video_text(int x, int y, const char *text, int font,
	int bg_color, int color, int alpha, double secs, double scale)
{
	lock_window("RecordMonitorGUI::display_text");
	set_font(font);
	int ch = get_text_height(font);
	int h = get_text_height(font,text) + ch/2;
	int w = get_text_width(font, text) + ch;
	BC_Pixmap pixmap(this, w, h);
	set_opaque();
	set_color(bg_color);
	draw_box(0, 0, w, h, &pixmap);
	set_color(color);
	draw_text(ch/2, ch, text, strlen(text), &pixmap);
	BC_Bitmap bitmap(this, w, h, BC_RGB888, 0);
	VFrame in(&bitmap, w, h, BC_RGB888, -1);
	Drawable drawable = pixmap.get_pixmap();
	bitmap.read_drawable(drawable, 0, 0, &in);
	unlock_window();
	record->display_vframe(&in, x, y, alpha, secs, scale);
}

ReverseInterlace::ReverseInterlace(Record *record, int x, int y)
 : BC_CheckBox(x, y, record->reverse_interlace, _("Swap fields"))
{
	this->record = record;
}

ReverseInterlace::~ReverseInterlace()
{
}

int ReverseInterlace::handle_event()
{
	record->reverse_interlace = get_value();
	return 0;
}

RecordMonitorCanvas::RecordMonitorCanvas(MWindow *mwindow,
	RecordMonitorGUI *window, Record *record,
	int x, int y, int w, int h)
 : Canvas(mwindow, window, x, y, w, h,
	record->default_asset->width,
	record->default_asset->height,
	0)
{
	this->window = window;
	this->mwindow = mwindow;
	this->record = record;
//printf("RecordMonitorCanvas::RecordMonitorCanvas 1 %d %d %d %d\n",
//x, y, w, h);
//printf("RecordMonitorCanvas::RecordMonitorCanvas 2\n");
}

RecordMonitorCanvas::~RecordMonitorCanvas()
{
}

void RecordMonitorCanvas::create_objects(EDL *edl)
{
	Canvas::create_objects(edl);
	canvas_menu->add_item(new CanvasPopupResetTranslation(this));
}

int RecordMonitorCanvas::get_output_w()
{
	return record->default_asset->width;
}

int RecordMonitorCanvas::get_output_h()
{
	return record->default_asset->height;
}


int RecordMonitorCanvas::button_press_event()
{

	if(Canvas::button_press_event()) return 1;
	if( mwindow->edl->session->vconfig_in->driver == SCREENCAPTURE ) {
		window->current_operation = MONITOR_TRANSLATE;
		window->translate_x_origin = record->video_x;
		window->translate_y_origin = record->video_y;
		window->cursor_x_origin = get_cursor_x();
		window->cursor_y_origin = get_cursor_y();
	}

	return 0;
}

void RecordMonitorCanvas::zoom_resize_window(float percentage)
{
	int canvas_w, canvas_h;
	calculate_sizes(mwindow->edl->get_aspect_ratio(),
		record->default_asset->width, record->default_asset->height,
		percentage, canvas_w, canvas_h);
	int new_w, new_h;
	new_w = canvas_w + (window->get_w() - mwindow->theme->rmonitor_canvas_w);
	new_h = canvas_h + (window->get_h() - mwindow->theme->rmonitor_canvas_h);
	window->resize_window(new_w, new_h);
	window->resize_event(new_w, new_h);
}


int RecordMonitorCanvas::button_release_event()
{
	window->current_operation = MONITOR_NONE;
	return 0;
}

int RecordMonitorCanvas::cursor_motion_event()
{
//SET_TRACE
	if( window->current_operation == MONITOR_TRANSLATE ) {
//SET_TRACE
		record->set_translation(
			get_cursor_x() - window->cursor_x_origin + window->translate_x_origin,
			get_cursor_y() - window->cursor_y_origin + window->translate_y_origin);
//SET_TRACE
	}

	return 0;
}

int RecordMonitorCanvas::cursor_enter_event()
{
	if(mwindow->edl->session->vconfig_in->driver == SCREENCAPTURE)
		set_cursor(MOVE_CURSOR);
	return 0;
}

void RecordMonitorCanvas::reset_translation()
{
	record->set_translation(0, 0);
}

int RecordMonitorCanvas::keypress_event()
{
	if( !get_canvas() ) return 0;

	switch(get_canvas()->get_keypress()) {
	case LEFT:
		record->set_translation(--record->video_x, record->video_y);
		break;
	case RIGHT:
		record->set_translation(++record->video_x, record->video_y);
		break;
	case UP:
		record->set_translation(record->video_x, --record->video_y);
		break;
	case DOWN:
		record->set_translation(record->video_x, ++record->video_y);
		break;
	default:
		return 0;
	}
	return 1;
}

int RecordMonitorCanvas::keypress_event(RecordMonitorGUI *window)
{
	return Canvas::keypress_event(window);
}


RecordMonitorFullsize::RecordMonitorFullsize(MWindow *mwindow,
	RecordMonitorGUI *window)
 : BC_MenuItem(_("Zoom 100%"))
{
	this->mwindow = mwindow;
	this->window = window;
}
int RecordMonitorFullsize::handle_event()
{
	return 1;
}








// ================================== slippery playback ============================


RecordMonitorThread::RecordMonitorThread(MWindow *mwindow,
	Record *record,
	RecordMonitor *record_monitor)
 : Thread(1, 0, 0)
{
	this->mwindow = mwindow;
	this->record_monitor = record_monitor;
	this->record = record;
	this->ovly = 0;
	reset_parameters();
	output_lock = new Condition(1, "RecordMonitor::output_lock");
	input_lock = new Condition(1, "RecordMonitor::input_lock");
}

void RecordMonitorThread::reset_parameters()
{
	input_frame = 0;
	output_frame = 0;
	shared_data = 0;
	jpeg_engine = 0;
	dv_engine = 0;
	ready = 0;
	done = 1;
}


RecordMonitorThread::~RecordMonitorThread()
{
	stop_playback();
	if( input_frame && !shared_data )
		delete input_frame;
	delete ovly;
	delete output_lock;
	delete input_lock;
}

void RecordMonitorThread::init_output_format()
{
//printf("RecordMonitorThread::init_output_format 1\n");
	switch(mwindow->edl->session->vconfig_in->driver) {
	case SCREENCAPTURE:
		output_colormodel = record->vdevice->get_best_colormodel(record->default_asset);
		break;

	case VIDEO4LINUX2JPEG:
		jpeg_engine = new RecVideoMJPGThread(record, this,
			mwindow->edl->session->vconfig_in->v4l2jpeg_in_fields);
		jpeg_engine->start_rendering();
		output_colormodel = BC_YUV422P;
		break;

	case CAPTURE_FIREWIRE:
	case CAPTURE_IEC61883:
		dv_engine = new RecVideoDVThread(record, this);
		dv_engine->start_rendering();
		output_colormodel = BC_YUV422P;
		break;

	case CAPTURE_JPEG_WEBCAM:
		jpeg_engine = new RecVideoMJPGThread(record, this, 1);
		jpeg_engine->start_rendering();
		output_colormodel = BC_YUV420P;
		break;

	case CAPTURE_YUYV_WEBCAM:
		output_colormodel = BC_YUV422;
		break;


	case CAPTURE_DVB:
	case VIDEO4LINUX2:
	case VIDEO4LINUX2MPEG:
		output_colormodel = record->vdevice->get_best_colormodel(record->default_asset);
//printf("RecordMonitorThread::init_output_format 2 %d\n", output_colormodel);
		break;
	}
}

int RecordMonitorThread::start_playback()
{
	ready = 1;
	done = 0;
	output_frame = 0;
	output_lock->lock("RecordMonitorThread::start_playback");
	Thread::start();
	return 0;
}

int RecordMonitorThread::stop_playback()
{
	if( done ) return 0;
	done = 1;
	output_lock->unlock();
	Thread::join();
//printf("RecordMonitorThread::stop_playback 1\n");

	switch(mwindow->edl->session->vconfig_in->driver) {
	case VIDEO4LINUX2JPEG:
		if( jpeg_engine ) {
			jpeg_engine->stop_rendering();
			delete jpeg_engine;
			jpeg_engine = 0;
		}
		break;

	case CAPTURE_FIREWIRE:
	case CAPTURE_IEC61883:
		if( dv_engine ) {
			dv_engine->stop_rendering();
			delete dv_engine;
			dv_engine = 0;
		}
		break;
	case CAPTURE_DVB:
	case VIDEO4LINUX2MPEG:
		break;
	}
//printf("RecordMonitorThread::stop_playback 4\n");

	return 0;
}

int RecordMonitorThread::write_frame(VFrame *new_frame)
{
	if( ready ) {
		ready = 0;
		shared_data = (new_frame->get_color_model() != BC_COMPRESSED);


// Need to wait until after Record creates the input device before starting monitor
// because the input device deterimes the output format.
// First time
		if( !output_frame ) init_output_format();
		if( !shared_data ) {
			if(!input_frame) input_frame = new VFrame;
			input_frame->allocate_compressed_data(new_frame->get_compressed_size());
			memcpy(input_frame->get_data(),
				new_frame->get_data(),
				new_frame->get_compressed_size());
			input_frame->set_compressed_size(new_frame->get_compressed_size());
			input_frame->set_field2_offset(new_frame->get_field2_offset());
		}
		else {
			input_lock->lock("RecordMonitorThread::write_frame");
			input_frame = new_frame;
		}
		output_lock->unlock();
	}
	return 0;
}

int RecordMonitorThread::render_jpeg()
{
//printf("RecordMonitorThread::render_jpeg 1\n");
	jpeg_engine->render_frame(input_frame, input_frame->get_compressed_size());
//printf("RecordMonitorThread::render_jpeg 2\n");
	return 0;
}

int RecordMonitorThread::render_dv()
{
	dv_engine->render_frame(input_frame, input_frame->get_compressed_size());
	return 0;
}

// VFrame xfer all/odd/even inputs
void RecordMonitorThread::render_uncompressed()
{
	VFrame *in = input_frame, *out = output_frame;
	out->set_timestamp(in->get_timestamp());
	out->copy_params(in);

	unsigned char *in_ptrs[4], *out_ptrs[4];
	unsigned char **inp, **outp;
	if( BC_CModels::is_planar(in->get_color_model()) ) {
		in_ptrs[0] = in->get_y();
		in_ptrs[1] = in->get_u();
		in_ptrs[2] = in->get_v();
		in_ptrs[3] = in->get_a();
		inp = in_ptrs;
	}
	else
		inp = in->get_rows();
	if( BC_CModels::is_planar(out->get_color_model()) ) {
		out_ptrs[0] = out->get_y();
		out_ptrs[1] = out->get_u();
		out_ptrs[2] = out->get_v();
		out_ptrs[3] = out->get_a();
		outp = out_ptrs;
	}
	else
		outp = out->get_rows();

        int out_colormodel = out->get_color_model();
	int out_x = 0, out_y = 0;
	int out_w = out->get_w(), out_h = out->get_h();
	int out_rowspan = out->get_bytes_per_line();
        int inp_colormodel = in->get_color_model();
	int inp_x = 0, inp_y = 0;
	int inp_w = in->get_w(), inp_h = in->get_h();
	int inp_rowspan = in->get_bytes_per_line();
	int bg_color = 0;

        int ret = 1;
        if( inp_w > 0 && inp_h > 0 && out_w > 0 && out_h > 0 ) {
                BC_Xfer xfer(outp, out_colormodel, out_x, out_y, out_w, out_h, out_rowspan,
                        inp, inp_colormodel, inp_x, inp_y, inp_w, inp_h, inp_rowspan,
                        bg_color,0xff);
                int *row_table = xfer.row_table;
		switch( record->deinterlace ) {
		case RECORD_LACE_NONE:
			break;
		case RECORD_LACE_EVEN: {
			int inp_y1 = inp_y;
	                for( int i=0; i<out_h; ++i ) {
				if( (row_table[i] &= ~1) < inp_y1 )
					row_table[i] = inp_y1;
			}
			break; }
		case RECORD_LACE_ODD: {
			int inp_y2 = inp_y + inp_h-1;
	                for( int i=0; i<out_h; ++i ) {
				if( (row_table[i] |= 1) > inp_y2 )
					row_table[i] = inp_y2;
			}
			break; }
		}
                ret = xfer.xfer();
        }
        if( ret )
                printf("RecordMonitorThread::render_uncompressed failed: "
			"%d %d(%dx%d) to %d(%dx%d)\n", __LINE__,
                        inp_colormodel, inp_w, inp_h, out_colormodel, out_w, out_h);
}

void RecordMonitorThread::show_output_frame()
{
	if( ovly && ovly->overlay(output_frame) )
		undisplay_vframe();
	record_monitor->device->write_buffer(output_frame, record->edl);
}


void RecordMonitorThread::unlock_input()
{
	if(shared_data) input_lock->unlock();
}

void RecordMonitorThread::lock_input()
{
	if(shared_data) input_lock->lock();
}

int RecordMonitorThread::render_frame()
{
	switch(mwindow->edl->session->vconfig_in->driver) {
	case VIDEO4LINUX2JPEG:
	case CAPTURE_JPEG_WEBCAM:
		render_jpeg();
		break;

	case CAPTURE_FIREWIRE:
	case CAPTURE_IEC61883:
		render_dv();
		break;

	default:
		render_uncompressed();
		break;
	}

	return 0;
}

void RecordMonitorThread::new_output_frame()
{
	record_monitor->device->new_output_buffer(&output_frame, 
		output_colormodel,
		record->edl);
}

void RecordMonitorThread::
display_vframe(VFrame *in, int x, int y, int alpha, double secs, double scale)
{
	delete ovly;
	int ticks = secs * SESSION->vconfig_in->in_framerate;
	scale *= SESSION->vconfig_in->h / 1080.;
	ovly = new RecVideoOverlay(in, x, y, ticks, scale, alpha/255.);
}

RecVideoOverlay::
RecVideoOverlay(VFrame *vframe, int x, int y, int ticks, float scale, float alpha)
{
	this->x = x;
	this->y = y;
	this->ticks = ticks;
	this->scale = scale;
	this->alpha = alpha;
	this->vframe = new VFrame(*vframe);
}

RecVideoOverlay::
~RecVideoOverlay()
{
	delete vframe;
}

int RecVideoOverlay::
overlay(VFrame *out)
{
	VFrame *in = vframe;
	int xx = x * scale, yy = y * scale;
	int w = in->get_w(), h = in->get_h();
	int ww = w * scale, hh = h * scale;
	BC_CModels::transfer(out->get_rows(), in->get_rows(),
		out->get_y(), out->get_u(), out->get_v(),
		in->get_y(), in->get_u(), in->get_v(),
		0, 0, w, h, xx, yy, ww, hh,
		in->get_color_model(), out->get_color_model(), 0,
		in->get_bytes_per_line(), out->get_bytes_per_line());
	return ticks > 0 && --ticks == 0 ? 1 : 0;
}

void RecordMonitorThread::
undisplay_vframe()
{
	delete ovly;  ovly = 0;
}

void RecordMonitorThread::run()
{
//printf("RecordMonitorThread::run 1 %d\n", getpid());
	while(!done) {
// Wait for next frame
//SET_TRACE
		output_lock->lock("RecordMonitorThread::run");

		if(done) {
			unlock_input();
			return;
		}
//SET_TRACE
		new_output_frame();
//SET_TRACE
		render_frame();
//SET_TRACE
		record_monitor->scope_thread->process(output_frame);
//SET_TRACE
		show_output_frame();
//SET_TRACE
		unlock_input();
// Get next frame
		ready = 1;
	}
}



RecVideoMJPGThread::RecVideoMJPGThread(Record *record,
	RecordMonitorThread *thread,
	int fields)
{
	this->record = record;
	this->thread = thread;
	mjpeg = 0;
	this->fields = fields;
}

RecVideoMJPGThread::~RecVideoMJPGThread()
{
}

int RecVideoMJPGThread::start_rendering()
{
	mjpeg = mjpeg_new(record->default_asset->width,
		record->default_asset->height,
		fields);
//printf("RecVideoMJPGThread::start_rendering 1 %p\n", mjpeg);
	return 0;
}

int RecVideoMJPGThread::stop_rendering()
{
//printf("RecVideoMJPGThread::stop_rendering 1 %p\n", mjpeg);
	if(mjpeg) mjpeg_delete(mjpeg);
//printf("RecVideoMJPGThread::stop_rendering 2\n");
	return 0;
}

int RecVideoMJPGThread::render_frame(VFrame *frame, long size)
{
// printf("RecVideoMJPGThread::render_frame %d %02x%02x %02x%02x\n",
// frame->get_field2_offset(),
// frame->get_data()[0],
// frame->get_data()[1],
// frame->get_data()[frame->get_field2_offset()],
// frame->get_data()[frame->get_field2_offset() + 1]);
//frame->set_field2_offset(0);
	mjpeg_decompress(mjpeg,
		frame->get_data(),
		frame->get_compressed_size(),
		frame->get_field2_offset(),
		thread->output_frame->get_rows(),
		thread->output_frame->get_y(),
		thread->output_frame->get_u(),
		thread->output_frame->get_v(),
		thread->output_frame->get_color_model(),
		record->mwindow->preferences->processors);
	return 0;
}




RecVideoDVThread::RecVideoDVThread(Record *record, RecordMonitorThread *thread)
{
	this->record = record;
	this->thread = thread;
	dv = 0;
}

RecVideoDVThread::~RecVideoDVThread()
{
}


int RecVideoDVThread::start_rendering()
{
#ifdef HAVE_DV
	dv = dv_new();
#endif
	return 0;
}

int RecVideoDVThread::stop_rendering()
{
#ifdef HAVE_DV
	if( dv ) { dv_delete(((dv_t*)dv));  dv = 0; }
#endif
	return 0;
}

int RecVideoDVThread::render_frame(VFrame *frame, long size)
{
#ifdef HAVE_DV
	unsigned char *yuv_planes[3];
	yuv_planes[0] = thread->output_frame->get_y();
	yuv_planes[1] = thread->output_frame->get_u();
	yuv_planes[2] = thread->output_frame->get_v();
	dv_read_video(((dv_t*)dv),
		yuv_planes,
		frame->get_data(),
		frame->get_compressed_size(),
		thread->output_frame->get_color_model());
#endif
	return 0;
}

