#include "../globals.h"

#ifdef CARDREADER_DB2COM
#include "../oscam-time.h"
#include "icc_async.h"
#include "ifd_phoenix.h"
#include "io_serial.h"

// Multicam defines
#define DEV_MULTICAM       "/dev/multicam"
#define MULTICAM_GET_PCDAT 10
#define MULTICAM_SET_PCDAT 13

#define OK 0
#define ERROR 1

bool detect_db2com_reader(struct s_reader *reader)
{
	struct stat sb;
	if(stat(DEV_MULTICAM, &sb) == -1)
		{ return false; }
	if(stat(reader->device, &sb) == 0)
	{
		if(S_ISCHR(sb.st_mode))
		{
			int32_t dev_major = major(sb.st_rdev);
			int32_t dev_minor = minor(sb.st_rdev);
			if(dev_major == 4 || dev_major == 5)
			{
				int32_t rc;
				switch(dev_minor & 0x3F)
				{
				case 0:
					rc = R_DB2COM1;
					break;
				case 1:
					rc = R_DB2COM2;
					break;
				default:
					return false;
				}
				reader->typ = rc;
			}
			rdr_log_dbg(reader, D_READER, "device is major: %d, minor: %d, typ=%d", dev_major, dev_minor, reader->typ);
		}
	}
	return true;
}

static int32_t db2com_init(struct s_reader *reader)
{
	if(reader->typ != R_DB2COM1 && reader->typ != R_DB2COM2)
		{ detect_db2com_reader(reader); }

	reader->handle = open(reader->device,  O_RDWR | O_NOCTTY | O_SYNC);
	if(reader->handle < 0)
	{
		rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)", reader->device, errno, strerror(errno));
		return ERROR;
	}
	if((reader->fdmc = open(DEV_MULTICAM, O_RDWR)) < 0)
	{
		rdr_log(reader, "ERROR: Opening device %s (errno=%d %s)", DEV_MULTICAM, errno, strerror(errno));
		close(reader->handle);
		return ERROR;
	}
	if(Phoenix_Init(reader))
	{
		rdr_log(reader, "ERROR: Phoenix_Init returns error");
		Phoenix_Close(reader);
		return ERROR;
	}
	return OK;
}

static int32_t db2com_get_status(struct s_reader *reader, int32_t *status)
{
	*status = 0;
	uint16_t msr = 1;
	IO_Serial_Ioctl_Lock(reader, 1);
	ioctl(reader->fdmc, MULTICAM_GET_PCDAT, &msr);
	if(reader->typ == R_DB2COM2)
		{ *status = !(msr & 1); }
	else
		{ *status = (msr & 0x0f00) == 0x0f00; }
	IO_Serial_Ioctl_Lock(reader, 0);
	return OK;
}

static bool db2com_DTR_RTS(struct s_reader *reader, int32_t *dtr, int32_t *rts)
{
	int32_t rc;
	uint16_t msr;
	uint16_t rts_bits[2] = { 0x10, 0x800 };
	uint16_t dtr_bits[2] = {0x100,     0 };
	int32_t mcport = reader->typ == R_DB2COM2;

	rc = ioctl(reader->fdmc, MULTICAM_GET_PCDAT, &msr);
	if(rc < 0)
		{ return ERROR; }

	if(dtr)
	{
		rdr_log_dbg(reader, D_DEVICE, "%s DTR:%s", __func__, *dtr ? "set" : "clear");
		if(dtr_bits[mcport])
		{
			if(*dtr)
				{ msr &= (uint16_t)(~dtr_bits[mcport]); }
			else
				{ msr |= dtr_bits[mcport]; }
			rc = ioctl(reader->fdmc, MULTICAM_SET_PCDAT, &msr);
		}
		else
		{
			rc = 0; // Dummy, can't handle using multicam.o
		}
	}

	if(rts)
	{
		rdr_log_dbg(reader, D_DEVICE, "%s RTS:%s", __func__, *rts ? "set" : "clear");
		if(*rts)
			{ msr &= (uint16_t)(~rts_bits[mcport]); }
		else
			{ msr |= rts_bits[mcport]; }
		rc = ioctl(reader->fdmc, MULTICAM_SET_PCDAT, &msr);
	}

	if(rc < 0)
		{ return ERROR; }
	return OK;
}

const struct s_cardreader cardreader_db2com =
{
	.desc          = "db2com",
	.typ           = R_DB2COM1,
	.flush         = 1,
	.need_inverse  = 1,
	.read_written  = 1,
	.reader_init   = db2com_init,
	.get_status    = db2com_get_status,
	.activate      = Phoenix_Reset,
	.transmit      = IO_Serial_Transmit,
	.receive       = IO_Serial_Receive,
	.close         = Phoenix_Close,
	.set_parity    = IO_Serial_SetParity,
	.set_baudrate  = IO_Serial_SetBaudrate,
	.set_DTS_RTS   = db2com_DTR_RTS,
};

#endif
