#include <linux/dvb/frontend.h>
#include <linux/dvb/dmx.h>
#include <linux/dvb/audio.h>
#include <linux/dvb/version.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

struct channel {
	int id;
	const char *name;
	unsigned int frequency;
	unsigned int ts_id;
};
static int search(int adapter_nr,struct channel *ch)
{
	char file[256];
	int fd;
	struct dvb_frontend_info info;
	struct channel *channel;
	struct dtv_property prop[3];
	struct dtv_properties props;
	int i;
	fe_status_t status;

	sprintf(file, "/dev/dvb/adapter%d/frontend0", adapter_nr);
	if ((fd = open(file, (O_RDWR | O_CREAT | O_TRUNC), 0666)) < 0) {
		perror("open");
		return -1;
	}

	if (ioctl(fd, FE_GET_INFO, &info) < 0) {
		perror("ioctl FE_GET_INFO");
		goto out;
	}

	if (info.type == FE_QPSK) {
		channel = ch;
		//channel = lookup_channel(channel_id, isdbs_channels,
		//			 ARRAY_SIZE(isdbs_channels));
	} else if (info.type == FE_OFDM) {
		channel = ch;
		//channel = lookup_channel(channel_id, isdbt_channels,
		//			 ARRAY_SIZE(isdbt_channels));
	} else {
		fprintf(stderr, "Unknown type of adapter\n");
		goto out;
	}
	if (channel == NULL) {
		fprintf(stderr, "Unknown id of channel\n");
		goto out;
	}

	prop[0].cmd = DTV_FREQUENCY;
	prop[0].u.data = channel->frequency;
	prop[1].cmd = DTV_ISDBS_TS_ID;
	prop[1].u.data = channel->ts_id;
	prop[2].cmd = DTV_TUNE;

	props.props = prop;
	props.num = 3;

	if ((ioctl(fd, FE_SET_PROPERTY, &props)) < 0) {
		perror("ioctl FE_SET_PROPERTY");
		goto out;
	}

	for (i = 0; i < 4; i++) {
		if (ioctl(fd, FE_READ_STATUS, &status) < 0) {
			perror("ioctl FE_READ_STATUS");
			goto out;
		}
		if (status & FE_HAS_LOCK) {
			fprintf(stderr, "Successfully tuned to %d(%d) .\n",
				channel->frequency,channel->ts_id);
			return 0;
		}
		usleep(250 * 1000);
	}

	fprintf(stderr, "Failed to tune to %s (status %02x).\n",
		channel->name, status);

out:
	close(fd);
	return -1;
}

static int track(int adapter_nr)
{
	char file[256];
	int fd;
	struct dmx_pes_filter_params filter;

	filter.pid = 0x2000;
	filter.input = DMX_IN_FRONTEND;
	filter.output = DMX_OUT_TS_TAP;
	filter.pes_type =  DMX_PES_VIDEO;
	filter.flags = DMX_IMMEDIATE_START;

	sprintf(file, "/dev/dvb/adapter%d/demux0", adapter_nr);
	if ((fd = open(file, O_RDWR)) < 0) {
		perror("open");
		return -1;
	}


	if (ioctl(fd, DMX_SET_PES_FILTER, &filter) < 0) {
		perror("ioctl DMX_SET_PES_FILTER");
		close(fd);
		return -1;
	}
}

void record(int adapter_nr, char* output, int rectime) {
	int fin, fout;
	char input[256];
	time_t start_time, current_time;
	char buf[1316];
	ssize_t rt, wt;
	int size_remain;

	fout = open(output, (O_WRONLY | O_CREAT | O_TRUNC));
	if ( fout < 0 ) {
		printf("output file open failed\n");
		return;
	}
	sprintf(input, "/dev/dvb/adapter%d/dvr0", adapter_nr);
	start_time = time(NULL);
	fin = open(input, (O_RDONLY));
	while ( rt = read(fin, buf, 1316) ) {
		while ( wt = write(fout, buf, rt) ) {
			rt -= wt;
			if ( rt == 0 ) break;
			if ( wt == 0 ) {
				printf("output file write failed\n");
				goto error;
			}
		}
		current_time = time(NULL);
		if ( current_time - start_time > rectime ) {
			break;
		}
	}

	error:
	close(fin);
	close(fout);
}

int main(int argc, char *argv[]) {
	int adapter_nr;
	int channel_id;
	int channel_freq;
	int fd;
	int ret;
	int rectime;

	if (argc <= 2) {
		fprintf(stderr, "Usage: %s adapter_nr freq [tsid]\n", argv[0]);
		return 1;
	}
	struct channel *ch;
	adapter_nr = strtol(argv[1], NULL, 0);
	channel_freq = strtol(argv[2], NULL, 10);
	channel_id=0;
	if (argc >= 4){
		channel_id = strtol(argv[3], NULL, 10);
	}
	if ( argc >= 5 ) {
		rectime = atoi(argv[4]);
	}
	struct channel ch1 ={0,"",channel_freq,channel_id};
	ch = &ch1;
	//struct channel ch;//{   1, "NHK BS-1",          1318000, 0x40f1 }
	//ch={0,"",argv}
	fd = search(adapter_nr, ch);
	if (fd < 0)
		return 1;

	ret = track(adapter_nr);
	record(adapter_nr, argv[5], rectime);
	close(fd);

	return ret < 0;
}
