/***************************************************************
 * Name:      BarCode.cpp
 * Purpose:   Code for Application Frame
 * Author:    Igor I (info@tiptopcity.com)
 * Created:   2010-12-01
 * Copyright: Igor I (www.tiptopcity.com)
 * License:   Commercial
 **************************************************************/

//#include "stdafx.h"
#include "BarCode.h"
//#include <wx/dcsvg.h>

/*
#include <api/setupapi.h>
extern "C"
{
  #include <api/hidsdi.h>
}
/**/
//#include "hidsdi.h" //Это если считыватель штрих кодов как HID устройство windows в папке C:\WinDDK\7600.16385.1\inc\

//------------------------------------------------------------------------------
//TSimpleList<TCheckData*>* g_BarCode=NULL; 
BarCode* g_BarCode=NULL;
//------------------------------------------------------------------------------
BarCode::BarCode()
{
	m_id=0;
	bString = true;

	m_crit = new wxCriticalSection(); //Для синхронизации
	m_ListD = new TSimpleList<unsigned char*>(5,false);

	m_critW = new wxCriticalSection(); //Для синхронизации записи в поток
	m_ListW = new TSimpleList<SData*>(5,false); //Список пакетов которых надо записать в поток

	m_ListL = new TSimpleList<CharStringListener*>(5,false);

	cPort=new ComPort();
	
	if(g_BarCode!=NULL) g_BarCode->Disconnect();
	g_BarCode=this;
}
//------------------------------------------------------------------------------
BarCode::~BarCode()
{
	//Следим за глобальным объектом.
	if(g_BarCode==this) g_BarCode=NULL;
	/*if(g_BarCode!=NULL && g_BarCode->count()==0)
	{	delete g_BarCode;
		g_BarCode=NULL;
	}*/

	//Чистим и удаляем список для записи с поток
	for(unsigned int i=0;i<m_ListD->count();i++)
	{	delete m_ListW->get(i)->data;
		delete m_ListW->get(i);
	}
	delete m_ListW;

	for(unsigned int i=0;i<m_ListD->count();i++) delete m_ListD->get(i); //Не востребованные удаляем если есть
	delete m_ListD; m_ListD=NULL;
	delete m_ListL; m_ListL=NULL;
	delete cPort; cPort=NULL;
	delete m_crit; m_crit=NULL;
	delete m_ListW; m_ListW=NULL;
}
//------------------------------------------------------------------------------
//Добавить слушателя выбора
void BarCode::AddListener(CharStringListener* listener) 
{	//m_crit->Enter();
	if(m_ListL!=NULL && listener!=NULL)
		m_ListL->add(listener);
	//m_crit->Leave();
};
//------------------------------------------------------------------------------
//Удалить слкшателя выбора
void BarCode::RemListener(CharStringListener* listener) 
{	//m_crit->Enter();
	if(m_ListL!=NULL && listener!=NULL)
		m_ListL->del(listener);
	//m_crit->Leave();
};
//------------------------------------------------------------------------------
bool BarCode::Connect(int port,int speed)
{
	cPort->BaudRate=speed;
	if (cPort->Open(port))
	{	
		if(cPort->Setup(0))
		{
			if(Create()==wxTHREAD_NO_ERROR)
			{	Run();
				return true;
			}
		}
	}
	return false;
}
//------------------------------------------------------------------------------
///<Отсоедениться от порта и удалить поток
bool BarCode::Disconnect()
{	return cPort->Close();
}
//------------------------------------------------------------------------------
//Функция - поток служит для приёма и отправкм данных
void* BarCode::Entry()
{
	unsigned char* ch=new unsigned char[255]; //Буфер
	unsigned int pos=0; //Позиция в буфере
	while(cPort->bOpen)
	{
		if(bString)
		{
			//Читаем побайтно до заданного признака
			if(cPort->Read(&ch[pos],1)>0)
			{
				if(ch[pos]==13 || ch[pos]==10 || pos==254)
				{	if(pos>0)
					{
						ch[pos]=0; //Ставим признак конца строки
						m_ListD->add(ch);
						ch=new unsigned char[255];
						pos=0;
					}
				}else
					pos++;
			}
		}else
		{
			//Как прочитали 1 байт так с разу и добавляем в список событий
			if(cPort->Read(ch,1)>0)
			{
				ch[1]=0; //Ставим признак конца строки
				m_ListD->add(ch);
				ch=new unsigned char[255];
				pos=0;
			}
		}

		//Если есть данные для отправки отправляем их в поток
		m_critW->Enter();
		for(unsigned int i=0;i<m_ListW->count();i++)
		{
			if(m_ListW->get(i)->state==0) //Те которые ждут отправки
			{
				m_ListW->get(i)->sizeW = cPort->Write(m_ListW->get(i)->data,m_ListW->get(i)->size);
				m_ListW->get(i)->state = m_ListW->get(i)->sizeW == m_ListW->get(i)->size ? 2 : 3; // 2 - ok, 3 - error (Нет состоояния 1 так как отправляем блоком)
			}
		}
		m_critW->Leave();

		//wxThread::Sleep(100); //Расскоментировать если полностью в асинхронном режиме ком порт
	}
	delete ch;
	return NULL;
}
//------------------------------------------------------------------------------
//Работает в основном потоке для проверки прихода данных (сам не вызвается надо из внешней проги проверять)
bool BarCode::OnTimer()
{
	if(m_ListD==NULL || m_ListL==NULL) return false;
	unsigned char *ch;
	unsigned char **p; //Указатель на указатель чтоб слушатели могли забрать себе.
	p=&ch;
	do
	{	ch=NULL;
		m_crit->Enter();
		if(m_ListD->count()>0)
		{	ch=m_ListD->get(0);
			m_ListD->del((unsigned int)0); //Удаляем из списка
		}
		m_crit->Leave();
		//Информируем слушателей о новой таблице
		if(ch!=NULL)
		{	m_crit->Enter();
			for(unsigned int i=0;i<m_ListL->count();i++)
			{
				m_ListL->get(i)->OnDataIncoming(p);
				if(ch!=NULL) break; //Кто-то воспользовался...
			}
			m_crit->Leave();
			//Если никому не понадобилась удаляем таблицу
			if(ch!=NULL) {delete ch; ch=NULL;}
		}
	}
	while(ch!=NULL);
	return true;
}
//------------------------------------------------------------------------------
//Синхронно записать данные в поток
unsigned long BarCode::WriteS(const void* lpBuffer,unsigned long nNumberOfBytesToWrite)
{
	int id=GetNextId();

	SData* d=new SData;
	d->id=id;
	d->sizeW=0;
	d->state=0;
	d->data=new char[nNumberOfBytesToWrite];

	memcpy(d->data,lpBuffer,nNumberOfBytesToWrite); //Копируем данные (TODO можно и не копировать так как здесь эмуляция синхронности)
	d->size=nNumberOfBytesToWrite;

	m_critW->Enter();
	m_ListW->add(d);
	m_critW->Leave();

	//Ждём ответ то что все данные записанны в порт тоесть в пакете поменялся статус
	unsigned long size=0;
	for(int i=0;i<600;i++) //Макс минуту ждём статус...
	{
		SData* rez=NULL;
		m_critW->Enter();
		for(unsigned int j=0;j<m_ListW->count();j++)
		{
			if(m_ListW->get(j)->id==id && (m_ListW->get(j)->state==2 || m_ListW->get(j)->state==3))
			{
				rez=m_ListW->get(j);
				m_ListW->del(j);
				break;
			}
		}
		m_critW->Leave();
		if(rez!=NULL)
		{
			size=rez->sizeW;
			delete rez->data;
			delete rez;
			break;
		}
		wxThread::Sleep(100);
	}
	return size;
}
//------------------------------------------------------------------------------