#include "stdafx.h"
#include "WaveParserProto.h"
#include "SettingsDlg.h"

#include "globals.h"
#include "BKTapeReaderDlg.h"

bool CWaveParserProto::ParseWaveFile(TAPE_FILE_INFO *pTfi, WaveData &pWave, CString &name, CString strID)
{
	ASSERT(m_pParent);
	m_nPos = 0;     //начнём сначала
	m_dPrevV = 1.0; //предыдущее значение
	CalcAverage(pWave);
	m_bInverse = false;
	memset(pTfi, 255, sizeof(TAPE_FILE_INFO));
	int nLength = 0;
	m_pParent->winprintf(_T("---------------------------------------[%s]\r\n"), strID);
	m_pParent->winprintf(_T("Распознаём файл: %s\r\nНастроечная последовательность: "), name);

	//ищем настроечную последовательность в начале файла
	if (!FindTuning(true, pWave, 1024, pTfi->start_tuning, nLength))
	{
		m_pParent->winprintf(_T("не найдена.\r\n"));
		return false;
	}

	pTfi->synchro_start = m_nPos;
	m_pParent->winprintf(_T("начало: %d длина: %d\r\n"), pTfi->start_tuning, nLength);
	m_pParent->winprintf(_T("Поиск первого маркера с %d : "), pTfi->synchro_start);

	//ищем маркер
	if (!FindMarker1(pWave))
	{
		m_pParent->winprintf(_T("не найден. Тек.позиция %d\r\n"), m_nPos);
		return false;
	}

	m_pParent->winprintf(_T("найден. Тек.позиция %d\r\n"), m_nPos);

	if (m_bInverse)
	{
		m_pParent->winprintf(_T("Сигнал опознан как ИНВЕРСНЫЙ!\r\n"));
	}

	m_pParent->winprintf(_T("Поиск второго маркера.\r\nСинхро: "));

	//ищем настроечную последовательность в начале заголовка файла
	if (!FindTuning(true, pWave, 7, pTfi->marker1, nLength))
	{
		m_pParent->winprintf(_T("не найдена.\r\n"));
		return false;
	}

	pTfi->synchro_header = m_nPos;
	m_pParent->winprintf(_T("начало: %d длина: %d. Тек.позиция %d\r\n"), pTfi->marker1, nLength, m_nPos);
	m_pParent->winprintf(_T("Второй маркер: "));

	//ищем маркер
	if (!FindMarker(pWave))
	{
		m_pParent->winprintf(_T("не найден.\r\n"));
		return false;
	}

	pTfi->header = m_nPos;
	m_pParent->winprintf(_T("найден. Заголовок с %d\r\n"), pTfi->header);
	m_pParent->winprintf(_T("Чтение заголовка: "));

	//читаем заголовок файла
	for (int i = 0; i < 20; ++i)
	{
		if (!ReadByte(pWave, ((uint8_t *)&pTfi->address)[i]))
		{
			m_pParent->winprintf(_T("Ошибка. Тек.позиция %d\r\n"), m_nPos);
			return false;
		}
	}

	m_pParent->winprintf(_T("Успешно\r\n"));
	m_pParent->winprintf(_T("Содержимое заголовка:\r\n Адрес: %06o\r\n Длина: %06o\r\n"), pTfi->address, pTfi->length);
	CString BKName = BKToUNICODE(pTfi->name, 16);
	m_pParent->winprintf(_T(" Имя: \"%s\"\r\n"), BKName);
	m_pParent->winprintf(_T("Поиск третьего маркера.\r\nСинхро: "));

	//ищем настроечную последовательность в начале массива данных файла
	if (!FindTuning(true, pWave, 7, pTfi->marker2, nLength))
	{
		m_pParent->winprintf(_T("не найдена.\r\n"));
		return false;
	}

	pTfi->synchro_data = m_nPos;
	m_pParent->winprintf(_T("начало: %d длина: %d. Тек.позиция %d\r\n"), pTfi->marker2, nLength, m_nPos);
	m_pParent->winprintf(_T("Третий маркер: "));

	//ищем маркер
	if (!FindMarker(pWave))
	{
		m_pParent->winprintf(_T("не найден.\r\n"));
		return false;
	}

	pTfi->data = m_nPos;
	m_pParent->winprintf(_T("найден. Данные с %d\r\n"), pTfi->data);

	if (pTfi->length)
	{
		SAFE_DELETE_ARRAY(m_pBin);
		m_pBin = new uint8_t[pTfi->length];

		if (!m_pBin)
		{
			return false;
		}
	}
	else
	{
		m_pParent->winprintf(_T("Ошибка в заголовке - нулевая длина.\r\n"));
		return false;
	}

	m_pParent->winprintf(_T("Чтение данных: "));

	//читаем данные
	for (int i = 0; i < pTfi->length; ++i)
	{
		if (!ReadByte(pWave, m_pBin[i]))
		{
			m_pParent->winprintf(_T("Ошибка. Тек.позиция %d\r\n"), m_nPos);
			return false;
		}
	}

	m_pParent->winprintf(_T("Успешно.\r\n"));
	m_pParent->winprintf(_T("Чтение контрольной суммы: "));

	//читаем контрольную сумму
	if (!ReadByte(pWave, ((uint8_t *)&pTfi->crc)[0]))
	{
		m_pParent->winprintf(_T("Ошибка. Тек.позиция %d\r\n"), m_nPos);
		return false;
	}

	if (!ReadByte(pWave, ((uint8_t *)&pTfi->crc)[1]))
	{
		m_pParent->winprintf(_T("Ошибка. Тек.позиция %d\r\n"), m_nPos);
		return false;
	}

	m_pParent->winprintf(_T("Успешно. КС=%06o\r\n"), pTfi->crc);
	pTfi->synchro6 = m_nPos;

	//ищем финишную последовательность
	if (FindSyncro6(pWave))
	{
		m_pParent->winprintf(_T("Найдена финишная последовательность с %d\r\n"), m_nPos);
		FindTuning(true, pWave, 128, pTfi->end_tuning, nLength);
		pTfi->end = pTfi->end_tuning + nLength;
	}
	else
	{
		//её может и не быть в нестандартных случаях
		pTfi->end = DWORD(pWave.size());
	}

	m_pParent->winprintf(_T("Конец распознавания.\r\n"));
	return true;
}

//возвращаем длину импульса в средних импульсах
// 0 - очень короткий импульс (неправильный или ложный)
// 1 - "0" и синхроимпульс, их длительность == L (длина среднего импульса)
// 2 - "1", его длительность == 2L
// 4 - маркер, его длительность  == 4L
// 8 - очень длинный импульс (или финиш, или неправильный)
int CWaveParserProto::DefineLength(int nLength)
{
	double l = double(nLength) / m_dAvgLength;

	if (l < MinusDelta(1.0))
	{
		return 0;
	}

	if (l < PlusDelta(1.0))
	{
		return 1;
	}

	if (l < PlusDelta(2.0)) //упрощённые интервалы, без разрывов между допусками.
	{
		return 2;
	}

	if (l < PlusDelta(4.0))
	{
		return 4;
	}

	return 8;
}

void CWaveParserProto::CalcAverage(WaveData &pWave)
{
	m_nAverage = 0; //посчитаем средний уровень

	for (auto &i : pWave)
	{
		m_nAverage += i;
	}

	m_nAverage /= pWave.size();
}

double CWaveParserProto::MinusDelta(double x)
{
	return x - x * g_Settings.dDeltaImp;
}

double CWaveParserProto::PlusDelta(double x)
{
	return x + x * g_Settings.dDeltaImp;
}

/*
* Вход: nLength - ориентировочная длина искомой последовательности
* Выход: wave_pos - начало найденной последовательности
*        wave_length - длина найденной последовательности
*/
bool CWaveParserProto::FindTuning(bool bRollback, WaveData &pWave, int nLength, DWORD &wave_pos, int &wave_length)
{
	m_dAvgLength = 0;
	bool bFound = false;
	int syncro_pos = CWaveParserProto::BAD_LENGTH;
	int last_pos = m_nPos;
	int imp_num = 0;
	int begin_pos = m_nPos;
	int nLastLen = 0;
//	bool bFirstImp = true;

	for (;;)
	{
		last_pos = m_nPos; // позиция перед импульсом

		if (m_nPos >= int(pWave.size()))
		{
			return false;
		}

		int len = CalcImpLength(pWave, m_nPos); //считаем длину импульса в сэмплах

		if (len == CWaveParserProto::BAD_LENGTH)  // если не получилось посчитать
		{
			if (bRollback)
			{
				m_nPos = last_pos;  // откатываемся на позицию перед подсчётом
			}

			return false;       // и выходим
		}

		register int a = nLastLen - len; //вычисляем разницу в длине импульсов

//      if (bFirstImp)
//      {
//          a = 0;
//          bFirstImp = false;
//      }

		if (a < 0)
		{
			a = -a;     // берём модуль
		}

		if (a <= g_Settings.nTuneImpScatter)     // если разница меньше разброса длительностей
		{
			// то это правильный импульс, будем с ним работать
			m_dAvgLength += double(len);    // прибавим к средней длительности, чтоб потом вычислить
			imp_num++;              // посчитаем этот импульс

			if (imp_num >= nLength && !bFound) // если количество правильных импульсов достигло ориентировочной длины, и ещё не зафиксировали
			{
				syncro_pos = begin_pos; // фиксируем начало синхропоследовательности
				bFound = true;      // отмечаем факт
			}
		}
		else if (!bFound)   //импульс неправильный и ещё начало не нашли
		{
			m_dAvgLength = 0.0; //то всё отменяем
			imp_num = 0;
			begin_pos = m_nPos; //сдвигаем предполагаемое начало синхропоследовательности
		}
		else    //импульс неправильный, а начало мы уже зафиксировали
		{
			break;  //выходим из цикла, наверно до маркера дошли
		}

		nLastLen = len; //текущую длину -> предыдущую длину
	}

	m_nPos = last_pos;  //откатываемся к предыдущему импульсу
	wave_pos = syncro_pos; //выдаём найденное начало синхропоследовательности
	wave_length = m_nPos - syncro_pos; //выдаём длительность найденной синхропоследовательности

	if (imp_num)        // если есть что считать
	{
		m_dAvgLength = m_dAvgLength / imp_num; //вычисляем среднюю длину с округлением в большую сторону
	}

	return true;
}


bool CWaveParserProto::FindMarker(WaveData &pWave)
{
	int size = 0;
	int nLength = 0;

	for (;;)
	{
		nLength = CalcImpLength(pWave, m_nPos);

		if (nLength == CWaveParserProto::BAD_LENGTH)
		{
			return false;
		}

		size = DefineLength(nLength);

		if (size >= 4)
		{
			break;
		}
	}

	bool bBit;

	if (ReadBit(pWave, bBit))
	{
		return bBit;    //в правильном маркере бит - "1"
	}

	return false;
}


bool CWaveParserProto::FindSyncro6(WaveData &pWave)
{
	int nLength = CalcImpLength(pWave, m_nPos);

	if (nLength == CWaveParserProto::BAD_LENGTH)
	{
		return false;
	}

	int size = DefineLength(nLength);
	return (size >= 6);
}


bool CWaveParserProto::ReadBit(WaveData &pWave, bool &bBit)
{
	int oldPos = m_nPos;
	int length0 = CalcImpLength(pWave, m_nPos); //длина информационного импульса

	if (length0 != CWaveParserProto::BAD_LENGTH)
	{
		int length1 = CalcImpLength(pWave, m_nPos); //длина синхроимпульса за инфоимпульсом

		if (length1 != CWaveParserProto::BAD_LENGTH)
		{
			int size0 = DefineLength(length0);
			//  int size1 = DefineLength(length1); //не имеет значения, но считать длину надо, чтобы указатель двигать

			if (size0 == 1)
			{
				bBit = false;
				return true;
			}

			if (size0 == 2)
			{
				bBit = true;
				return true;
			}
		}
	}

	m_nPos = oldPos;
	return false;
}


bool CWaveParserProto::ReadByte(WaveData &pWave, uint8_t &byte)
{
	uint8_t mask = 1;
	byte = 0;
	bool bBit;

	for (int i = 0; i < 8; ++i)
	{
		if (!ReadBit(pWave, bBit))
		{
			return false;
		}

		if (bBit)
		{
			byte |= mask;
		}

		mask <<= 1;
	}

	return true;
}

uint16_t CWaveParserProto::CalcCRC(TAPE_FILE_INFO *pTfi)
{
	if (!m_pBin)
	{
		return 0;
	}

	int crc = 0;

	for (int i = 0; i < pTfi->length; ++i)
	{
		crc += m_pBin[i];

		if (crc & 0xFFFF0000)
		{
			//если случился перенос в 17 разряд (т.е. бит С для word)
			crc &= 0x0000FFFF; //его обнулим
			crc++; //но прибавим к сумме
		}
	}

	return LOWORD(crc);
}

