#include "Sockets.h"
#include "SocketPort.h"
#include "BluePad.h"
#include "stdTools.h"

#include <sstream>

//---------------------------------------------------------------------------
THSocket::THSocket() {
	parent = NULL;
	id = -1;
	sp = NULL;
	m_exe_thread_read = true;
	m_threadid_read = NULL;
	m_exe_thread_write = true;
	m_threadid_write = NULL;


	length_write = 0;
	memset(data_write, 0, sizeof(data_write));

	len_read = 0;
	memset(data_read, 0, sizeof(data_read));
};
//---------------------------------------------------------------------------
THSocket::~THSocket() {

	//Останавливаю потоки и дожидаюсь их завершения
	m_exe_thread_read = false;
	m_threadid_read->join();
	delete m_threadid_read;

	Sleep(10);
	m_exe_thread_write = false;
	Sleep(10);
	m_threadid_write->join();
	delete m_threadid_write;

	//Закрываю и удаляю сокет 
	sp->Close();
	delete sp;
	sp = NULL;

	id = -1;
}
//---------------------------------------------------------------------------
//Для чтения данных из сокетов
void THSocket::thread_func_read(void *pData)
{
	THSocket* sock = (THSocket*)pData;

	char data[65500];
	while (sock->m_exe_thread_read) { //Поток намертво не должен тормозиться иначе из него не выйдет  (функция Read должна быть с таймаутом)

		int size = sock->sp->Read(data, sizeof(data));
		//Если удалось что либо прочитать то переписываем м массив
		if (size > 0) {

			sock->parent->m_mutexList.lock();
			for (int i = 0; i < size; i++) {
				if (sock->len_read + i >= sizeof(sock->data_read))
					break;
				sock->data_read[sock->len_read + i] = data[i];
			}
			sock->len_read += size;
			sock->parent->m_mutexList.unlock();

			std::stringstream ss;
			ss << "--------------------------------------------------Read from soket = " << size;
			Utility::logrotate(sock->m_LogFilePath + Utility::sp3() + "log.txt", ss.str()); //TODO закоментить как отлажу
		}
	}
	//TODO закоментить как отлажу
	if (sock->m_SaveLog) {
		std::stringstream ss;
		ss << "========== END SOCK READ THREAD ID = " << sock->id << " ==========";
		Utility::logrotate(sock->m_LogFilePath + Utility::sp3() + "log.txt", ss.str());
	}
}
//---------------------------------------------------------------------------
//Поток для записи данных в сокет
void THSocket::thread_func_write(void *pData)
{
	THSocket* sock = (THSocket*)pData;
	char data[65535];
	int len = 0;
	while (sock->m_exe_thread_write) {

		//Переписываю данные для отправки на сервер в локальную переменную
		sock->parent->m_mutexList.lock();
		for (int i = 0; i < sock->length_write; i++) {
			data[i] = sock->data_write[i];
		}
		len = sock->length_write;
		sock->parent->m_mutexList.unlock();

		if (len > 0) {
			//Отправляю данные в сокет
			int size = sock->sp->Write(data, len);

			//Сдвигаю данные на количество отправленных данных
			sock->parent->m_mutexList.lock();
			for (int i = 0; i < sock->length_write - size; i++) {
				sock->data_write[i] = sock->data_write[i + size];
			}
			sock->length_write -= size;
			sock->parent->m_mutexList.unlock();

			std::stringstream ss;
			ss << "--------------------------------------------------Write to soket = " << size << " from = " << len;
			Utility::logrotate(sock->m_LogFilePath + Utility::sp3() + "log.txt", ss.str()); //TODO закоментить как отлажу
		}
	}
	//TODO закоментить как отлажу
	if (sock->m_SaveLog) {
		std::stringstream ss;
		ss << "========== END SOCK WRITE THREAD ID = " << sock->id << " ==========";
		Utility::logrotate(sock->m_LogFilePath + Utility::sp3() + "log.txt", ss.str());
	}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
Sockets::Sockets(BluePad* bp)
{
	m_bp = bp;
	m_LogFilePath = m_bp->m_LogFilePath;

	m_list = new TSimpleList<THSocket*>(10, true); //Делаю владельцем чтобы список сам объекты удалял
};
//---------------------------------------------------------------------------
Sockets::~Sockets()
{
	delete m_list;
};
//---------------------------------------------------------------------------
//создать новый сокет и запустить потоки на чтение и зипись
//timeout - таймаут в секундах (только на что?)
bool Sockets::openSoket(int id, std::string address, int port, int timeout)
{
	bool result = true;

	SocketPort* sp = new SocketPort();
	sp->Open(address.c_str(), port);
	if (sp->isOpen()) {

		//Добавляем созданный объект в массив
		THSocket* ths = new THSocket();
		ths->m_LogFilePath = m_LogFilePath;
		ths->id = id;
		ths->sp = sp;
		ths->parent = this;
		ths->m_threadid_read = new std::thread(THSocket::thread_func_read, (void*)ths);
		ths->m_threadid_write = new std::thread(THSocket::thread_func_write, (void*)ths);

		m_mutexList.lock();
		m_list->add(ths);
		m_mutexList.unlock();


		std::stringstream ss;
		ss << "========== OPEN SOCK THREAD ID = " << id << " ==========";
		Utility::logrotate(m_LogFilePath + Utility::sp3() + "log.txt", ss.str());
	}
	else {
		result = false;
		delete sp;
	}
	return result;
}
//---------------------------------------------------------------------------
//Закрыть удалить сокет и остановить связанные с ним потоки
bool Sockets::closeSoket(int id) {
	TSimpleList<THSocket*>* list = new TSimpleList<THSocket*>(10,true);

	m_mutexList.lock();
	for (int i = 0; i<m_list->count(); i++) {
		if (m_list->get(i)->id == id) {
			list->add(m_list->get(i));
			m_list->rem(i);
			i--;
			// break не ставить, могут быть одинаковые id
		}
	}
	m_mutexList.unlock();

	delete list; //Не в мютексе а то повиснет из-за join при удалении

	return true;
}
//---------------------------------------------------------------------------
// Записать данные для последующей отправки в отдельном потоке
bool Sockets::sendData(int id, char* data, int len) {

	std::lock_guard<std::mutex> lock(m_mutexList);

	for (int i = 0; i<m_list->count(); i++) {
		if (m_list->get(i)->id == id) {

			THSocket* ths = m_list->get(i);

			//Дописываем данные в конец массива для последующей отправки на сервер
			for (int i = 0; i < len; i++) {
				if (ths->length_write + i >= sizeof(ths->data_write))
					break;
				ths->data_write[ths->length_write + i] = data[i];
			}
			ths->length_write += len;
			
			break;
		}
	}

	return true;
}
//---------------------------------------------------------------------------
//Перебираем массив и проверяем есть ли данные для отправки и если есть то создаём объект данных который будет отправлен через последовательный порт в отдельном потоке
DataToSend* Sockets::getDataToSend() {

	std::lock_guard<std::mutex> lock(m_mutexList);

	DataToSend* result = NULL;

	for (int i = 0; i<m_list->count(); i++) {
		if (m_list->get(i)->len_read > 0) {

			m_list->get(i)->len_read += 1;

			result = new DataToSend();
			//Подготавливаю пакет 01: RECEIVE DATA для отправки полученных даных с сервера на пинпад
			result->mas[0] = 0x3E; // '>': start paket (ASCII symbol '>')
			result->mas[1] = 0x40; // EXTERNAL INTERNET COMMANDS
			result->mas[2] = 0x00; // 00
			result->mas[3] = ((char*)&m_list->get(i)->len_read)[1]; //LH length of DATA (старший наиболее значащий байт)
			result->mas[4] = ((char*)&m_list->get(i)->len_read)[0]; //LL length of DATA
			result->mas[5] = 0x01; //SUBCMD: 01: RECEIVE DATA 
			for (int j = 0; j < m_list->get(i)->len_read; j++) {
				result->mas[6+j] = m_list->get(i)->data_read[j];
			}
			result->len = 6 + m_list->get(i)->len_read;
			result->calcCRC();
			result->log_text = "getDataToSend()";
				
			m_list->get(i)->len_read = 0;
			break;
		}
	}
	
	return result;
}
//---------------------------------------------------------------------------
