// aplCSV.cpp

#include "stdafx.h"
#include "aplAggr.h"

CaplCSV::CaplCSV(char razd):m_bUseBreakStr(false){m_razd=razd;m_cur_line= -1;}	

bool CaplCSV::OpenFile(LPCTSTR file_name)
{
	if(file_name==0) return false;
    if(TRUE==m_file.Open(file_name,CaplFile::modeRead|CaplFile::typeText|CaplFile::shareDenyWrite)){m_cur_line= 0; return true;}
	else return false;
}

void CaplCSV::CloseFile() {	m_file.Close();}
void CaplCSV::SeekToBegin(void) { m_file.SeekToBegin();m_cur_line= 0;}

int GetNumberOfQuotes( CString& str )
{
    int iQN = 0;
    for(int i = 0; i < (int)str.GetLength(); ++i )
        if( str[ i ] == _T('"') )  iQN++;
    return iQN;
}

bool CaplCSV::ReadString(CStringArray &strings)
{
    bool bOddQuoteNum = false; // в строке количество кавычек должно быть четным
	strings.RemoveAll();
	CString buf, tmp;

    do
    {
        if(!m_file.ReadString(buf)) return false;
        // посчитаем количество кавычек
        if( 0 != GetNumberOfQuotes( buf ) % 2 || bOddQuoteNum == true )
        {
            tmp += buf + _T(" ");
            bOddQuoteNum = true;
        }
    }
    while( 0 != GetNumberOfQuotes( tmp ) % 2 );

    if ( tmp != _T("") )
    {
        buf = tmp;
    }

	m_cur_line++;
	return StrToArray(buf,strings,m_razd, m_bUseBreakStr);
}
bool CaplCSV::StrToArray(LPCTSTR str, CStringArray &strings, char razd, bool bUseBreakStr)
{	
	strings.RemoveAll();
	if(str==0) return false;
	int i, j, k=_strlen(str);
	CString buf1;
	bool is_str=false;

	j=0;
	while (str[j]==_T(' ')) j++; // пропуск лиших пробелов

	for(i=j;i<k;i++)
	{
		TCHAR c=str[i];

		if(c==razd)
		{
			if(!is_str)
			{
				strings.Add(buf1);
				buf1=_T("");
				
				while (str[i+1]==_T(' ')) i++; // пропуск лиших пробелов
			}
			else buf1+=c;
		}
		else if(c==_T('"'))
		{
			if(!is_str)
			{
				is_str=true;

				// отмечаем начало следующего слова
				j = i+1;
			}

			//  [9/11/2012 lobanov]
			// я вот не очень понимаю все вот эти навороты, что я закомментил в блоке else ниже,
			// но могу предложить свой вариант блока в предположении что внутренние строки обрамляются
			// двойными двойными кавычками ("")
// Вернул исходный код разбора. Код лобанова некорректно разбирает строки типа 1;"Филиал ""АО ""Парус""";"Филиал ""АО ""Парус""";ЕщеСтрока
// 			else if (i<k-1)
// 			{
// 				int i_nxt=i+1;
// 				while(str[i_nxt]==_T(' ') && i_nxt<k) i_nxt++;
// 				if (i_nxt==k)
// 					is_str=false;
// 				else if (str[i_nxt]==razd && GetNumberOfQuotes(buf1)%2==0)
// 					is_str=false;
// 				else if (i>j && str[i-1]==_T('"'))
// 					buf1+=c;
// 			}
			else 
			{
				if(i<k-1)
				{
					if(i && str[i-1]=='"' && buf1.IsEmpty()) // начало строки в кавычках или пустая строка
					{
						int i_nxt=i+1;
						while(str[i_nxt]==' ' && i_nxt<k) i_nxt++;
						if(i_nxt!=k && str[i_nxt]==razd) is_str=false;
						else
						{
							buf1+=c;
							if(str[i+1]=='"') i++;
						}
					}
					else
					{
						// определяем внутриячеечные разделители
						int i_nxt=i+1;
						while(str[i_nxt]==' ' && i_nxt<k) i_nxt++;
						if(i_nxt==k) is_str=false;
						else if(str[i_nxt]==',')
						{
							i_nxt++;
							while(str[i_nxt]==' ' && i_nxt<k) i_nxt++;
							if(i_nxt==k) is_str=false;
							else if(str[i_nxt]=='"')
							{
								buf1+='"';
								buf1+=',';
								buf1+='"';
								i=i_nxt;
							}
							else is_str=false;
						}
						else if(str[i+1]=='"') // не разделительные кавычки или окончание строки в кавычках
						{
							i++;
							buf1+=c;
							i_nxt=i+1;
							while(str[i_nxt]==' ' && i_nxt<k) i_nxt++;
							if(i_nxt==k) is_str=false;
							else if(str[i_nxt]==razd)
							{
								i=i_nxt-1;
								is_str=false;
							}
						}
						else is_str=false;
					}
				}
			}

		}
		else if(c==_T('\\') && bUseBreakStr)
		{
			if(!is_str)
				is_str=true;
			else 
			{
				if(i<k-1)
				{
					if(str[i+1]==_T('\\'))
					{
						i++;
						buf1+=c;
					}
					else if(str[i+1]==_T('n'))
					{
						i++;
						buf1+=_T("\n");
					}
					else
						is_str=false;
				}
			}
		}
		else if(c==_T('\t'))
		{
			if(is_str) buf1+=c;
		}
		else buf1+=c;
	}
	strings.Add(buf1);
	return true;
}
bool CaplCSV::ReadFile(LPCTSTR filePath, std::vector<std::vector<CString>> &fileData )
{
	// откроем файл
	if(OpenFile(filePath)) 
	{
		fileData.clear();

		// прочтем файл
		CStringArray strArr;
		while (ReadString(strArr))
		{
			fileData.push_back(std::vector<CString>());
			const int index = fileData.size() - 1;

			for(int i=0; i<strArr.GetSize(); ++i)
				fileData[index].push_back(strArr.GetAt(i));
		}

		// закроем файл
		CloseFile();

		// проверим, что кол-во колонок везде одинаково
        int columnCount = -1;
		for(unsigned int i=0; i<fileData.size(); ++i)
		{
			if(columnCount == -1) 
			{
				columnCount = fileData[i].size();
			}
			else
			{
				if(columnCount != (int)fileData[i].size())
				{
					aplMessageBox(APL_T("Количество колонок в строках неодинаково!"), MB_ICONSTOP);
					return false;
				}
			}
		}
		return true;
	}
	return false;
}

