
/*	Copyright (C) G. Jespersen
	See Licence.txt.*/

// 10/10/2020 Modified PlayVideoLav

#include "stdafx.h"
#include "DsLavSplit.h"
#include "RadioBrowser.h"
#include "Curlinet.h"


///////////////////////////////////////////////////
/* CRadioBrowse code overview:

	Search for a single radio station.
	Search stations by tags(s)
	Search for all stations in a country
*/

/* not used

///////////////////////////////////////////////////
// Class CRadioBrowse

CRadioBrowse::CRadioBrowse()
{
}


CRadioBrowse::~CRadioBrowse()
{
}

// Get tag list: 113 tags - each used by at least 80 stations.
//  filepath is writable temp file.
//  server = "https://nl1.api.radio-browser.info"
int CRadioBrowse::GetTags(CString filePath, CString server, CString & errStr)
{
	int nItemsRead = 0;
	CCurlinet inet;
	CString strErr;

	// Build query.
	CString query = server;
	query += _T("/xml/tags"); // http://nl1.api.radio-browser.info/xml/tags

	// Fetch - returns xml data - stores in filePath temp file.
	if(!inet.GetInternetDataUnicode(query, filePath, strErr))
	{
		errStr.Format(_T("CRadioBrowse GetTags - GetInternetData failed: %s\n"), strErr);
		TRACE(errStr);
		return 0;
	}

	// Open File for reading.
	CFile file;
	CFileException e;
	if(!file.Open(filePath, CFile::modeRead | CFile::typeBinary, &e))
	{
		TCHAR szError[1024];
		e.GetErrorMessage(szError, 1024);
		CString er;
		er.Format(_T("Couldn't open file: %s"), szError);
		TRACE(er);
		strErr = er;
		return 0;
	}

	// Tags xml is:
	//  <tag name="#electro" stationcount="2"/>

	// Read vars.
	const UINT bufSz = 4096;
	BYTE buf[bufSz];
	TCHAR	ch			= _T('0');
	TCHAR	quote		= _T('"');
	UINT	filePos		= 0;
	UINT	nRead		= 0;
	BOOL	bInString	= FALSE;
	CString name, count;
	int nString = 0;
	int nStations = 0;
	int nMinStations = 80;	// 113 tags gotton w/ min 80 stations

	CTag tag;
	m_tagAr.RemoveAll();

	// Read chunks.
	while((nRead = file.Read(buf, bufSz)) > 0)
	{
		// Parse the chunk.
		for(UINT i = 0; i < nRead; i++)
		{
			filePos ++;
			ch = buf[i];

			// Get a string.
			if(bInString)
			{
				// If at the closing quote - '"'.
				if(ch == quote)
				{
					bInString = FALSE;
					nString ++;

					// Have name-count pair.
					if(nString >= 2)
					{
						nStations = _wtoi(count);	//_wtoi unicode, _ttoi TCHAR

						// Save pair.
						if(nStations >= nMinStations)
						{
							tag.Zero();
							tag.m_name = name;
							tag.m_nStations = nStations;
							m_tagAr.Add(tag);
						}

						// Prep for next
						nString = 0;
						name = _T("");
						count = _T("");
					}
					continue;
				}
				// Else save chars to string.
				//
				// Getting name.
				if(nString == 0)
					name += ch;
				// Getting count.
				else if(nString == 1)
					count += ch;
			}

			// Run to a quote - '"'.
			if(ch == quote)
			{
				bInString = TRUE;
				continue;
			}
		}
	}
	// Done.
	file.Close();

	// Results.
	int sz = m_tagAr.GetSize(); // 721 w/ 10 min, 379 w/ 20 min, 262 w/ 30 min, 199 w/ 40 min, 
	if(!sz)						// 172 w/ 50 min, 146 w/ 60 min, 129 w/ 70 min, 113 w/ 80 min,
	{
		strErr = _T("CRadioBrowse GetTags - got no tags.\n");
		TRACE(strErr);
		return 0;
	}
	return sz;
}

// Get counrty list - 210 countries.
//  filepath is writable temp file.
//  server = "https://nl1.api.radio-browser.info"
int CRadioBrowse::GetCountries(CString filePath, CString server, CString & errStr)
{
	int nItemsRead = 0;
	CCurlinet inet;
	CString strErr;

	// Build query.
	CString query = server;
	query += _T("/xml/countries"); // http://nl1.api.radio-browser.info/xml/countries

	// Fetch - returns xml data - stores in filePath temp file.
	if(!inet.GetInternetDataUnicode(query, filePath, strErr))
	{
		errStr.Format(_T("CRadioBrowse GetCountries - GetInternetData failed: %s\n"), strErr);
		TRACE(errStr);
		return 0;
	}

	// Open File for reading.
	CFile file;
	CFileException e;
	if(!file.Open(filePath, CFile::modeRead | CFile::typeBinary, &e))
	{
		TCHAR szError[1024];
		e.GetErrorMessage(szError, 1024);
		CString er;
		er.Format(_T("Couldn't open file: %s"), szError);
		TRACE(er);
		strErr = er;
		return 0;
	}

	// Countries xml is:
	//  <tag name="#101" stationcount="1"/>

	// Read vars.
	const UINT bufSz = 4096;
	BYTE buf[bufSz];
	TCHAR	ch			= _T('0');
	TCHAR	quote		= _T('"');
	UINT	filePos		= 0;
	UINT	nRead		= 0;
	BOOL	bInString	= FALSE;
	CString name, count;
	int nString = 0;

	CCountry country;
	m_countryAr.RemoveAll();

	// Read chunks.
	while((nRead = file.Read(buf, bufSz)) > 0)
	{
		// Parse the chunk.
		for(UINT i = 0; i < nRead; i++)
		{
			filePos ++;
			ch = buf[i];

			// Get a string.
			if(bInString)
			{
				// If at the closing quote - '"'.
				if(ch == quote)
				{
					bInString = FALSE;
					nString ++;

					// Have name-count pair.
					if(nString >= 2)
					{
						// Save pair.
						country.Zero();
						country.m_name = name;
						country.m_nStations = _wtoi(count);	//_wtoi unicode, _ttoi TCHAR
						m_countryAr.Add(country);

						// Prep for next
						nString = 0;
						name = _T("");
						count = _T("");
					}
					continue;
				}
				// Else save chars to string.
				//
				// Getting name.
				if(nString == 0)
					name += ch;
				// Getting count.
				else if(nString == 1)
					count += ch;
			}

			// Run to a quote - '"'.
			if(ch == quote)
			{
				bInString = TRUE;
				continue;
			}
		}
	}
	// Done.
	file.Close();

	// Results.
	int sz = m_countryAr.GetSize(); //210
	if(!sz)
	{
		strErr = _T("CRadioBrowse GetCountries - got no countries.\n");
		TRACE(strErr);
		return 0;
	}
	return sz;
}


// Get RB servers - usually 4
//  filepath is writable temp file.
int CRadioBrowse::GetServers(CString filePath, CString & errStr)
{
	int nItemsRead = 0;
	CCurlinet inet;
	CString strErr;
	CString query = _T("http://nl1.api.radio-browser.info/json/servers");

	// Fetch - returns json data - stores in filePath temp file.
	if(!inet.GetInternetDataUnicode(query, filePath, strErr))
	{
		errStr.Format(_T("CRadioBrowse GetServers - GetInternetData failed: %s\n"), strErr);
		TRACE(errStr);
		return 0;
	}

	// Open filePath.
	CStdioFile file;
	CFileException e;
	if(!file.Open(filePath, 
				  CFile::modeRead | CFile::typeText,
				  &e)) 
	{
		errStr.Format(_T("CRadioBrowse GetServers - can not open file %s, error = %u\n"), 
							filePath, e.m_cause );
		TRACE(errStr);		
		return 0;
	}
	/ * typical json data:
	[{"ip":"2001:19f0:5001:32a4:5400:2ff:fe37:75c2","name":"nl1.api.radio-browser.info"},
	 {"ip":"2001:19f0:6801:19d7:5400:2ff:fe71:bdb7","name":"fr1.api.radio-browser.info"},
	 {"ip":"2a03:4000:37:42:c4fe:4cff:fea7:8941","name":"de1.api.radio-browser.info"},
	 {"ip":"95.179.139.106","name":"nl1.api.radio-browser.info"},
	 {"ip":"45.77.62.161","name":"fr1.api.radio-browser.info"},
	 {"ip":"91.132.145.114","name":"de1.api.radio-browser.info"}]
	* /

	// Verify file size for CString below.
	int fileLen = file.GetLength();
	if(fileLen > 2048)
	{
		file.Close();
		errStr.Format(_T("CRadioBrowse GetServers - file %s is too long.\n"), filePath);
		TRACE(errStr);
		return 0;
	}

	// Parse into m_rbSeverAr.
	//
	CString strServer, fileStr, temp;
	CString https = _T("https://");
	int index = 0;
	int len = 0, nChars = 0;

	// Read the disk file into fileStr in one gulp - no newlines in json.
	while(file.ReadString(fileStr))
	{
		len = fileStr.GetLength(); //428
		nChars += len;

		// Parse server strings.
		CString term = _T("api.radio-browser.info");
		int nPos = 0, nStart = 0, nEnd = 0;

		nPos = fileStr.Find(term, nEnd);
		while(nPos != -1)
		{
			nStart = nPos - 4;
			nEnd = nPos + 22;

			// Verify bounds;
			if(nStart < 0 || nEnd >= len)
			{
				file.Close();
				errStr = _T("CRadioBrowse GetServers - parsing error.\n");
				TRACE(errStr);
				return 0;
			}

			// Get mid.
			temp = fileStr.Mid(nStart, nEnd - nStart);

			// Build url.
			strServer = https;
			strServer += temp;

			// Ensure no duplicates - usually 2 of each.
			int sz = m_rbSeverAr.GetSize();
			BOOL exists = FALSE;
			for(int i = 0; i < sz; i++)
			{
				if(m_rbSeverAr[i] == strServer)
				{
					exists = TRUE;
					break;
				}
			}
			if(!exists)
			{
				// Add to list
				m_rbSeverAr.Add(strServer);
			}
			// Get next.
			nPos = fileStr.Find(term, nEnd);
		}
	}
	// Done with file.
	file.Close();

	// Check results.
	int sz = m_rbSeverAr.GetSize();
	if(!sz)
	{
		errStr = _T("ERROR CRadioBrowse GetServers - no server urls gotten.\n");
		TRACE(errStr);
		return 0;
	}
	// Print to output.
	for(int i = 0; i < sz; i++)
	{
		CString s;
		s.Format(_T("%s\n"), m_rbSeverAr[i]);
		TRACE(s);
		/ *
		https://nl1.api.radio-browser.info
		https://fr1.api.radio-browser.info
		https://de1.api.radio-browser.info
		* /
	}
	return sz;
}


///////////////////////////////////////////////////
// CCountry
CCountry::CCountry()
{
	Zero();
}
CCountry::~CCountry()
{
}
CCountry::CCountry(const CCountry & country)
{
	Copy(country);
}
void CCountry::operator=(const CCountry & country)
{
	Copy(country);
}
BOOL CCountry::operator==(const CCountry & country)
{
	if(m_name == country.m_name && m_nStations == country.m_nStations)
		return TRUE;
	else
		return FALSE;
}
void CCountry::Copy(const CCountry & country)
{
	m_name = country.m_name;
	m_nStations = country.m_nStations;
}
void CCountry::Zero()
{
	m_name = _T("");
	m_nStations = 0;
}

///////////////////////////////////////////////////
// CTag
CTag::CTag()
{
	Zero();
}
CTag::~CTag()
{
}
CTag::CTag(const CTag & tag)
{
	Copy(tag);
}
void CTag::operator=(const CTag & tag)
{
	Copy(tag);
}
BOOL CTag::operator==(const CTag & tag)
{
	if(m_name == tag.m_name && m_nStations == tag.m_nStations)
		return TRUE;
	else
		return FALSE;
}
void CTag::Copy(const CTag & tag)
{
	m_name = tag.m_name;
	m_nStations = tag.m_nStations;
}
void CTag::Zero()
{
	m_name = _T("");
	m_nStations = 0;
}

*/

