/*
 * Copyright (c) 2017 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials provided
 *    with the distribution.
 *  * Neither the name of The Linux Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <iostream>
#include "InterfaceAbstraction.h"

#define MAX_OPEN_RETRY 10000

using std::cout;
using std::endl;

bool InterfaceAbstraction::Open(const char * toIPAPath, const char * fromIPAPath)
{
	int tries_cnt = MAX_OPEN_RETRY;
	if (NULL == toIPAPath && NULL == fromIPAPath)
	{
		printf("InterfaceAbstraction constructor got 2 null arguments.\n");
		exit(0);
	}

	if (NULL != toIPAPath) {
		while (tries_cnt > 0) {
			printf("trying to open %s %d/%d\n", toIPAPath, MAX_OPEN_RETRY - tries_cnt, MAX_OPEN_RETRY);
			// Sleep for 5 msec
			usleep(5000);
			m_toIPADescriptor = open(toIPAPath, O_WRONLY);
			if (-1 != m_toIPADescriptor) {
				printf("Success!\n");
				break;
			}
			tries_cnt--;
		}
		printf("open retries_cnt=%d\n", MAX_OPEN_RETRY - tries_cnt);
		if (-1 == m_toIPADescriptor) {
			printf("InterfaceAbstraction failed while opening %s.\n", toIPAPath);
			exit(0);
		}
		m_toChannelName = toIPAPath;
		printf("%s device node opened, fd = %d.\n", toIPAPath, m_toIPADescriptor);
	}
	tries_cnt = MAX_OPEN_RETRY;
	if (NULL != fromIPAPath) {
		while (tries_cnt > 0) {
			printf("trying to open %s %d/%d\n", fromIPAPath, MAX_OPEN_RETRY - tries_cnt, MAX_OPEN_RETRY);
			// Sleep for 5 msec
			usleep(5000);
			m_fromIPADescriptor = open(fromIPAPath, O_RDONLY);
			if (-1 != m_fromIPADescriptor) {
				printf("Success!\n");
				break;
			}
			tries_cnt--;
		}
		printf("open retries_cnt=%d\n", MAX_OPEN_RETRY - tries_cnt);
		if (-1 == m_fromIPADescriptor)
		{
			printf("InterfaceAbstraction failed on opening %s.\n", fromIPAPath);
			exit(0);
		}
		m_fromChannelName = fromIPAPath;
		printf("%s device node opened, fd = %d.\n", fromIPAPath, m_fromIPADescriptor);
	}

	return true;
}/*Ctor*/

void InterfaceAbstraction::Close()
{
	close(m_toIPADescriptor);
	close(m_fromIPADescriptor);
}

long InterfaceAbstraction::SendData(unsigned char *buf, size_t size)
{
	long bytesWritten = 0;

	printf("Trying to write %zu bytes to %d.\n", size, m_toIPADescriptor);

	bytesWritten = write(m_toIPADescriptor, buf, size);
	if (-1 == bytesWritten)
	{
		int err = errno;
		printf(
		"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
		printf(
		"Failed to execute command\n write(m_toIPADescriptor, buf, size);\n "
		"m_toIPADescriptor=%d, buf=0x%p, size=%zu",m_toIPADescriptor,
		buf,
		size);
		printf("Error on write execution, errno=%d, Quitting!\n", err);
		printf(
		"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
		exit(-1);
	}

	cout << "bytesWritten = " << bytesWritten << endl;

	return bytesWritten;
}

int InterfaceAbstraction::ReceiveData(unsigned char *buf, size_t size)
{
	size_t bytesRead = 0;
	size_t totalBytesRead = 0;
	bool continueRead = false;

	do
	{
		printf("Trying to read %zu bytes from %d.\n", size, m_fromIPADescriptor);

		bytesRead = read(m_fromIPADescriptor, (void*)buf, size);
		printf("Read %zu bytes.\n", bytesRead);
		totalBytesRead += bytesRead;
		if (bytesRead == size)
			continueRead = true;
		else
			continueRead = false;
	} while (continueRead);

	return totalBytesRead;
}

int InterfaceAbstraction::ReceiveSingleDataChunk(unsigned char *buf, size_t size){
	size_t bytesRead = 0;
	printf("Trying to read %zu bytes from %d.\n", size, m_fromIPADescriptor);
	bytesRead = read(m_fromIPADescriptor, (void*)buf, size);
	printf("Read %zu bytes.\n", bytesRead);
	return bytesRead;
}

int InterfaceAbstraction::setReadNoBlock(){
	int flags = fcntl(m_fromIPADescriptor, F_GETFL, 0);
	if(flags == -1){
		return -1;
	}
	return fcntl(m_fromIPADescriptor, F_SETFL, flags | O_NONBLOCK);
}

int InterfaceAbstraction::clearReadNoBlock(){
	int flags = fcntl(m_fromIPADescriptor, F_GETFL, 0);
	if(flags == -1){
		return -1;
	}
	return fcntl(m_fromIPADescriptor, F_SETFL, flags & ~O_NONBLOCK);
}

InterfaceAbstraction::~InterfaceAbstraction()
{
	close(m_fromIPADescriptor);
	m_fromChannelName = "";

	close(m_toIPADescriptor);
	m_toChannelName = "";
}