/*
 * CINELERRA
 * Copyright (C) 2016-2020 William Morrow
 *
 * 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
 */

#ifdef HAVE_WINTV

#include "channelinfo.h"
#include "cwindow.h"
#include "cwindowgui.h"
#include "edl.h"
#include "mbuttons.h"
#include "mwindow.h"
#include "mwindowgui.h"
#include "language.h"
#include "playbackengine.h"
#include "playtransport.h"
#include "record.h"
#include "recordgui.h"
#include "recordmonitor.h"
#include "remotecontrol.h"
#include "tracks.h"
#include "wintv.h"

#include <dirent.h>
#include <linux/input.h>

/* The field to look up in struct input_event for integer seconds */
#ifndef input_event_sec
#define input_event_sec time.tv_sec
#endif

/* The field to look up in struct input_event for fractional seconds */
#ifndef input_event_usec
#define input_event_usec time.tv_usec
#endif


WinTV::WinTV(MWindow *mwindow, int ifd)
{
	this->mwindow = mwindow;
	this->ifd = ifd;

	ev = new input_event;
	memset(ev, 0, sizeof(*ev));
	ev->code = -1;
	done = -1;
	last_code = -1;
	code = -1;
}

WinTV::~WinTV()
{
	stop();
	delete ev;
}

void WinTV::stop()
{
	if( ifd >= 0 ) {
		ioctl(ifd, EVIOCGRAB, 0);
		close(ifd);
		ifd = -1;
	}
	if( running() && !done ) {
		done = 1;
		cancel();
		join();
	}
}

void WinTV::start()
{
	ioctl(ifd, EVIOCGRAB, 1);
	done = 0;
	Thread::start();
}

int WinTV::open_usb_input(int vendor, int product, int &version)
{
	int ret = -1;
	const char *dev_input = "/dev/input";
	DIR *dir = opendir(dev_input);
	if( !dir ) return ret;

	struct dirent64 *dp;
	struct input_id dev_id;
	while( (dp = readdir64(dir)) != 0 ) {
		char *fn = dp->d_name;
		if( !strcmp(fn, ".") || !strcmp(fn, "..") ) continue;
		char path[PATH_MAX];  struct stat st;
		snprintf(path, PATH_MAX, "%s/%s", dev_input, fn);
		if( stat(path, &st) < 0 ) continue;
		if( !S_ISCHR(st.st_mode) ) continue;
		int fd = open(path, O_RDONLY);
		if( fd < 0 ) continue;
		if( !ioctl(fd, EVIOCGID, &dev_id) ) {
			if( dev_id.bustype == BUS_USB &&
			    dev_id.vendor == vendor &&
			    dev_id.product == product ) {
				version = dev_id.version;
				ret = fd;
				break;
			}
		}
		close(fd);
	}
	closedir(dir);
	return ret;
}

WinTV *WinTV::probe(MWindow *mwindow)
{
	int ver = -1;
	int ifd = open_usb_input(0x2040, 0x826d, ver);
	if( ifd < 0 ) return 0;
	printf("detected hauppauge WinTV Model 1657, ver=0x%04x\n", ver);
	return new WinTV(mwindow, ifd);
}

void WinTV::run()
{
	enable_cancel();
	while( !done ) {
		int ret = read(ifd, ev, sizeof(*ev));
		if( done ) break;
		if( ret != sizeof(*ev) ) {
			if( ret < 0 ) { perror("read event"); break; }
			fprintf(stderr, "bad read: %d\n", ret);
			break;
		}
		handle_event();
	}
}

int WinTV::check_menu_keys(int code)
{
	int result = 1;
	switch( code ) {
	case WTV_POWER:
		mwindow->quit();
		break;
	case WTV_TV: {
		Record *record = mwindow->gui->record;
		if( !record->running() )
			record->start();
		else
			record->record_gui->interrupt_thread->start(0);
		break; }
	case WTV_MENU:
#ifdef HAVE_DVB
		mwindow->gui->channel_info->toggle_scan();
#endif
		break;
	case WTV_RED: {
		RemoteControl *remote_control = mwindow->gui->remote_control;
		if( !remote_control->deactivate() )
			remote_control->activate();
		break; }
	default:
		result = 0;
	}
	return result;
}

void WinTV::handle_event()
{
	switch(ev->type) {
	case EV_KEY: {
		if( !ev->value ) break;
		this->last_code = this->code;
		this->code = ev->code;
		if( check_menu_keys(code) ) break;
		RemoteHandler *handler = mwindow->gui->remote_control->handler;
		if( handler )
			handler->process_key(ev->code);
		break; }
	case EV_SYN:
	case EV_MSC:
		break;
	default: {
		time_t t = ev->input_event_sec;
		struct tm *tp = localtime(&t);
		printf("wintv event: %4d/%02d/%02d %02d:%02d:%02d.%03d = (%d, %d, 0x%x)\n",
			tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday,
			tp->tm_hour, tp->tm_min, tp->tm_sec,
			(int)ev->input_event_usec/1000, ev->type, ev->code, ev->value);
		break; }
	}
}


int WinTVCWindowHandler::wintv_process_code(int code)
{
	MWindow *mwindow = wintv->mwindow;
	EDL *edl = mwindow->edl;
	if( !edl ) return 0;
	PlayTransport *transport = mwindow->gui->mbuttons->transport;
	if( !transport->get_edl() ) return 0;
	PlaybackEngine *engine = transport->engine;
	double position = engine->get_tracking_position();
	double length = edl->tracks->total_length();
	int next_command = -1;

	switch( code ) {
	case WTV_OK:
		break;
// select window tile config = BACK 1,2,3
	case WTV_1: case WTV_2: case WTV_3:
		if( mwindow->wintv->last_code == WTV_BACK ) {
			RemoteGUI *rgui = mwindow->gui->cwindow_remote_handler->gui;
			rgui->tile_windows(code - WTV_1);
			return 1;
		} // fall thru
// select asset program config = TEXT 1,2,3,4,5,6
	case WTV_4: case WTV_5: case WTV_6:
		if( mwindow->wintv->last_code == WTV_TEXT ) {
			mwindow->select_asset(code - WTV_1, 1);
			break;
		} // fall thru
// select position in 10 percent units
	case WTV_7: case WTV_8: case WTV_9:
	case WTV_0:
		position = length * (code - WTV_0)/10.0;
		break;
// jump +- 10/60 secs
	case WTV_LT:  position -= 10.0;  break;
	case WTV_UP:  position += 60.0;  break;
	case WTV_RT:  position += 10.0;  break;
	case WTV_DN:  position -= 60.0;  break;
	case WTV_BACK: return 1;
	case WTV_HOME: {
		CWindowCanvas *canvas = mwindow->cwindow->gui->canvas;
		int on = canvas->get_fullscreen() ? 0 : 1;
		canvas->Canvas::set_fullscreen(on, 0);
		return 1; }
	case WTV_VOLUP: return 1;
	case WTV_VOLDN: return 1;
	case WTV_CH_UP: return 1;
	case WTV_CH_DN: return 1;
	case WTV_TEXT:  return 1;
	case WTV_CC:    return 1;
	case WTV_BOX:   return 1;
	case WTV_START: next_command = SLOW_REWIND;  break;
	case WTV_REV:   next_command = FAST_REWIND;  break;
	case WTV_STOP:  next_command = STOP;         break;
	case WTV_PLAY:  next_command = NORMAL_FWD;   break;
	case WTV_FWD:   next_command = FAST_FWD;     break;
	case WTV_END:   next_command = SLOW_FWD;     break;
	case WTV_MUTE:  return 1;
	default:
		printf("wintv cwindow: unknown code: %04x\n", code);
		return -1;
	}

	if( next_command < 0 ) {
		if( position < 0 ) position = 0;
		transport->change_position(position);
	}
	else
		transport->handle_transport(next_command);
	return 0;
}

int WinTVCWindowHandler::process_key(int key)
{
	return wintv_process_code(key);
}

int WinTVRecordHandler::process_key(int key)
{
	Record *record = wintv->mwindow->gui->record;
	return record->wintv_process_code(key);
}


WinTVRecordHandler::WinTVRecordHandler(WinTV *wintv, RemoteControl *remote_control)
 : RemoteHandler(remote_control->gui, GREEN)
{
	this->wintv = wintv;
}

WinTVCWindowHandler::WinTVCWindowHandler(WinTV *wintv, RemoteControl *remote_control)
 : RemoteHandler(remote_control->gui, BLUE)
{
	this->wintv = wintv;
}

// HAVE_WINTV
#endif
