//DataSource.cpp

#include "StdAfx.h"
#include "ReportDict.h"
#include "Functions.h"
#include "CommonQuery.h"
#include <aplxml.h>


CDataSource::CDataSource(CHeader* header, CaplReportMgr *ReportMgr)
{
	m_header = header;
	m_rows = 0;
	if(header!=0)
		m_cols = header->GetSize();
	else
		m_cols = 0;
	m_Data.RemoveAll();
	m_Separator = _T(';');

	m_ReportMgr = ReportMgr;

	m_inst = NULL;
}

CDataSource::~CDataSource()
{
	RemoveAll();
}

int CDataSource::AddColumn()
{
	int i;
	int rows = m_Data.GetSize();
	m_header->AddColumn(NULL);
	for(i=0; i<rows;i++)
	{
		CTableRow* row = m_Data.GetAt(i);
		if(row==NULL) continue;
		row->AddColumn(NULL);
	}
	m_cols++;
	return i;
}

bool CDataSource::GetSize(int &rows, int &cols)
{
	rows = m_rows;
	cols = m_cols;
	return true;
}

int CDataSource::AddRow(CTableRow* row)
{
	int cnt = row->GetSize();
	if(cnt<m_cols)
	{
		AfxMessageBox(APL_T("Неверно сформирована строка!"));
		return -1;
	}
	int ind = m_Data.Add(row);
	ASSERT(ind>-1);
	m_rows = m_Data.GetSize();
	return ind;
}

int CDataSource::AddRow()
{
	CTableRow* row = new CTableRow;
	for(int i=0; i<m_cols; i++)
	{
		CaplReportValue *val = new CaplReportValue;
		row->AddColumn(val);
	}
	int ind = m_Data.Add(row);
	ASSERT(ind>-1);
	m_rows = m_Data.GetSize();
	return ind;
}

int CDataSource::InsertRow(CTableRow* row, int pos)
{
	int cnt = row->GetSize();
	if(cnt<m_cols)
	{
		AfxMessageBox(APL_T("Неверно сформирована строка!"));
		return -1;
	}
	
	m_Data.InsertAt(pos, row);
	m_rows = m_Data.GetSize();
	return m_rows;
}

bool CDataSource::DeleteColumn(int pos)
{
	int rows = m_Data.GetSize();
	m_header->RemoveAt(pos);
	for(int i=0; i<rows;i++)
	{
		CTableRow* row = m_Data.GetAt(i);
		if(row==NULL) continue;
		row->RemoveAt(pos);
	}
	m_cols++;
	return true;
}

bool CDataSource::DeleteRow(int pos)
{
	if(pos<0 || pos>=m_Data.GetSize()) return false;
	CTableRow* row = m_Data.GetAt(pos);
	delete row;
	m_Data.RemoveAt(pos);		
	m_rows--;
	return true;
}

void CDataSource::RemoveAll()
{
	int i, cnt = m_Data.GetSize();
	for(i=0; i<cnt; i++)
	{
		CTableRow* row = m_Data.GetAt(i);
		delete row;
	}
	m_Data.RemoveAll();
	if(m_header)
	{
		m_header->RemoveAll();
		delete m_header;
	}
	
	m_header = NULL;
	m_cols = 0;
	m_rows = 0;

	m_RParams.RemoveAll();
	m_params.RemoveAll();
}

void CDataSource::Clear()
{
	int cnt = m_Data.GetSize();
	for(int i=0;i<cnt;i++)
	{
		DELETE_OBJECT(m_Data.GetAt(i));
	}
	m_cols = 0;
	m_rows = 0;
	m_Data.RemoveAll();
}

CHeader* CDataSource::SetHeader(CHeader* header, bool bDeleteOldHeader)
{
	CHeader* old = m_header;
	m_header = header;
	m_cols = header->GetSize();
	
	if(bDeleteOldHeader && old)
	{
		delete old;
		return NULL;
	}
	else
		return old;
}

bool CDataSource::SetAt(int row, int col, CaplReportValue* val)
{
	if(row<0 || row>=m_Data.GetSize()) return false;
	CTableRow* r = m_Data.GetAt(row);
	if(r->GetColumn(col))
		delete r->GetColumn(col);
	return r->SetAt(col,val);
}

bool CDataSource::SetAt(int row, int col, CaplInstance* val)
{
	if(row<0 || row>=m_Data.GetSize()) return false;
	CTableRow* r = m_Data.GetAt(row);
	CaplReportValue* Val = r->GetColumn(col);
	if(Val==NULL) Val = new CaplReportValue;
	Val->Set(val);
	return r->SetAt(col,Val);
}

bool CDataSource::SetAt(int row, int col, LPCTSTR val)
{
	if(row<0 || row>=m_Data.GetSize()) return false;
	if(col<0) return false;
	CTableRow* r = m_Data.GetAt(row);
	if(col>r->GetSize()-1) return false;
	CaplReportValue* Val = r->GetColumn(col);
	if(Val==NULL) Val = new CaplReportValue;
	Val->Set(val);
	return r->SetAt(col,Val);
}

bool CDataSource::SetAt(int row, int col, int val)
{
	if(row<0 || row>=m_Data.GetSize()) return false;
	CTableRow* r = m_Data.GetAt(row);
	CaplReportValue* Val = r->GetColumn(col);
	if(Val==NULL) Val = new CaplReportValue;
	Val->Set(val);
	return r->SetAt(col,Val);
}

bool CDataSource::SetAt(int row, int col, double val)
{
	if(row<0 || row>=m_Data.GetSize()) return false;
	CTableRow* r = m_Data.GetAt(row);
	CaplReportValue* Val = r->GetColumn(col);
	if(Val==NULL) Val = new CaplReportValue;
	Val->Set(val);
	return r->SetAt(col,Val);
}

bool CDataSource::SetAt(int row, LPCTSTR col, CaplInstance* val)
{
	if(row<0 || row>=m_Data.GetSize()) return false;
	CTableRow* r = m_Data.GetAt(row);
	int ind = m_header->Find(col);
	if(ind<0) return false;
	CaplReportValue* Val = r->GetColumn(ind);
	if(Val==NULL) Val = new CaplReportValue;
	Val->Set(val);
	return r->SetAt(ind,Val);
}

bool CDataSource::SetAt(int row, LPCTSTR col, LPCTSTR val)
{
	if(row<0 || row>=m_Data.GetSize()) return false;
	CTableRow* r = m_Data.GetAt(row);
	int ind = m_header->Find(col);
	if(ind<0) return false;
	CaplReportValue* Val = r->GetColumn(ind);
	if(Val==NULL) Val = new CaplReportValue;
	Val->Set(val);
	return r->SetAt(ind,Val);
}

bool CDataSource::SetAt(int row, LPCTSTR col, int val)
{
	if(row<0 || row>=m_Data.GetSize()) return false;
	CTableRow* r = m_Data.GetAt(row);
	int ind = m_header->Find(col);
	if(ind<0) return false;
	CaplReportValue* Val = r->GetColumn(ind);
	if(Val==NULL) Val = new CaplReportValue;
	Val->Set(val);
	return r->SetAt(ind,Val);
}

bool CDataSource::SetAt(int row, LPCTSTR col, double val)
{
	if(row<0 || row>=m_Data.GetSize()) return false;
	CTableRow* r = m_Data.GetAt(row);
	int ind = m_header->Find(col);
	if(ind<0) return false;
	CaplReportValue* Val = r->GetColumn(ind);
	if(Val==NULL) Val = new CaplReportValue;
	Val->Set(val);
	return r->SetAt(ind,Val);
}

bool CDataSource::SetAt(int row, LPCTSTR col, CaplReportValue* val)
{
	if(row<0 || row>=m_Data.GetSize()) return false;
	CTableRow* r = m_Data.GetAt(row);
	int ind = m_header->Find(col);
	return r->SetAt(ind,val);
}

void CDataSource::Sort(char* name, bool bSortAsString, int SortDirection)
{
	int ind = m_header->Find(name);
	Sort(ind, &m_Data, bSortAsString, SortDirection);
}

void CDataSource::Sort(int col, TR_Array *Data, bool bSortAsString, int SortDirection)
{
	if (SortDirection==aplSortNone) return;
	if(Data==NULL) return;
	if(col<0) return;
	int cnt = Data->GetSize();
	if(cnt<2) return;
	int gap, i, eo, res;
	gap = cnt/2;
	do {
		do {
			eo = 0;
			for(i=0; i<cnt-gap;i++)
			{
				CaplReportValue *val1, *val2;
				CTableRow *row1, *row2;
				row1 = Data->GetAt(i);
				row2 = Data->GetAt(i+gap);
				val1 = row1->GetColumn(col);
				val2 = row2->GetColumn(col);
				res = Compare(val1, val2, bSortAsString);
				if(SortDirection==aplZA)
					res = -res;
				if(res>0)
				{
					if(gap!=0)
					{
						Data->SetAt(i,row2);
						Data->SetAt(i+gap,row1);
					}
					eo = 1;
				}
			}
		} while(eo);
	} while(gap=gap/2);
}

void CDataSource::Sort(ISortArray *cols, TR_Array* pData)
{
	if(cols==NULL) return;
	int count = cols->GetSize();
	if(count==0) return;
	if(pData==NULL)
		pData = &m_Data;

	if(pData->GetSize()<=1) return;
	int Ind = cols->GetAt(0).col_num;
	int j, i;
	Sort(Ind, pData, cols->GetAt(0).bSortAsString, cols->GetAt(0).direction);
	int cnt = pData->GetSize();
	TR_Array* Data;
	CArray<TR_Array*, TR_Array*> DataArray;
	ISortArray Ints;
	TSortParam SortParam;
	SortParam = cols->GetAt(0);
	for(i=1; i<count;i++)
		Ints.Add(cols->GetAt(i));
		

	CTableRow* row = pData->GetAt(0);
	CaplReportValue *val1, *val2;

	val1 = row->GetColumn(Ind);
	int ind = 0;
	Data = new TR_Array;
	for(j=0; j<pData->GetSize(); j++)
	{
		row = pData->GetAt(j);
		val2 = row->GetColumn(Ind);
		if(Compare(val1,val2, cols->GetAt(0).bSortAsString)!=0)
		{
			Sort(&Ints, Data);
			ind = ind+Data->GetSize();
			j = ind;
			val1 = val2;
			DataArray.Add(Data);
			Data = new TR_Array;
			Data->RemoveAll();
		}
		Data->Add(row);
	}
	Sort(&Ints, Data);
	DataArray.Add(Data);
	TR_Array ResArray;
	//Заполнение ИД и удаление массива массивов
	if(SortParam.m_Flags&APL_GROUP_SORT_BY_ROWS_COUNT)
	{
		//Сортируем массив
		TR_Array *pTmpData;
		int MaxCnt, MinCnt;
		for(i=0; i<DataArray.GetSize(); i++)
		{
			MinCnt = MaxCnt = DataArray[i]->GetSize();
			ind = i;
			for(j=i; j<DataArray.GetSize(); j++)
			{
				if(SortParam.direction==aplZA)
				{
					if(MaxCnt<DataArray[j]->GetSize())
					{
						MaxCnt = DataArray[j]->GetSize();
						ind = j;
					}
				}
				else if (SortParam.direction==aplAZ)
				{
					if(MinCnt>DataArray[j]->GetSize())
					{
						MinCnt = DataArray[j]->GetSize();
						ind = j;
					}
				}
			}
			//Делаем подмену
			if(ind!=i)
			{
				pTmpData = DataArray[i];
				DataArray.SetAt(i, DataArray[ind]);
				DataArray.SetAt(ind, pTmpData);
			}
		}
	}
	//В тупую объединяем полученные результаты
	for(i=0; i<DataArray.GetSize(); i++)
	{
		ResArray.Append(*DataArray[i]);
		delete DataArray[i];
	}
	pData->RemoveAll();
	pData->Append(ResArray);
	ResArray.RemoveAll();
}

void CDataSource::Sort(CHeaderColumn* col, bool bSortAsString, int SortDirection)
{
	if(m_header==NULL) return;
	
	int ind = m_header->Find(col->GetName());
	Sort(ind, &m_Data, bSortAsString, SortDirection);
}

double CDataSource::GetCountInGroup(int start_row, CHeaderColumn* gc, CHeaderColumn* col, CString sCondition, CConditionArray* pConditions)
{
	CArray<CTableRow*, CTableRow*> Data;
	if(!GetRowsInGroup(gc, start_row, &Data, pConditions))
		return 0;
	return Data.GetSize();
}

double CDataSource::GetAverangeInGroup(int start_row, CHeaderColumn* gc, CHeaderColumn* col, CConditionArray* pConditions)
{
	CArray<CTableRow*, CTableRow*> Data;
	if(!GetRowsInGroup(gc, start_row, &Data, pConditions))
		return 0;
	CaplReportValue* val;
	double Summ = 0, dVal;
	CString sVal;
	int iVal;
	int ind = GetHeader()->Find(col->m_name);
	if(ind<0) return 0;
	for(int i=0; i<Data.GetSize(); i++)
	{
		iVal = 0;
		dVal = 0;
		sVal.Empty();
		val = Data[i]->GetColumn(ind);
		if(val->type==aplREAL)
			val->Get(dVal);
		else if(val->type==aplINTEGER)
			val->Get(iVal);
		else if(aplSTRING)
		{
			val->Get(sVal);
			if(IsRealOrEmpty(sVal) && !sVal.IsEmpty())
				dVal = __atof(sVal);
		}
		Summ+=dVal+iVal;
	}
	double averange = Summ/double(Data.GetSize());
	return averange;
}

void CDataSource::Delete()
{
	if(m_ReportMgr==NULL) return;
	if(m_ReportMgr->m_data==NULL) return;
	
	if(m_inst==NULL) return;
	
	int i;
	aplExtent ext_to_del;
	aplExtent ext, ext1;
	
	//Удаляем все старые
	m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_ds_columns, ext);
	for(i=0; i<ext.GetSize(); i++)
	{
		ext_to_del.Add(ext[i]);
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_ds_column_values, ext1);
		ext_to_del.Append(ext1);
	}
	
	ext_to_del.Add(m_inst);
	
	for(i=0; i<ext_to_del.GetSize(); i++)
		m_ReportMgr->m_data->DeleteInstance(ext_to_del[i]);
}

CTableRow* CDataSource::FindRow(int col, CString sVal)
{
	if(col<0 || col>m_cols) return NULL;
	
	CTableRow *pRow;
	int i;
	for(i=0; i<m_Data.GetSize(); i++)
	{
		pRow = m_Data[i];
		if(pRow->GetAsString(col).CompareNoCase(sVal)==0) return pRow;
	}
	
	return NULL;
}


bool CDataSource::GetAt(int row, int col, CString& Val)
{
	CaplReportValue* ReportValue = GetAt(row, col);
	if(ReportValue)
	{
		Val = GetAsString(ReportValue);
		return true;
	}
	else return false;
}

bool CDataSource::GetRowsInGroup(CGroupColArray* pGroups, CGroupCol *Group, int GroupLastRow, CArray<CTableRow*,  CTableRow*> *Data, CConditionArray* pConditions)
{
	int cnt = pGroups->GetSize();
	int pos = -1, i, j;
	for(i=0; i<cnt; i++)
	{
		if(pGroups->GetAt(i)==Group)
		{
			pos = i+1;
			break;
		}
	}
	if(pos<0) return false;
	CStringArray strs;
	CString buf, buf1;
	CTableRow* row = GetRow(GroupLastRow);
	if(!row)
	{
		/*Иногда сюда залетает... В чем физический смысл - не разобрались :)) Нахимович 07.12.2010*/
		int ggg=0;
		return false;
	}
	CHeader* header = GetHeader();
	int ind = -1;
	for(j=0; j<pos; j++)
	{
		ind = header->Find(pGroups->GetAt(j)->m_col->GetName());
		strs.Add(row->GetAsString(ind));
	}
	Data->Add(row);
	bool bOk = true;
	for(i=GroupLastRow-1; i>-1;i--)
	{
		//Проверка источника данных на усливия секции и групп.
		bOk = true;
		if(pConditions)
			if(!pConditions->Compare(i)) continue;
		for(j=0; j<cnt; j++)
		{
			if(!pGroups->GetAt(j)->m_conditions.Compare(i))
			{
				bOk = false;
				break;
			}
		}
		if(!bOk) continue;
		//Проверка строки на вхождение в группу.
		row = GetRow(i);
		for(j=0; j<pos; j++)
		{
			buf = row->GetAsString(header->Find(pGroups->GetAt(j)->m_col->GetName()));
			if(buf.Compare(strs[j])!=0)
			{
				bOk = false;
				break;
			}
		}
		//Если строка не входит в группу, то сравнивать более бесполезно,
		//т.к. к этому моменту ИД отсортирован
		if(!bOk)
			break;
		Data->Add(row);
	}
	//Если переданная строка первая в группе
	int col_cnt, row_cnt;
	GetSize(row_cnt, col_cnt);

	for(i=GroupLastRow+1; i<row_cnt;i++)
	{
		//Проверка источника данных на усливия секции и групп.
		bOk = true;
		if(pConditions)
			if(!pConditions->Compare(i)) continue;
		for(j=0; j<cnt; j++)
		{
			if(!pGroups->GetAt(j)->m_conditions.Compare(i))
			{
				bOk = false;
				break;
			}
		}
		if(!bOk) continue;
		//Проверка строки на вхождение в группу.
		row = GetRow(i);
		for(j=0; j<pos; j++)
		{
			buf = row->GetAsString(header->Find(pGroups->GetAt(j)->m_col->GetName()));
			if(buf.Compare(strs[j])!=0)
			{
				bOk = false;
				break;
			}
		}
		//Если строка не входит в группу, то сравнивать более бесполезно,
		//т.к. к этому моменту ИД отсортирован
		if(!bOk)
			break;
		Data->Add(row);
	}
	return true;
}

double CDataSource::GetSummInGroup(int start_row, CGroupColArray* pGroups, CGroupCol* gc, CHeaderColumn* col, CConditionArray* pConditions, int *pRows)
{
	CArray<CTableRow*, CTableRow*> Data;
	if(!GetRowsInGroup(pGroups, gc, start_row, &Data, pConditions))
		return 0;
	CaplReportValue* val;
	double Summ = 0, dVal;
	int iVal;
	CString sVal;
	int ind = GetHeader()->Find(col->m_name);
	if(ind<0) return 0;
	for(int i=0; i<Data.GetSize(); i++)
	{
		iVal = 0;
		dVal = 0;
		sVal.Empty();
		val = Data[i]->GetColumn(ind);
		if(val->type==aplREAL)
			val->Get(dVal);
		else if(val->type==aplINTEGER)
			val->Get(iVal);
		else if(aplSTRING)
		{
			val->Get(sVal);
			if(IsRealOrEmpty(sVal) && !sVal.IsEmpty())
				dVal = __atof(sVal);
		}
		Summ+=dVal+iVal;
	}
	if(pRows) *pRows = Data.GetSize();
	return Summ;
}

double CDataSource::GetCountInGroup(int start_row, CGroupColArray* pGroups, CGroupCol* gc, CHeaderColumn* col, CString sCondition, CConditionArray* pConditions)
{
	int i;
	int cnt = 0;
	CArray<CTableRow*, CTableRow*> Data;

	if(!GetRowsInGroup(pGroups, gc, start_row, &Data, pConditions))
		return 0;

	for(i=0; i<Data.GetSize(); i++)
	{
		if(ParseCondition(sCondition, Data[i], m_header, &m_params, this))
			cnt++;
	}

	return cnt;
}

double CDataSource::GetAverangeInGroup(int start_row, CGroupColArray* pGroups, CGroupCol* gc, CHeaderColumn* col, CConditionArray* pConditions, int *pRows)
{
	CArray<CTableRow*, CTableRow*> Data;
	if(!GetRowsInGroup(pGroups, gc, start_row, &Data, pConditions))
		return 0;
	CaplReportValue* val;
	double Summ = 0, dVal;
	CString sVal;
	int iVal;
	int ind = GetHeader()->Find(col->m_name);
	if(ind<0) return 0;
	for(int i=0; i<Data.GetSize(); i++)
	{
		iVal = 0;
		dVal = 0;
		sVal.Empty();
		val = Data[i]->GetColumn(ind);
		if(val->type==aplREAL)
			val->Get(dVal);
		else if(val->type==aplINTEGER)
			val->Get(iVal);
		else if(aplSTRING)
		{
			val->Get(sVal);
			if(IsRealOrEmpty(sVal) && !sVal.IsEmpty())
				dVal = __atof(sVal);
		}
		Summ+=dVal+iVal;
	}
	double averange = Summ/double(Data.GetSize());
	if(pRows) *pRows = Data.GetSize();
	return averange;
}

bool CDataSource::GetRowsInGroup(CHeaderColumn* Group, int GroupLastRow, CArray<CTableRow*,  CTableRow*> *Data, CConditionArray* pConditions)
{
	int pos;
	int j;
	int i;
	CStringArray strs;
	CString buf, buf1;
	CTableRow* row = m_Data[GroupLastRow];
	int ind = -1;
	pos = m_header->Find(Group->GetName());
	for(j=0; j<pos; j++)
	{
		ind = m_header->Find(Group->GetName());
		strs.Add(row->GetAsString(ind));
	}
	Data->Add(row);
	bool bOk = true;
	for(i=GroupLastRow-1; i>-1;i--)
	{
		//Проверка источника данных на усливия секции и групп.
		bOk = true;
		if(pConditions)
			if(!pConditions->Compare(i)) continue;

		//Проверка строки на вхождение в группу.
		row = GetRow(i);
		for(j=0; j<pos; j++)
		{
			buf = row->GetAsString(m_header->Find(Group->GetName()));
			if(buf.Compare(strs[j])!=0)
			{
				bOk = false;
				break;
			}
		}
		//Если строка не входит в группу, то сравнивать более бесполезно,
		//т.к. к этому моменту ИД отсортирован
		if(!bOk)
			break;
		Data->Add(row);
	}
	//Если переданная строка первая в группе
	int col_cnt, row_cnt;
	GetSize(row_cnt, col_cnt);
	
	for(i=GroupLastRow+1; i<row_cnt;i++)
	{
		//Проверка источника данных на усливия секции и групп.
		bOk = true;
		if(pConditions)
			if(!pConditions->Compare(i)) continue;
			//Проверка строки на вхождение в группу.
		row = GetRow(i);
		for(j=0; j<pos; j++)
		{
			buf = row->GetAsString(m_header->Find(Group->GetName()));
			if(buf.Compare(strs[j])!=0)
			{
				bOk = false;
				break;
			}
		}
		//Если строка не входит в группу, то сравнивать более бесполезно,
		//т.к. к этому моменту ИД отсортирован
		if(!bOk)
			break;
		Data->Add(row);
	}
	return true;
}

double CDataSource::GetSummInGroup(int start_row, CHeaderColumn* gc, CHeaderColumn* col, CConditionArray* pConditions)
{
	CArray<CTableRow*, CTableRow*> Data;
	if(!GetRowsInGroup(gc, start_row, &Data, pConditions))
		return 0;
	CaplReportValue* val;
	double Summ = 0, dVal;
	int iVal;
	CString sVal;
	int ind = GetHeader()->Find(col->m_name);
	if(ind<0) return 0;
	for(int i=0; i<Data.GetSize(); i++)
	{
		iVal = 0;
		dVal = 0;
		sVal.Empty();
		val = Data[i]->GetColumn(ind);
		if(val->type==aplREAL)
			val->Get(dVal);
		else if(val->type==aplINTEGER)
			val->Get(iVal);
		else if(aplSTRING)
		{
			val->Get(sVal);
			if(IsRealOrEmpty(sVal) && !sVal.IsEmpty())
				dVal = __atof(sVal);
		}
		Summ+=dVal+iVal;
	}
	return Summ;
}


bool CDataSource::GetAt(int row, int col, double& Val)
{
	CaplReportValue* ReportValue = GetAt(row, col);
	if(ReportValue)
	{
		if(ReportValue->type!=aplREAL)
		{
			CString buf;
			buf = GetAsString(ReportValue);
			Val = __atof(buf);
		}
		else
			ReportValue->Get(Val);
		return true;
	}
	else return false;
}

bool CDataSource::GetAt(int row, int col, int& Val)
{
	CaplReportValue* ReportValue = GetAt(row, col);
	if(ReportValue)
	{
		if(ReportValue->type!=aplINTEGER)
		{
			CString buf;
			buf = GetAsString(ReportValue);
			Val = _atoi(buf);
		}
		else
			ReportValue->Get(Val);
		return true;
	}
	else return false;
}

bool CDataSource::GetColorAt(int row, int col, COLORREF& color)
{
	CaplReportValue* ReportValue = GetAt(row, col);
	if(ReportValue)
	{
		color = ReportValue->m_text_color;
		return true;
	}
	else return false;
}

bool CDataSource::GetBoldAt(int row, int col, BOOL& bold)
{
	CaplReportValue* ReportValue = GetAt(row, col);
	if (ReportValue)
	{
		bold = ReportValue->m_bold;
		return true;
	}
	else return false;
}

bool CDataSource::GetItalicAt(int row, int col, BOOL& italic)
{
	CaplReportValue* ReportValue = GetAt(row, col);
	if (ReportValue)
	{
		italic = ReportValue->m_italic;
		return true;
	}
	else return false;
}

bool CDataSource::LoadParams(CStdioFile &file)
{
	CString str, name, value;
	int ind;

	do
	{
		if(!file.ReadString(str))
			break;
		str.TrimLeft();
		str.TrimRight();
		if(str.IsEmpty())
			continue;
		if(str.CompareNoCase(_T("[Data]"))==0)
			break;
		if(str[0]==_T(';')) continue;
		ind = str.Find(_T("="));
		if(ind<0)
			continue;
		name = str.Left(ind);
		value = str.Right(str.GetLength()-ind-1);
		CDataSource::ReplaceBreaks(value);
		m_RParams.Add(name, value);
	} while(str.CompareNoCase(_T("[Data]"))!=0);

	return true;
}

bool CDataSource::LoadData(CStdioFile  &file)
{
	CString str;
	bool bUseQuote = false;
	int cols=0;

	while(1)
	{
		if(!file.ReadString(str))
			return false;
		if(!str.IsEmpty()) break;
	}
	
	if(str[0]==_T('\"'))
		bUseQuote = true;
	if(-1==str.Find(m_Separator))
		m_Separator=_T(',');
	CHeader* header = new CHeader(NULL);
	CString name;
	while(GetNextWord(str, name, bUseQuote))
	{
		header->AddColumn(name, aplSTRING);
		cols++;
	}
	m_cols = cols;
	CHeader* pHeader = SetHeader(header);
	if(pHeader)
		delete pHeader;
	CString tmp_val;
	while(file.ReadString(str))
	{
		str.TrimLeft();
		str.TrimRight();
		if(str.CompareNoCase(_T("[params]"))==0)
			break;
		CTableRow* row = new CTableRow;
		CString value;
		for(int i=0; i<cols;i++)
		{
			GetNextWord(str, value, bUseQuote);
			ReplaceBreaks(value);
			row->AddColumn(value);
		}
		AddRow(row);
	}

	return true;
}

bool CDataSource::LoadFromFile(CString &file_name)
{
	RemoveAll();

	CString ext;
	if(file_name.ReverseFind(_T('.'))>-1)
	{
		ext = file_name.Right(file_name.GetLength()-file_name.ReverseFind(_T('.'))-1);
		if(ext.CompareNoCase(_T("xml"))==0)
			return LoadXMLFile(file_name);
	}

	CStdioFile f;
	if(!f.Open(file_name,CFile::modeRead))
	{
		CString sMsg;
		sMsg.Format(APL_T("Не удалось открыть файл на чтение!\r\n%s"), file_name);
		AfxMessageBox(sMsg, MB_ICONERROR);
		return false;
	}
	CString str;
	int cols=0;
	bool bUseQuote = false;
	if(!f.ReadString(str))
	{
		f.Close();
		CString sMsg;
		sMsg.Format(APL_T("Файл %s открыт на чтение, но прочитать его не удалось!"), file_name);
		AfxMessageBox(sMsg, MB_ICONERROR);
		return false;
	}
	bool bAplDSF = false;
	bool bData = false;
	bool bParams = false;
	str.TrimLeft();
	str.TrimRight();
	if(str.CompareNoCase(_T("Data Source File"))==0)
	{
		do 
		{
			if(!f.ReadString(str))
			{
				f.Close();
				CString sMsg;
				sMsg.Format(APL_T("Файл %s открыт на чтение, но повторно прочитать его не удалось!"), file_name);
				AfxMessageBox(sMsg, MB_ICONERROR);
				return false;
			}
			str.TrimLeft();
			str.TrimRight();
			bData = str.CompareNoCase(_T("[Data]"))==0;
			bParams = str.CompareNoCase(_T("[Params]"))==0;
		} while(bData==false && bParams==false);
		
		bAplDSF = true;
	}

	if(bParams)
	{
		LoadParams(f);
		LoadData(f);
	}
	else if(bData)
	{
		LoadData(f);
		LoadParams(f);
	}
	
	f.Close();
	return true;
}


bool CDataSource::LoadXMLFile(LPCTSTR FileName)
{
	CHeader* header = new CHeader(NULL);
	CHeader* pHeader = SetHeader(header);
	if(pHeader)
		delete pHeader;

	CaplXMLFile XMLFile;
	if(!XMLFile.LoadFromFile(FileName)) return false;
	//Проверяем наш ли это XML
	if(XMLFile.root.name.CompareNoCase(_T("data_source"))!=0) return false;
	
	CaplXMLNode* pNode = NULL;
	CaplXMLNode* pColumnsNode = NULL;
	CaplXMLNode* pDataNode = NULL;
	CaplXMLNode* pRowsNode = NULL;
	CaplXMLNode* pRowNode = NULL;
	CaplXMLNode* pValuesNode = NULL;
	CaplXMLNode* pValueNode = NULL;
	int i, j, k, ind, index;
	CString buf, value;
	CHeaderColumn* val;
	CTableRow* pRow;
	//Ищем субноде с именем columns
	for(i=0; i<XMLFile.root.subnodes.GetSize(); i++)
	{
		pNode = XMLFile.root.subnodes[i];
		if(pNode->name.CompareNoCase(_T("columns"))==0)
			pColumnsNode = pNode;
		else if(pNode->name.CompareNoCase(_T("data"))==0)
			pDataNode = pNode;
	}
	if(pColumnsNode==NULL) return false;
	if(pDataNode==NULL) return false;

	for(i=0; i<pDataNode->subnodes.GetSize(); i++)
	{
		pNode = pDataNode->subnodes[i];
		if(pNode->name.CompareNoCase(_T("rows"))==0)
		{
			pRowsNode = pNode;
			break;
		}
	}

	if(pRowsNode==NULL) return false;

	//читаем заголовок
	CMap<int, int, int, int> IndexMap;
	CArray<int, int> IntArray;
	bool bUnique;
	//проверяем индексы на уникальность
	for(i=0; i<pColumnsNode->subnodes.GetSize(); i++)
	{
		pNode = pColumnsNode->subnodes[i];
		buf = pNode->GetParam(_T("ind"));
		ind = _atoi(buf);
		bUnique = true;
		for(j=0; j<IntArray.GetSize(); j++)
		{
			if(IntArray[j]==ind)
			{
				bUnique = false;
				AfxMessageBox(APL_T("Индексы колонок источника данных не уникальны!"));
				return false;
			}
		}
		IntArray.Add(ind);
	}
	for(i=0; i<pColumnsNode->subnodes.GetSize(); i++)
	{
		pNode = pColumnsNode->subnodes[i];
		buf = pNode->GetParam(_T("name"));
		val = new CHeaderColumn(NULL);
		val->m_name = buf;
		buf = pNode->GetParam(_T("type"));
		val->m_type = _atoi(buf);
		buf = pNode->GetParam(_T("ind"));
		ind = _atoi(buf);
		IndexMap[ind] = header->AddColumn(val);
	}
	//читаем данные и согласуем их с нашим заголовком
	//определяем нашу строку
	for(i=0; i<pRowsNode->subnodes.GetSize(); i++)
	{
		pRowNode = pRowsNode->subnodes[i]; //это наша строка
		if(pRowNode->name.CompareNoCase(_T("row"))!=0) continue;
		pRow = m_Data.GetAt(AddRow());
		for(j=0; j<pRowNode->subnodes.GetSize(); j++)
		{
			pValuesNode = pRowNode->subnodes[j];
			if(pValuesNode->name.CompareNoCase(_T("values"))!=0) continue;
			for(k=0; k<pValuesNode->subnodes.GetSize(); k++)
			{
				pValueNode = pValuesNode->subnodes[k];
				ind = _atoi(pValueNode->GetParam(_T("ind")));
				if(IndexMap.Lookup(ind, index)!=TRUE)
				{
					AfxMessageBox(APL_T("В строке присутствует ссылка на несуществующий индекс поля"));
					return false;
				}
				pRow->SetAt(index, pValueNode->GetParam(_T("value")));
			}
		}
	}
	
	return true;
}

CHeaderColumn* CDataSource::GetColumn(int i)
{
	if(m_header==0) return NULL;
	return m_header->GetColumn(i);
}

int CDataSource::GetColumnInd(CHeaderColumn* col)
{
	if(m_header==NULL) return -2;//Header not valid
	if(col==NULL) return -3;//Колонка не задана. это может быть уровень!
	return m_header->Find(col->GetName());
}

CaplReportValue* CDataSource::GetAt(int row, int col)
{
	if(row<0 || row>=m_rows) return NULL;
	CTableRow* r = m_Data.GetAt(row);
	return r->GetColumn(col);
}

CaplReportValue* CDataSource::GetAt(int row, CHeaderColumn *col)
{
	if(row<0 || row>=m_rows) return NULL;
	if(m_header==0) return NULL;
	if(col==NULL) return NULL;
	CTableRow* r = m_Data.GetAt(row);
	int ind = m_header->Find(col->GetName());
	return r->GetColumn(ind);
}

CTableRow* CDataSource::GetRow(int ind)
{
	if(ind<0 || ind>=m_rows) return NULL;
	return m_Data.GetAt(ind);
}

bool CDataSource::Extend(CHeader *add, CaplNetStepData* data)
{
	int cnt = add->GetSize();
	for(int i=0; i<cnt;i++)
	{
		CHeaderColumn* col = add->GetColumn(i);
		CString name = col->m_name;
		if(col->m_col_type==APL_ATTR_COLUMN)
		{
			if(data==0) continue;
			int ind1 = name.Find(_T(".")), ind2 = name.Find(_T("!")), ind=-1;
			if(ind1>-1 && ind2>-1)
				ind = ind1 > ind2 ? ind2 : ind1;
			else if(ind1>-1)
				ind = ind1;
			else
				ind = ind2;
			if(ind<0) continue;
			CString attr = name.Right(name.GetLength()-ind);
			if(attr.IsEmpty())
				continue;
			name = 	name.Left(ind);
			CaplReportValue* val = NULL;
			CHeaderColumn* ncol = new CHeaderColumn(NULL);
			ncol->m_name = col->m_name;
			int pos = m_header->Find(name);
			ASSERT(pos>=0);
			m_header->AddColumn(ncol);
			int tmp_cnt = m_Data.GetSize();
			CString Entity, Attr, buf1;
			for(int j=0; j<tmp_cnt;j++)
			{
				CaplReportValue* nval = new CaplReportValue;
				CTableRow* row = m_Data.GetAt(j);
				val = row->GetColumn(pos);
				if(val==0) continue;
				buf1 = attr;
				if(val->type==aplINSTANCE)
				{
					CaplInstance* inst;
					val->Get(inst);
					if(inst)
					{
						if(!ParseAttr(buf1, Entity, Attr))
						{
							throw(CString(APL_T("Неверно задан атрибут или ентити!")));
							return false;
						}
						if(!Entity.IsEmpty())
						{
							CaplEntity *ent = data->GetEntityBN(Entity);
							if(!data->IsKindOf(inst, ent))
							{
								row->AddColumn(_T(""));
								continue;
							}
						}
						nval->Set(GetAttrEx(inst,Attr.GetBuffer(0),data));
					}
				}
				row->AddColumn(nval);
			}
		}
		else if(col->m_col_type==APL_COMBINE_COLUMN)
		{
			int ind = name.Find(_T("="));
			if(ind<0) continue;
			CString tfunct, funct = name.Right(name.GetLength()-ind-1);
			CString val;
			tfunct = funct;
			m_header->AddColumn(col->GetName(),aplSTRING);
			int tmp_cnt = m_Data.GetSize();
			for(int j=0; j<tmp_cnt;j++)
			{
				CTableRow* row = m_Data.GetAt(j);
				funct = tfunct;
				val.Empty();
				if(!ParseFunction(funct, val, row))
				{
					throw(CString(APL_T("Неверно задана функция!")));//Не найдено поле.
					return false;
				}
				row->AddColumn(val);
			}
		}
	}
	m_cols = m_header->GetSize();

	return true;
}

int CDataSource::Compare(CaplReportValue* val1, CaplReportValue* val2, bool bStringCompareAsString)
{
	if(val1==NULL || val2==NULL) return 0;

	if(val1->type!=val2->type) return 0;
	if(val1->type==aplINTEGER)
	{
		int ival1, ival2;
		val1->Get(ival1);
		val2->Get(ival2);
		if(ival1>ival2)	return 1;
		else if(ival1<ival2) return -1;
	}
	else if(val1->type==aplSTRING && !bStringCompareAsString)
	{
		CString str1, str2;
		val1->Get(str1);
		val2->Get(str2);
		//проверка на числа...
		int ind = str1.GetLength()-1;
		if(ind<0)
		{
			if(str1>str2) return 1;
			else if(str1<str2) return -1;
			return 0;
		}
		//строки с числом.
		CString tStr1 = str1, tStr2 = str2;
		while(1)
		{
			if(str1.IsEmpty())
				break;
			ind = 0;
			bool bCompare = true;
			while(!iswdigit(str1[ind]))
			{
				ind++;
				if(ind==str1.GetLength())
				{
					bCompare = false;
					break;
				}
			}
			if(str2.GetLength()-1<ind)
				bCompare = false;
			else if(!iswdigit(str2[ind]))
				bCompare = false;
			if(bCompare)
			{
				CString buf1 = str1.Left(ind), buf2 = str2.Left(ind), buf;
				if(buf1==buf2)
				{
					buf =  buf1;
					str1.TrimLeft(buf); buf1 = str1;
					str2.TrimLeft(buf); buf2 = str2;
					//Выдергиваем числа...
					//первая строка
					int ind1 = 0;
					while(iswdigit(buf1[ind1]))
					{
						ind1++;
						if(ind1>=buf1.GetLength())
							break;
					}
					//вторая строка
					int ind2 = 0;
					while(iswdigit(buf2[ind2]))
					{
						ind2++;
						if(ind2>=buf2.GetLength())
							break;
					}
					buf1 = buf1.Left(ind1);
					buf2 = buf2.Left(ind2);
					str1 = str1.Right(str1.GetLength()-ind1);
					str2 = str2.Right(str2.GetLength()-ind2);
					int i1 = _atoi(buf1);
					int i2 = _atoi(buf2);
					CString tmp1, tmp2;
					tmp1.Format(_T("%d"), i1);
					tmp2.Format(_T("%d"), i2);
					if(i1>i2)
					{
						return 1;
					}
					else if(i1<i2)
					{
						return -1;
					}
					else if((tmp1!=buf || tmp2!=buf) && !buf.IsEmpty())
					{
						if(buf1>buf2) return 1;
						else if(buf1<buf2) return -1;
					}
				}
				else
					break;
			}
			else
				break;
		}
		if(tStr1>tStr2) return 1;
		else if(tStr1<tStr2) return -1;
	}
	else if(val1->type==aplSTRING && bStringCompareAsString)
	{
		CString str1, str2;
		val1->Get(str1);
		val2->Get(str2);
		if(str1>str2) return 1;
		else if(str1<str2) return -1;
	}
	else if(val1->type==aplREAL)
	{
		double dval1, dval2;
		val1->Get(dval1);
		val2->Get(dval2);
		if(dval1>dval2) return 1;
		else if(dval1<dval2)return -1;
	}
	return 0;
}

bool CDataSource::SaveToBuf(CString &buf, bool bGenerated /*= true*/)
{
	buf.Empty();
	CString sec, tmp, str;
	CReportParam* Param;
	CaplRParam* par;
	int i, cnt;
	sec = _T("Data Source File\n");
	buf+=sec;
	
	sec = _T("[Params]\n");
	buf+=sec;
	if (bGenerated)
	{
		for(i=0; i<m_params.GetSize(); i++)
		{
			Param = m_params.GetAt(i);
			tmp.Format(_T("%s=%s\n"),Param->m_name,Param->GetText());
			buf+=tmp;
		}
	}
	else
	{
		for(i=0; i<m_RParams.GetSize(); i++)
		{
			par = m_RParams[i];
			tmp.Format(_T("%s=%s\n"), m_RParams[i]->m_name, m_RParams[i]->m_value);
			buf+=tmp;
		}
	}
	sec = _T("[Data]\n");
	buf+=sec;

	if(m_header==NULL) return true;
	int h_cnt = m_header->GetSize();
	for(i=0; i<h_cnt;i++)
	{
		CHeaderColumn* col = m_header->GetColumn(i);			
		tmp = col->GetName();
		tmp.Replace(_T("\""),_T("\"\""));
		str.Format(_T("\"%s\""), tmp);
		if(i<h_cnt-1)
			str+=m_Separator;
		buf+=str;
	}
	str = _T("\n");
	buf+=str;
	cnt = m_Data.GetSize();
	for(i=0; i<cnt;i++)
	{
		CTableRow* row = m_Data.GetAt(i);			
		for(int j=0; j<m_cols;j++)
		{
			CaplReportValue* val = row->GetColumn(j);
			tmp = GetAsString(val);
			tmp.Replace(_T("\""),_T("\"\""));
			tmp.Replace(_T("\\n"),_T("\\\\n"));
			tmp.Replace(_T("\n"),_T("\\n"));
			tmp.Replace(_T("\r"),_T(""));
			CString str_buf = _T("\"")+tmp+_T("\"");
			if(j<m_cols-1)
				str_buf+=m_Separator;
			buf+=str_buf;
		}
		buf+=CString(_T("\n"));
	}
	return true;
}

bool CDataSource::SaveToFile(CString file_name, bool bCSVFormat /*= false*/)
{
	if(m_header == NULL) return false;
	
	int cnt = m_Data.GetSize();
	int h_cnt, i;
	CString buf, str;
	CHeaderColumn* col;
	CaplRParam* par;
	CFile f;

	if(!f.Open(file_name,CFile::modeCreate|CFile::modeWrite))
	{
		TRACE_TO_FILE(APL_T("Не могу создать файл ")+file_name);
		return false;
	}
	if (!bCSVFormat)
	{
		str = _T("Data Source File\n");
		f.Write(str.GetBuffer(str.GetLength()),str.GetLength());

		str = _T("[Params]\n");
		f.Write(str.GetBuffer(str.GetLength()),str.GetLength());
		for(i=0; i<m_RParams.GetSize(); i++)
		{
			par = m_RParams[i];
			str.Format(_T("%s=%s\n"), m_RParams[i]->m_name, m_RParams[i]->m_value);
			f.Write(str.GetBuffer(str.GetLength()),str.GetLength());
		}
	
		str = _T("[Data]\n");
		f.Write(str.GetBuffer(str.GetLength()),str.GetLength());
	}

	h_cnt = m_header->GetSize();
	for(i=0; i<h_cnt;i++)
	{
		col = m_header->GetColumn(i);			
		buf = col->GetName();
		buf.Replace(_T("\""),_T("\"\""));
		str = _T("\"")+buf+_T("\"");
		if(i<h_cnt-1)
			str+=m_Separator;
		f.Write(str.GetBuffer(str.GetLength()),str.GetLength());
	}
	str = _T("\n");
	f.Write(str.GetBuffer(str.GetLength()),str.GetLength());
	for(i=0; i<cnt;i++)
	{
		CTableRow* row = m_Data.GetAt(i);			
		for(int j=0; j<m_cols;j++)
		{
			CaplReportValue* val = row->GetColumn(j);
			CString buf_str;
			buf_str = GetAsString(val);
			buf_str.Replace(_T("\""),_T("\"\""));
			buf_str.Replace(_T("\\"), _T("\\\\"));
			buf_str.Replace(_T("\n"), _T("\\n"));
			buf_str.Replace(_T("\r"), _T(""));
			CString str_L = _T("\"")+buf_str+_T("\"");
			if(j<m_cols-1)
				str_L+=m_Separator;
			f.Write(str_L.GetBuffer(str_L.GetLength()), str_L.GetLength());
		}
		str = _T("\n");
		f.Write(str.GetBuffer(str.GetLength()),str.GetLength());
	}
	f.Close();
	return true;
}

bool CDataSource::GetFunctionValue(CString &sFunction, CString &val, CTableRow *row, CHeader* header, CHeader* header_ex, CParamArray *params, CDataSource* pDS)
{
	int Ind = header->Find(sFunction);
	if(Ind<0 && header_ex!=NULL)
		Ind = header_ex->Find(sFunction);
	if(Ind<0)
	{
		if(sFunction[0]==_T('$'))
		{
			sFunction = sFunction.Right(sFunction.GetLength()-1);
			Ind = params->Find(sFunction);
			if(Ind<0)
				return false;
			else
				val = params->GetAt(Ind)->GetText();
		}
		else
		{
			return false;
//			return ParseFunction(sFunction, val, row, header, header_ex, params, pDS);
		}
	}
	else
	{
		if(row && pDS)
		{
			Ind = pDS->GetHeader()->Find(sFunction);
			val = row->GetAsString(Ind);
		}
	}

	return true; 
}

bool CDataSource::ParseFunction(CString funct, CString &val, CTableRow *row)
{
	return ParseFunction(funct, val, row, m_header, NULL, &m_params, this);
}

bool CDataSource::ParseFunction(CString funct, CString &val, CTableRow *row, CHeader* header, CHeader* header_ex, CParamArray *params, CDataSource* pDS)
{
	if(header==NULL) return false;
	if(params==NULL) return false;
	bool bIsParamInd = false;
	val.Empty();
	while(funct.GetLength()>0)
	{
		bIsParamInd = false;
		if(funct[0]==_T(' '))//просто пробел
			funct = funct.Right(funct.GetLength()-1);
		else if(funct[0]==_T('+'))//просто сложение
			funct = funct.Right(funct.GetLength()-1);
		else if(funct[0]==_T('\"'))//Начало строковой константы
		{
			//Выцепляем все слово
			funct = funct.Right(funct.GetLength()-1);
			CString str = _T("");
			int pos = 0;
			while(1)
			{
				pos = funct.Find(_T("\""), pos);
				if(pos<0) return false;
				if(pos<funct.GetLength()-1)
				{
					if(funct[pos+1]==_T('\"'))
						pos+=2;
					else
						break;
				}
				else break;
			}
			if(pos<0) return false;
			str = funct.Mid(0, pos);
			str.Replace(_T("\\n"),_T("\n"));
			str.Replace(_T("\"\""), _T("\""));
			funct = funct.Right(funct.GetLength()-pos-1);
			val+=str;
		}
		else if(funct[0]==_T('?'))//Условный оператор
		{
			funct = funct.Right(funct.GetLength()-1);
			if(funct.GetLength()==0) return false;
			if(funct[0]!=_T('(')) return false;
			int sCnt=0;
			CString word;
			for(int i=0; i<funct.GetLength(); i++)
			{
				if(funct[i]==_T('('))
					sCnt++;
				else if(funct[i]==_T(')'))
					sCnt--;
				if(sCnt==0)
				{
					word = funct.Mid(1, i-1);
					funct = funct.Right(funct.GetLength()-i-1);
					break;
				}
			}
			//Из полученного слова в скобках выцепляем имя
			CString col_name;
			for(int i=0; i<word.GetLength(); i++)
			{
				if(word[i]==_T('('))
					sCnt++;
				else if(word[i]==_T(')'))
					sCnt--;
				if(sCnt==0 && (word[i]==_T(',') || word[i]==_T('!') || word[i]==_T('=')))
				{
					col_name = word.Left(i);
					if(word[i]==_T(','))
						word = word.Right(word.GetLength()-i-1);
					else
						word = word.Right(word.GetLength()-i);
					break;
				}
			}
			col_name.Trim(_T(" "));
			//Сюда надо вставить проверку на имя и на значение
//			int Ind(-1);
			CString sub_word;
			CString tmp_sub_val;
			CString tmp_val;
			CString tmp_val_L;

			if(!GetFunctionValue(col_name, tmp_val_L, row, header, header_ex, params, pDS))
				return false;

			bool bNot = false;
			if(!word.IsEmpty())//Пытаемся выцепить слово для сравнения если оно есть.
			{
				if(word[0]==_T('!'))
				{
					word = word.Right(word.GetLength()-1);
					if(word.GetLength()==0) return false;
					if(word[0]!=_T('=')) return false;
					bNot = true;
				}
				if(word[0]==_T('='))
				{
					word = word.Right(word.GetLength()-1);
					word.TrimLeft(_T(" "));
					if(word.IsEmpty()) return false;

					int unit_count = 0;
					int kov_count = 0;
					int unit_ind = -1;
					int sep_ind = -1;
					int tst_ind;
					
					bool bOk = false;
					
					sep_ind = word.Find(_T(","));
					if(sep_ind<0) //это какая то фигня
						return false;
					while(!bOk)
					{
						unit_count = 0;
						kov_count = 0;
						
						sub_word = word.Left(sep_ind);
						
						//сколько начал скобок среди этих позиций
						tst_ind = -1;
						while((tst_ind=sub_word.Find(_T("("), tst_ind+1))>-1)
							unit_count++;
						tst_ind = -1;
						while((tst_ind=sub_word.Find(_T(")"), tst_ind+1))>-1)
							unit_count--;
						tst_ind = -1;
						while((tst_ind=sub_word.Find(_T("\""), tst_ind+1))>-1)
							kov_count = kov_count==1?0:1;
						
						if(unit_count==0 && kov_count==0) break;
						sep_ind = word.Find(_T(","), sep_ind+1);
						if(sep_ind<0) return false;
					}
					//Все нормально продолжаем работать
					if(!ParseFunction(sub_word, tmp_sub_val, row, header, header_ex, params, pDS)) return false;
					word = word.Right(word.GetLength()-sep_ind-1);
					if(row!=NULL)
					{
						//Проверяем доступность значений
						if(!bNot)
						{
							if(tmp_val_L.CompareNoCase(tmp_sub_val)==0)
							{
								if(!ParseFunction(word, tmp_val, row, header, header_ex, params, pDS)) return false;
								val+=tmp_val;
							}
						}
						else
						{
							if(tmp_val_L.CompareNoCase(tmp_sub_val)!=0)
							{
								if(!ParseFunction(word, tmp_val_L, row, header, header_ex, params, pDS)) return false;
								val+=tmp_val_L;
							}
						}
					}
				}
				else
				{
					if(!bNot)
					{
						if(row==NULL || ((tmp_val_L.CompareNoCase(tmp_sub_val)==0 && !tmp_sub_val.IsEmpty()) || (tmp_sub_val.IsEmpty() && !tmp_val_L.IsEmpty())))
						{
							tmp_val_L = _T("");
							if(!ParseFunction(word, tmp_val_L, row, header, header_ex, params, pDS)) return false;
							val+=tmp_val_L;
						}
					}
					else
					{
						if(row==NULL || ((tmp_val_L.CompareNoCase(tmp_sub_val)!=0 && !tmp_sub_val.IsEmpty()) || (tmp_sub_val.IsEmpty() && tmp_val_L.IsEmpty())))
						{
							tmp_val = _T("");
							if(!ParseFunction(word, tmp_val, row, header, header_ex, params, pDS)) return false;
							val+=tmp_val;
						}
					}
				}
			}
		}
		else if(funct[0]==_T('!'))//Условный оператор Not
		{
			funct = funct.Right(funct.GetLength()-1);
			if(funct.GetLength()==0) return false;
			if(funct[0]!=_T('(')) return false;
			int sCnt=0;
			CString word;
			for(int i=0; i<funct.GetLength(); i++)
			{
				if(funct[i]==_T('('))
					sCnt++;
				else if(funct[i]==_T(')'))
					sCnt--;
				if(sCnt==0)
				{
					word = funct.Mid(1, i-1);
					funct = funct.Right(funct.GetLength()-i-1);
					break;
				}
			}
			if(sCnt!=0) return false;
			//Из полученного слова в скобках выцепляем имя
			CString col_name;
			int pos = word.Find(_T(","),0);
			if(pos<0) return false;
			col_name = word.Left(pos);
			col_name.Trim(_T(" "));
			//Сюда надо вставить проверку на имя и на значение
			int Ind = header->Find(col_name);
			if(Ind<0) return false;
			CString tmp_val;
			if(row)
			{
				tmp_val = row->GetAsString(Ind);
			}
			word = word.Right(word.GetLength()-pos-1);
			if(tmp_val.IsEmpty() || row==0)
			{
				tmp_val.Empty();
				if(!ParseFunction(word, tmp_val, row, header, header_ex, params, pDS)) return false;
				val+=tmp_val;
			}
		}
		else if(funct[0]==_T('%'))//Значет это ключевое слово
		{
			funct = funct.Right(funct.GetLength()-1);
			if(funct.GetLength()==0) return false;
			CString FunctName;
			int ind = funct.Find(_T("("));
			if(ind<0) return false;
			FunctName = funct.Left(ind);
			funct = funct.Right(funct.GetLength()-ind);
			//Определяем слово в скобках
			CString word;
			int sCnt=0;
			for(int i=0; i<funct.GetLength(); i++)
			{
				if(funct[i]==_T('('))
					sCnt++;
				else if(funct[i]==_T(')'))
					sCnt--;
				if(sCnt==0)
				{
					word = funct.Mid(1, i-1);
					funct = funct.Right(funct.GetLength()-i-1);
					break;
				}
			}
			if(sCnt!=0) return false;
			//Из полученного слова формируем массив полей.
			if(FunctName.Compare(_T("SUMM"))==0)
			{
				CStringArray Columns;
				CString col_name;
				while((ind=word.Find(_T(",")))>-1)
				{
					col_name = word.Left(ind);
					col_name.TrimLeft(_T(" "));
					col_name.TrimRight(_T(" "));
					word = word.Right(word.GetLength()-ind-1);
					if(!col_name.IsEmpty())
						Columns.Add(col_name);
				}
				word.TrimLeft(_T(" "));
				word.TrimRight(_T(" "));
				if(!word.IsEmpty())
					Columns.Add(word);

				double Summ = 0;
				double dVal;
				CString strVal;

				for(int i=0; i<Columns.GetSize(); i++)
				{
					dVal = 0;
					ind = header->Find(Columns[i]);
					if(ind<0) return false;
					if(row)
					{
						strVal = row->GetAsString(ind);
						if(IsRealOrEmpty(strVal))
							dVal = __atof(strVal);
						Summ+=dVal;
					}
				}
				GetDoubleAsString(Summ, val);
			}
			if(FunctName.Compare(_T("MULTIPLY"))==0)
			{
				CStringArray Columns;
				CString col_name;
				while((ind=word.Find(_T(",")))>-1)
				{
					col_name = word.Left(ind);
					col_name.Trim(_T(" "));
					word = word.Right(word.GetLength()-ind-1);
					if(!col_name.IsEmpty())
						Columns.Add(col_name);
				}
				word.Trim(_T(" "));
				if(!word.IsEmpty())
					Columns.Add(word);
				
				double Summ = 1;
				double dVal;
				CString strVal;
				
				for(int i=0; i<Columns.GetSize(); i++)
				{
					dVal = 0;
					ind = header->Find(Columns[i]);
					if(ind<0)
					{
						if(!IsRealOrEmpty(Columns[i]))
							return false;
					}
					if(row)
					{
						strVal = row->GetAsString(ind);
						if(IsRealOrEmpty(strVal))
							dVal = __atof(strVal);
						Summ*=dVal;
					}
				}
				GetDoubleAsString(Summ, val);
			}
			else if(FunctName.Compare(_T("DIVIDE"))==0)
			{
				CStringArray Columns;
				CString col_name;
				while((ind=word.Find(_T(",")))>-1)
				{
					col_name = word.Left(ind);
					col_name.TrimLeft(_T(" "));
					col_name.TrimRight(_T(" "));
					word = word.Right(word.GetLength()-ind-1);
					if(!col_name.IsEmpty())
						Columns.Add(col_name);
				}
				word.TrimLeft(_T(" "));
				word.TrimRight(_T(" "));
				if(!word.IsEmpty())
					Columns.Add(word);

				double Summ = 0;
				double dVal;
				CString strVal;

				for(int i=0; i<Columns.GetSize(); i++)
				{
					dVal = 0;
					ind = header->Find(Columns[i]);
					if(ind<0)
					{
						if(!IsRealOrEmpty(Columns[i]))
							return false;
					}
					if(row)
					{
						if(ind>-1)
						{
							strVal = row->GetAsString(ind);
						}
						else
							strVal = Columns[i];
						if(IsRealOrEmpty(strVal))
							dVal = __atof(strVal);
						if(i==0)
							Summ = dVal;
						else if(dVal!=0)
							Summ/=dVal;
						else
						{
							val = _T("1.#");
							break;
						}
					}
				}
				GetDoubleAsString(Summ, val);
			}
			else if(FunctName.Compare(_T("AVERAGE"))==0)//Среднее арифметическое
			{
				CStringArray Columns;
				CString col_name;
				while((ind=word.Find(_T(",")))>-1)
				{
					col_name = word.Left(ind);
					col_name.TrimLeft(_T(" "));
					col_name.TrimRight(_T(" "));
					word = word.Right(word.GetLength()-ind-1);
					if(!col_name.IsEmpty())
						Columns.Add(col_name);
				}
				if(!word.IsEmpty())
					Columns.Add(word);

				double Summ = 0;
				double dVal;
				CString strVal;

				for(int i=0; i<Columns.GetSize(); i++)
				{
					dVal = 0;
					ind = header->Find(Columns[i]);
					if(ind<0) return false;
					if(row)
					{
						strVal = row->GetAsString(ind);
						if(IsRealOrEmpty(strVal))
							dVal = __atof(strVal);
						Summ+=dVal;
					}
				}
				double average = Summ/Columns.GetSize();
				GetDoubleAsString(average, val);
			}
			else if(FunctName.Compare(_T("LEFT"))==0)//Вырезать подстроку слева
			{
				//определяем поле
				CString col_name;
				CString sCount;
				CString strVal;
				int count;
				ind=word.Find(_T(","));
				if(ind>-1)
				{
					col_name = word.Left(ind);
					col_name.TrimLeft(_T(" "));
					col_name.TrimRight(_T(" "));
					word = word.Right(word.GetLength()-ind-1);
					if(col_name.IsEmpty()) return false;
				}
				else return false;
				//определяем количество символов
				ind=word.GetLength();
				if(ind>0)
				{
					sCount = word.Left(ind);
					sCount.TrimLeft(_T(" "));
					sCount.TrimRight(_T(" "));
					word = word.Right(word.GetLength()-ind-1);
					if(sCount.IsEmpty()) return false;
				}
				else return false;
				count = _atoi(sCount);
				
				ind = header->Find(col_name);
				if(ind<0) return false;
				if(row)
				{
					strVal = row->GetAsString(ind);
					val += strVal.Left(count);
				}
			}
			else if(FunctName.Compare(_T("RIGHT"))==0)//Вырезать подстроку справа
			{
				//определяем поле
				CString col_name;
				CString sCount;
				CString strVal;
				int count;
				ind=word.Find(_T(","));
				if(ind>-1)
				{
					col_name = word.Left(ind);
					col_name.TrimLeft(_T(" "));
					col_name.TrimRight(_T(" "));
					word = word.Right(word.GetLength()-ind-1);
					if(col_name.IsEmpty()) return false;
				}
				else return false;
				//определяем количество символов
				ind=word.GetLength();
				if(ind>0)
				{
					sCount = word.Left(ind);
					sCount.TrimLeft(_T(" "));
					sCount.TrimRight(_T(" "));
					word = word.Right(word.GetLength()-ind-1);
					if(sCount.IsEmpty()) return false;
				}
				else return false;
				count = _atoi(sCount);
				
				ind = header->Find(col_name);
				if(ind<0) return false;
				if(row)
				{
					strVal = row->GetAsString(ind);
					val += strVal.Right(count);
				}
			}
			else if(FunctName.Compare(_T("MID"))==0)//Вырезать подстроку справа
			{
				//определяем поле
				CString col_name;
				CString sStart;
				CString sCount;
				CString strVal;
				int iCount, iStart;
				ind=word.Find(_T(","));
				if(ind>-1)
				{
					col_name = word.Left(ind);
					col_name.TrimLeft(_T(" "));
					col_name.TrimRight(_T(" "));
					word = word.Right(word.GetLength()-ind-1);
					if(col_name.IsEmpty()) return false;
				}
				else return false;
				//определяем начальный символ
				ind=word.Find(_T(","));
				if(ind>-1)
				{
					sStart = word.Left(ind);
					sStart.TrimLeft(_T(" "));
					sStart.TrimRight(_T(" "));
					word = word.Right(word.GetLength()-ind-1);
					if(sStart.IsEmpty()) return false;
				}
				else return false;
				iStart = _atoi(sStart);
				//количество символов
				ind=word.GetLength();
				if(ind>0)
				{
					sCount = word.Left(ind);
					sCount.TrimLeft(_T(" "));
					sCount.TrimRight(_T(" "));
					word = word.Right(word.GetLength()-ind-1);
					if(sCount.IsEmpty()) return false;
				}
				else return false;
				iCount = _atoi(sCount);
				
				ind = header->Find(col_name);
				if(ind<0) return false;
				if(row)
				{
					strVal = row->GetAsString(ind);
					val += strVal.Mid(iStart-1, iCount);
				}
			}
			else if(FunctName.Compare(_T("FIND"))==0)//Найти в таблице
			{
				CString res_column, res_value;
				CString if_column, if_value;
				ind = -1;
				int Ind;
				bool bNot = false;

				word.TrimLeft(_T(" "));
				ind = word.Find(_T("="));
				if(ind<0) return false;
				if_column = word.Left(ind);
				Ind = header->Find(if_column);
				if(Ind<0) return false;
				word = word.Right(word.GetLength()-ind);
				if(!word.IsEmpty())//Пытаемся выцепить слово для сравнения если оно есть.
				{
					if(word[0]==_T('!'))
					{
						word = word.Right(word.GetLength()-1);
						if(word.GetLength()==0) return false;
						if(word[0]!=_T('=')) return false;
						bNot = true;
					}
					if(word[0]==_T('='))
					{
						word = word.Right(word.GetLength()-1);
						word.TrimLeft(_T(" "));
						if(word.IsEmpty()) return false;

						//Определяем с чем сравнивать
						int unit_count = 0;
						int kov_count = 0;
						int unit_ind = -1;
						int sep_ind = -1;
						int tst_ind;
						
						bool bOk = false;
						
						CString sub_word;
						CString tmp_sub_val;
						CString tmp_val;
						
						sep_ind = word.Find(_T(","));
						if(sep_ind<0) //это какая то фигня
							return false;
						while(!bOk)
						{
							unit_count = 0;
							kov_count = 0;
							
							sub_word = word.Left(sep_ind);
							
							//сколько начал скобок среди этих позиций
							tst_ind = -1;
							while((tst_ind=sub_word.Find(_T("("), tst_ind+1))>-1)
								unit_count++;
							tst_ind = -1;
							while((tst_ind=sub_word.Find(_T(")"), tst_ind+1))>-1)
								unit_count--;
							tst_ind = -1;
							while((tst_ind=sub_word.Find(_T("\""), tst_ind+1))>-1)
								kov_count = kov_count==1?0:1;
							
							if(unit_count==0 && kov_count==0) break;
							sep_ind = word.Find(_T(","), sep_ind+1);
							if(sep_ind<0) return false;
						}
						//Все нормально продолжаем работать
						if(!ParseFunction(sub_word, tmp_sub_val, row, header, header_ex, params, pDS)) return false;
						word = word.Right(word.GetLength()-sep_ind-1);
						
						if(word.IsEmpty()) return false;
						
						word.Trim(_T(" "));
						
						int ResColumn = header->Find(word);
						if(ResColumn<0) return false;
						
						if(row && pDS)
						{
							CTableRow *pFindedRow = pDS->FindRow(Ind, tmp_sub_val);
							if(pFindedRow)
								val = pFindedRow->GetAsString(ResColumn);
						}
					}
					else return false;
				}
				else return false;
			}
			else
			{
				AfxMessageBox(APL_T("Неизвестная функция!"));
				return false;
			}
		}
		else if(funct[0]==_T('$'))//Значет это параметр, который необходимо прибавить
		{
			funct = funct.Right(funct.GetLength()-1);
			if(funct.GetLength()==0) return false;
			int pos = funct.Find(_T("+"),0);
			if(pos<0) pos = funct.GetLength();
			CString param_name = funct.Left(pos);
			param_name.TrimRight(_T(" "));
			param_name.TrimLeft(_T(" "));
			//проверяем параметр на допустимость
			int param_ind = params->Find((CString)param_name);
			if(param_ind<0) return false;
			if(row)
				val=val+params->GetAt(param_ind)->GetText();
			funct = funct.Right(funct.GetLength()-pos);
		}
		else//Значит это имя поля
		{
			CString tmp_val;
			int pos = funct.Find(_T("+"),0);
			if(pos<0) pos = funct.GetLength();
			CString col_name = funct.Left(pos);
			col_name.TrimRight(_T(" "));
			col_name.TrimLeft(_T(" "));
			//Сюда надо вставить проверку на имя и значение
			funct = funct.Right(funct.GetLength()-pos);
			if(!GetFunctionValue(col_name, tmp_val, row, header, header_ex, params, pDS))
				return false;
			val+=tmp_val;
		}
	}
	return true;
}

#define USE_USER_LEGEND_TEXT		_T("bUseUserLegendText")
#define USER_LEGEND_TEXT			_T("sUserLegendText")
#define USE_ADD_USER_LEGEND_TEXT	_T("bUseAdditionalUserLegendText")
#define ADD_USER_LEGEND_TEXT		_T("sAdditionalUserLegendText")
#define USE_USER_COLORS				_T("bUseUserColors")
#define USER_COLORS					_T("cUserColors")
#define USE_USER_NULL_COLOR			_T("bUseUserNullColor")
#define USER_NULL_COLOR				_T("cUserNullColor")
#define SHOW_HIDE_VALUES_FLAG		_T("bShowValues")
#define VERT_HORZ_VALUES_FLAG		_T("bValuesHorz")
#define USE_GDI_PLUS_FLAG			_T("bUseGDIPlus")
#define INTEGER_MAX_Y_FLAG			_T("bIntegerMaxY")
#define TIME_Y_FORMAT_FLAG			_T("bTimeYFormat")

void CDataSource::CloneUserLegendText(CDataSource& DS, CaplReportMgr* ReportMgr)
{
	CStringArray strArr, strArrAdditional;
	CArray<COLORREF> colorArr;
	DS.GetUserLegendText(strArr);
	SetUserLegendText(DS.GetUseUserLegendText(), strArr);
	DS.GetAdditionalUserLegendText(strArrAdditional);
	SetAdditionalUserLegendText(DS.GetUseAdditionalUserLegendText(), strArrAdditional);
	DS.GetUserColors(colorArr);
	SetUserColors(DS.GetUseUserColors(), colorArr);
	SetUserNullColor(DS.GetUseUserNullColor(), DS.GetUserNullColor());
	SetShowValues(DS.GetShowValues());
	SetShowValuesHorz(DS.GetShowValuesHorz());
	SetUseGDIPlus(DS.GetUseGDIPlus());
	SetIntegerMaxY(DS.GetIntegerMaxY());
	SetTimeYFormat(DS.GetTimeYFormat());

	if(ReportMgr)
	{
		for(int i = 0; i < m_RParams.GetSize(); i++)
		{
			CaplRParam* rep_par = m_RParams.GetAt(i);
			int ind = m_params.Find(rep_par->m_name);
			if(ind < 0)
			{
				CReportParam* par = new CReportParam(ReportMgr);
				par->m_name = rep_par->m_name;
			}
		}
	}
}

void CDataSource::SetUserLegendText(bool bUseUserLegendText, CStringArray& sUserLegendText)
{
	CString sName = USE_USER_LEGEND_TEXT;
	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			if(bUseUserLegendText == true)
				m_RParams[i]->m_value = _T("1");
			else
				m_RParams[i]->m_value = _T("0");
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		if(bUseUserLegendText == true)
			m_RParams.Add(sName, _T("1"));
		else
			m_RParams.Add(sName, _T("0"));
	}

	CString sLegend;
	for(i = 0; i < sUserLegendText.GetSize(); i++)
	{
		if(!sLegend.IsEmpty())
			sLegend += _T(";");
		if(sUserLegendText[i].IsEmpty())
			sLegend += _T(" ");
		else
			sLegend += sUserLegendText[i];
	}
	sName = USER_LEGEND_TEXT;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			m_RParams[i]->m_value = sLegend;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		m_RParams.Add(sName, sLegend);
	}
}

void CDataSource::GetUserLegendText(CStringArray& sUserLegendText)
{
	CString str;
	CStringArray a;
	bool b;

	sUserLegendText.RemoveAll();
	if(GetUseUserLegendText() == false) return;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == USER_LEGEND_TEXT)
		{
			str = m_RParams[i]->m_value;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(USER_LEGEND_TEXT));
		if(ind > -1)
			str = m_params.GetAt(ind)->GetText();
	}

	Report_ConvertStrAggr2Array(str, sUserLegendText, a, b, false);
}

bool CDataSource::GetUseUserLegendText()
{
	bool ret = false;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == USE_USER_LEGEND_TEXT)
		{
			ret = m_RParams[i]->m_value == _T("1") ? true : false;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(USE_USER_LEGEND_TEXT));
		if(ind > -1)
			ret = m_params.GetAt(ind)->GetText() == _T("1") ? true : false;
	}

	return ret;
}

void CDataSource::SetAdditionalUserLegendText(bool bUseUserLegendText, CStringArray& sUserLegendText)
{
	CString sName = USE_ADD_USER_LEGEND_TEXT;
	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			if(bUseUserLegendText == true)
				m_RParams[i]->m_value = _T("1");
			else
				m_RParams[i]->m_value = _T("0");
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		if(bUseUserLegendText == true)
			m_RParams.Add(sName, _T("1"));
		else
			m_RParams.Add(sName, _T("0"));
	}

	CString sLegend;
	for(i = 0; i < sUserLegendText.GetSize(); i++)
	{
		if(!sLegend.IsEmpty())
			sLegend += _T(";");
		if(sUserLegendText[i].IsEmpty())
			sLegend += _T(" ");
		else
			sLegend += sUserLegendText[i];
	}
	sName = ADD_USER_LEGEND_TEXT;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			m_RParams[i]->m_value = sLegend;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		m_RParams.Add(sName, sLegend);
	}
}

void CDataSource::GetAdditionalUserLegendText(CStringArray& sUserLegendText)
{
	CString str;
	CStringArray a;
	bool b;

	sUserLegendText.RemoveAll();
	if(GetUseAdditionalUserLegendText() == false) return;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == ADD_USER_LEGEND_TEXT)
		{
			str = m_RParams[i]->m_value;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(ADD_USER_LEGEND_TEXT));
		if(ind > -1)
			str = m_params.GetAt(ind)->GetText();
	}

	Report_ConvertStrAggr2Array(str, sUserLegendText, a, b, false);
}

bool CDataSource::GetUseAdditionalUserLegendText()
{
	bool ret = false;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == USE_ADD_USER_LEGEND_TEXT)
		{
			ret = m_RParams[i]->m_value == _T("1") ? true : false;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(USE_ADD_USER_LEGEND_TEXT));
		if(ind > -1)
			ret = m_params.GetAt(ind)->GetText() == _T("1") ? true : false;
	}

	return ret;
}

void CDataSource::SetUserColors(bool bUseUserColors, CArray<COLORREF>& UserColors)
{
	CString sName = USE_USER_COLORS;
	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			if(bUseUserColors == true)
				m_RParams[i]->m_value = _T("1");
			else
				m_RParams[i]->m_value = _T("0");
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		if(bUseUserColors == true)
			m_RParams.Add(sName, _T("1"));
		else
			m_RParams.Add(sName, _T("0"));
	}

	CString sLegend;
	for(i = 0; i < UserColors.GetSize(); i++)
	{
		CString buf;
		buf.Format(_T("%u"), UserColors[i]);
		if(!sLegend.IsEmpty())
			sLegend += _T(";");
		sLegend += buf;
	}
	sName = USER_COLORS;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			m_RParams[i]->m_value = sLegend;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		m_RParams.Add(sName, sLegend);
	}
}

void CDataSource::GetUserColors(CArray<COLORREF>& UserColors)
{
	CString str;
	CStringArray colors, a;
	bool b;

	UserColors.RemoveAll();
	if(GetUseUserColors() == false) return;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == USER_COLORS)
		{
			str = m_RParams[i]->m_value;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(USER_COLORS));
		if(ind > -1)
			str = m_params.GetAt(ind)->GetText();
	}

	Report_ConvertStrAggr2Array(str, colors, a, b, false);
	for(i = 0; i < colors.GetSize(); i++)
		UserColors.Add((DWORD)_atoi(colors[i]));
}

bool CDataSource::GetUseUserColors()
{
	bool ret = false;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == USE_USER_COLORS)
		{
			ret = m_RParams[i]->m_value == _T("1") ? true : false;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(USE_USER_COLORS));
		if(ind > -1)
			ret = m_params.GetAt(ind)->GetText() == _T("1") ? true : false;
	}

	return ret;
}

void CDataSource::SetUserNullColor(bool bUseUserColor, COLORREF UserColor)
{
	CString sName = USE_USER_NULL_COLOR;
	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			if(bUseUserColor == true)
				m_RParams[i]->m_value = _T("1");
			else
				m_RParams[i]->m_value = _T("0");
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		if(bUseUserColor == true)
			m_RParams.Add(sName, _T("1"));
		else
			m_RParams.Add(sName, _T("0"));
	}

	CString sColor;
	sColor.Format(_T("%u"), UserColor);
	sName = USER_NULL_COLOR;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			m_RParams[i]->m_value = sColor;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		m_RParams.Add(sName, sColor);
	}
}

COLORREF CDataSource::GetUserNullColor()
{
	CString str;

	if(GetUseUserNullColor() == false) return 0;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == USER_NULL_COLOR)
		{
			str = m_RParams[i]->m_value;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(USER_NULL_COLOR));
		if(ind > -1)
			str = m_params.GetAt(ind)->GetText();
	}

	return (DWORD)_atoi(str);
}

bool CDataSource::GetUseUserNullColor()
{
	bool ret = false;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == USE_USER_NULL_COLOR)
		{
			ret = m_RParams[i]->m_value == _T("1") ? true : false;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(USE_USER_NULL_COLOR));
		if(ind > -1)
			ret = m_params.GetAt(ind)->GetText() == _T("1") ? true : false;
	}

	return ret;
}

void CDataSource::SetShowValues(bool bShowValues)
{
	CString sName = SHOW_HIDE_VALUES_FLAG;
	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			if(bShowValues == true)
				m_RParams[i]->m_value = _T("1");
			else
				m_RParams[i]->m_value = _T("0");
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		if(bShowValues == true)
			m_RParams.Add(sName, _T("1"));
		else
			m_RParams.Add(sName, _T("0"));
	}
}

bool CDataSource::GetShowValues()
{
	bool ret = false;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == SHOW_HIDE_VALUES_FLAG)
		{
			ret = m_RParams[i]->m_value == _T("1") ? true : false;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(SHOW_HIDE_VALUES_FLAG));
		if(ind > -1)
			ret = m_params.GetAt(ind)->GetText() == _T("1") ? true : false;
	}

	return ret;
}

void CDataSource::SetShowValuesHorz(bool bShowValuesHorz)
{
	CString sName = VERT_HORZ_VALUES_FLAG;
	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			if(bShowValuesHorz == true)
				m_RParams[i]->m_value = _T("1");
			else
				m_RParams[i]->m_value = _T("0");
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		if(bShowValuesHorz == true)
			m_RParams.Add(sName, _T("1"));
		else
			m_RParams.Add(sName, _T("0"));
	}
}

bool CDataSource::GetShowValuesHorz()
{
	bool ret = false;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == VERT_HORZ_VALUES_FLAG)
		{
			ret = m_RParams[i]->m_value == _T("1") ? true : false;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(VERT_HORZ_VALUES_FLAG));
		if(ind > -1)
			ret = m_params.GetAt(ind)->GetText() == _T("1") ? true : false;
	}

	return ret;
}

void CDataSource::SetUseGDIPlus(bool bUseGDIPlus)
{
	CString sName = USE_GDI_PLUS_FLAG;
	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			if(bUseGDIPlus == true)
				m_RParams[i]->m_value = _T("1");
			else
				m_RParams[i]->m_value = _T("0");
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		if(bUseGDIPlus == true)
			m_RParams.Add(sName, _T("1"));
		else
			m_RParams.Add(sName, _T("0"));
	}
}

bool CDataSource::GetUseGDIPlus()
{
	bool ret = false;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == USE_GDI_PLUS_FLAG)
		{
			ret = m_RParams[i]->m_value == _T("1") ? true : false;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(USE_GDI_PLUS_FLAG));
		if(ind > -1)
			ret = m_params.GetAt(ind)->GetText() == _T("1") ? true : false;
	}

	return ret;
}

void CDataSource::SetIntegerMaxY(bool bIntegerMaxY)
{
	CString sName = INTEGER_MAX_Y_FLAG;
	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			if(bIntegerMaxY == true)
				m_RParams[i]->m_value = _T("1");
			else
				m_RParams[i]->m_value = _T("0");
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		if(bIntegerMaxY == true)
			m_RParams.Add(sName, _T("1"));
		else
			m_RParams.Add(sName, _T("0"));
	}
}

bool CDataSource::GetIntegerMaxY()
{
	bool ret = false;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == INTEGER_MAX_Y_FLAG)
		{
			ret = m_RParams[i]->m_value == _T("1") ? true : false;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(INTEGER_MAX_Y_FLAG));
		if(ind > -1)
			ret = m_params.GetAt(ind)->GetText() == _T("1") ? true : false;
	}

	return ret;
}

void CDataSource::SetTimeYFormat(bool bTimeYFormat)
{
	CString sName = TIME_Y_FORMAT_FLAG;
	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == sName)
		{
			if(bTimeYFormat == true)
				m_RParams[i]->m_value = _T("1");
			else
				m_RParams[i]->m_value = _T("0");
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		if(bTimeYFormat == true)
			m_RParams.Add(sName, _T("1"));
		else
			m_RParams.Add(sName, _T("0"));
	}
}

bool CDataSource::GetTimeYFormat()
{
	bool ret = false;

	int i = 0;
	for(i = 0; i < m_RParams.GetSize(); i++)
	{
		if(m_RParams[i]->m_name == TIME_Y_FORMAT_FLAG)
		{
			ret = m_RParams[i]->m_value == _T("1") ? true : false;
			break;
		}
	}
	if(i == m_RParams.GetSize())
	{
		int ind = m_params.Find(CString(TIME_Y_FORMAT_FLAG));
		if(ind > -1)
			ret = m_params.GetAt(ind)->GetText() == _T("1") ? true : false;
	}

	return ret;
}

bool CDataSource::GetNextWord(CString &buf, CString &word, bool bUseQuote)
{
	int cnt = buf.GetLength();
	int QuoteCnt = 0;
	word = _T("");
	for(int i=0; i<cnt;i++)
	{
		if(buf[i]==_T('\"'))
		{
			if(i<cnt-1)
			{
				if(buf[i+1]==_T('\"'))
					QuoteCnt++;
				else if(QuoteCnt>0)
					QuoteCnt--;
				else if(QuoteCnt<=0)
					QuoteCnt++;
			}
			else
			{
				QuoteCnt--;
			}
		}
		if(buf[i]==m_Separator && QuoteCnt==0)
		{
			word = buf.Left(i);
			buf = buf.Right(cnt-i-1);
			if(bUseQuote)
			{
				if(word.GetLength()>0 && word[0]==_T('\"'))
					word = word.Right(word.GetLength()-1);
				if(word.GetLength()>0 && word[word.GetLength()-1]==_T('\"'))
					word = word.Left(word.GetLength()-1);
			}
			word.Replace(_T("\"\""), _T("\""));
			return true;
		}
	}
	if(buf.IsEmpty())
		return false;
	else
	{
		word = buf;
		buf = _T("");
		if(bUseQuote)
		{
			if(word.GetLength()>0 && word[0]==_T('\"'))
				word = word.Right(word.GetLength()-1);
			if(word.GetLength()>0 && word[word.GetLength()-1]==_T('\"'))
				word = word.Left(word.GetLength()-1);
		}
		word.Replace(_T("\"\""), _T("\""));
		return true;
	}
}

bool CDataSource::GetStringPart(CString str, CString &res)
{
	int ind = str.GetLength()-1;
	if(ind<0) return false;
	bool bIsDigit = false;
	while(iswdigit(str[ind]))
	{
		bIsDigit = true;
		ind--;
		if(ind<0) break;
	}
	if(!bIsDigit) return false;
	res = str.Left(ind+1);
	if(res==str) return false;
	return true;
}

int CDataSource::FindRow(CTableRow* row)
{
	if(row==NULL) return -1;
	int cnt = m_Data.GetSize();
	for(int i=0; i<cnt; i++)
	{
		if(row==m_Data[i]) return i;
	}
	return -1;
}

CHeader* CDataSource::GetHeader()
{
	return m_header;
}

bool CDataSource::ParseAttr(CString &Attr, CString &entity, CString &attr)
{
	attr.Empty();
	entity.Empty();
	if(Attr.IsEmpty()) return true;
	if(Attr[0]==_T('!'))
	{
		Attr = Attr.Right(Attr.GetLength()-1);
		if(Attr[0]!=_T('(')) return false;
		int ind = Attr.Find(_T(")"));
		if(ind<0) return false;
		CString buf = Attr.Mid(1, ind-1);
		Attr = Attr.Right(Attr.GetLength()-ind-1);
		ind = buf.Find(_T(","));
		if(ind<0) return false;
		entity = buf.Left(ind);
		attr = buf.Right(buf.GetLength()-ind-1);
		entity.TrimLeft(_T(" "));
		entity.TrimRight(_T(" "));
		attr.TrimLeft(_T(" "));
		attr.TrimRight(_T(" "));
	}
	else attr = Attr;
	return true;
}


bool CDataSource::GetDoubleAsString(double dVal, CString &sVal)
{
	sVal.Format(_T("%g"), dVal);
	return true;
}

bool CDataSource::LoadHeader(CString &file_name)
{
	if(m_header)
		m_header->RemoveAll();
	else
		m_header = new CHeader(NULL);
	m_params.RemoveAll();
	CStdioFile f;
	if(!f.Open(file_name,CFile::modeRead))
	{
		AfxMessageBox(APL_T("Не могу прочесть файл ")+file_name);
		return false;
	}
	CString str=_T("");
	if(f.ReadString(str))
	{
		if(str.CompareNoCase(_T("Data Source File"))==0)
		{
			do 
			{
				if(!f.ReadString(str))
					break;
			} while(str.CompareNoCase(_T("[Data]"))!=0);
			if(!f.ReadString(str))
			{
				AfxMessageBox(APL_T("Файл пуст!"));
				f.Close();
				return false;
			}
		}
		int ind = str.Find(m_Separator);
		CString column;
		CHeaderColumn* val;
		while(ind>-1)
		{
			column = str.Left(ind);
			str = str.Right(str.GetLength()-ind-1);
			ind = str.Find(m_Separator);
			column.TrimLeft(_T("\""));
			column.TrimRight(_T("\""));
			val = new CHeaderColumn(NULL);
			val->m_name = column;
			val->m_type = aplSTRING;
			m_header->AddColumn(val);
		}
		val = new CHeaderColumn(NULL);
		val->m_type = aplSTRING;
		str.TrimLeft(_T("\""));
		str.TrimRight(_T("\""));
		val->m_name = str;
		m_header->AddColumn(val);
	}
	else
	{
		AfxMessageBox(APL_T("Файл пуст!"));
		f.Close();
		return false;
	}
	f.Close();

	return true;
}

void CDataSource::Update(bool set)
{
	if(m_ReportMgr==NULL) return;

	if(set)//storing
	{
		if(m_header==NULL) return;
		if(m_inst==NULL)
			m_inst = m_ReportMgr->m_data->CreateInstance(m_ReportMgr->e_apl_data_source);

		int i;
		int j;
		aplExtent ext_to_del;
		aplExtent ext, ext1;
		CaplInstance *Column;
		CaplInstance *Value;

		//Удаляем все старые
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_ds_columns, ext);
		for(i=0; i<ext.GetSize(); i++)
		{
			ext_to_del.Add(ext[i]);
			m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_ds_column_values, ext1);
			ext_to_del.Append(ext1);
		}

		for(i=0; i<ext_to_del.GetSize(); i++)
			m_ReportMgr->m_data->DeleteInstance(ext_to_del[i]);

		//создаем новое
		ext1.Clear();
		for(i=0; i<m_header->GetSize(); i++)
		{
			Column = m_ReportMgr->m_data->CreateInstance(m_ReportMgr->e_apl_data_source_column);
			m_ReportMgr->m_data->PutAttr(Column, m_ReportMgr->a_apl_ds_column_name, m_header->GetColumn(i)->m_name);
			ext.Clear();
			for(j=0; j<m_rows; j++)
			{
				Value = m_ReportMgr->m_data->CreateInstance(m_ReportMgr->e_apl_data_source_string_value);
				m_ReportMgr->m_data->PutAttr(Value, m_ReportMgr->a_apl_dssv_value, m_Data[j]->GetAsString(i));
				ext.Add(Value);
			}
			m_ReportMgr->m_data->PutAttr(Column, m_ReportMgr->a_apl_ds_column_values, ext);
			ext1.Add(Column);
		}
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_ds_columns, ext1);
	}
	else if(m_inst!=NULL)
	{
		RemoveAll();
		m_header = new CHeader;

		int i;
		int j;
		int max_rows;
		aplExtent ext, ext1;
		double dVal;
		CString buf;
		CHeaderColumn* pColumn;
		
		max_rows = 0;
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_ds_columns, ext);
		for(i=0; i<ext.GetSize(); i++)
		{
			pColumn = new CHeaderColumn(m_ReportMgr);
			m_ReportMgr->m_data->GetAttr(ext[i], m_ReportMgr->a_apl_ds_column_name, pColumn->m_name);
			m_header->AddColumn(pColumn);

			m_ReportMgr->m_data->GetAttr(ext[i], m_ReportMgr->a_apl_ds_column_values, ext1);
			max_rows = ext1.GetSize()>max_rows?ext1.GetSize():max_rows;
		}

		m_cols = ext.GetSize();
		m_rows = max_rows;

		for(i=0; i<max_rows; i++)
			AddRow();

		for(i=0; i<m_cols; i++)
		{
			m_ReportMgr->m_data->GetAttr(ext[i], m_ReportMgr->a_apl_ds_column_values, ext1);
			for(j=0; j<ext1.GetSize(); j++)
			{
				if(m_ReportMgr->m_data->IsKindOf(ext1[j], m_ReportMgr->e_apl_data_source_string_value))
				{
					m_ReportMgr->m_data->GetAttr(ext1[j], m_ReportMgr->a_apl_dssv_value, buf);
					SetAt(j, i, buf);
				}
				else
				{
					m_ReportMgr->m_data->GetAttr(ext1[j], m_ReportMgr->a_apl_dssv_value, dVal);
					SetAt(j, i, dVal);
				}
			}
		}
	}
}

bool CDataSource::Append(CDataSource& DS)
{
	int i, r, c, j;
	DS.GetSize(r, c);
	if(c<m_cols) return false;
	CTableRow *pRow;
	CaplReportValue *pVal;
	for(i=0; i<r; i++)
	{
		pRow = new CTableRow;
		for(j=0; j<m_cols; j++)
		{
			pVal = new CaplReportValue;
			*pVal = *DS.GetAt(i, j);
			pRow->AddColumn(pVal);
		}
		m_Data.Add(pRow);
	}
	m_rows = m_Data.GetSize();

	m_RParams.Append(DS.m_RParams);

	return true;
}

bool CDataSource::CloneDataSource(CDataSource& DS, CaplReportMgr* ReportMgr /*= NULL*/)
{
	RemoveAll();

	CloneUserLegendText(DS, ReportMgr);

	int i;
	if(m_header)
		delete m_header;

	m_header = new CHeader;
	if(DS.GetHeader()!=NULL)
	{
		for(i=0; i<DS.m_header->GetSize(); i++)
			m_header->AddColumn(DS.m_header->GetColumn(i)->m_name, DS.m_header->GetColumn(i)->m_type);
		m_cols = m_header->GetSize();
	}
	else 
		m_cols = 0;

	return Append(DS);
}

bool CDataSource::ReplaceBreaks(CString &buf)
{
	int break_ind=0;
	CString tmp_val;
	while((break_ind = buf.Find(_T("\\n"), break_ind))>-1)
	{
		if(break_ind==0)
		{
			buf.SetAt(break_ind, _T('\n'));
			buf.Insert(break_ind, _T('\r'));
			tmp_val = buf.Left(break_ind+1);
			buf = buf.Right(buf.GetLength()-break_ind-1);
			buf = tmp_val+buf;
		}
		else
		{
			if(buf[break_ind-1]!=_T('\\'))
			{
				buf.SetAt(break_ind, _T('\n'));
				tmp_val = buf.Left(break_ind+1);
				buf = buf.Right(buf.GetLength()-break_ind-2);
				buf = tmp_val+buf;
				buf.Insert(break_ind, _T('\r'));
			}
			else 
			{
				tmp_val = buf.Left(break_ind);
				buf = buf.Right(buf.GetLength()-break_ind-1);
				buf = tmp_val+buf;
				break_ind++;
			}
		}
	}
	return true;
}

bool CDataSource::ParseCondition(CString condition, CTableRow* row, CHeader* header, CParamArray *params, CDataSource* pDS)
{
	if(condition.IsEmpty()) return true;
	if(header==NULL) return false;

	//поле=/!=значение
	bool bIsNot;
	int char_ind;
	int col_ind;
	int i;
	CString word;
	CString col_name;
	CString tmp_value;
	CString value;
	CStringArray strs;
	//Разбиваем на строки по "\r\n"
	condition.Replace(_T("\r\n"), _T("\n"));
	while((char_ind=condition.Find(_T("\n")))>-1)
	{
		word = condition.Left(char_ind);
		strs.Add(word);
		condition = condition.Right(condition.GetLength()-char_ind-1);
	}
	strs.Add(condition);
	for(i=0; i<strs.GetSize(); i++)
	{
		bIsNot = false;
		condition = strs[i];
		char_ind = condition.Find(_T("="));
		if(char_ind<0) //Значит проверяем, чтобы строка была не пустой
		{
			col_name = condition;
			col_ind = header->Find(col_name);
			if(col_ind<0) return false;
			if(row)
			{
				value = row->GetAsString(col_ind);
				return (tmp_value.IsEmpty()==TRUE);
			}
		}
		else if(char_ind>0)//Значит поле сравнивается с чем-то
		{
			col_name = condition.Left(char_ind);
			if(col_name[char_ind-1]==_T('!'))
			{
				bIsNot = true;
				col_name = col_name.Left(col_name.GetLength()-1);
			}
			col_ind = header->Find(col_name);
			if(col_ind<0) return false;
			word = condition.Right(condition.GetLength()-char_ind-1);

			if(!ParseFunction(word, tmp_value, row, header, NULL, params, pDS)) return false;
			if(row)
			{
				value = row->GetAsString(col_ind);
				if(bIsNot && value.CompareNoCase(tmp_value)==0) return false;
				if(!bIsNot && value.CompareNoCase(tmp_value)!=0) return false;
			}
		}
		else return false;
	}

	return true;
}

bool CDataSource::ParseCondition(CString condition, CTableRow* row)
{
	return ParseCondition(condition, row, m_header, &m_params, this);
}


//////////////////////////////////////////////////////////////////////////
//CRParamsArray::
CRParamsArray::CRParamsArray()
{
	m_Data.RemoveAll();
}

CRParamsArray::~CRParamsArray()
{
	RemoveAll();
}

int CRParamsArray::Add(CaplRParam* par)
{
	if(par==0) return -1;
	return m_Data.Add(par);
}

int CRParamsArray::Add(LPCTSTR nName, LPCTSTR nValue, int color /*= 0*/)
{
	if(nName==0 || nValue==0) return -1;
	CaplRParam* par = new CaplRParam;
	par->m_name = nName;
	par->m_value = nValue;
	par->m_color = color;
	return m_Data.Add(par);
}

int CRParamsArray::Add(LPCTSTR nName, TParamTypes type, int color /*= 0*/)
{
	if(nName==0) return -1;
	CaplRParam* par = new CaplRParam;
	par->m_name = nName;
	par->m_type = type;
	par->m_color = color;
	return m_Data.Add(par);
}

CaplRParam* CRParamsArray::GetAt(int i)
{
	if(i<0) return NULL;
	return m_Data.GetAt(i);
}

int CRParamsArray::GetSize()
{
	return m_Data.GetSize();
}

void CRParamsArray::RemoveAll()
{
	int cnt = m_Data.GetSize();
	for(int i=0; i<cnt;i++)
		if(m_Data.GetAt(i)!=NULL) delete m_Data.GetAt(i);
		m_Data.RemoveAll();
}

bool CRParamsArray::Set(CString name, CString value)
{
	int cnt = m_Data.GetSize();
	for(int i=0; i<cnt;i++)
	{
		if(m_Data.GetAt(i)->m_name.CompareNoCase(name)==0)
		{
			m_Data.GetAt(i)->m_value = value;
			return true;
		}
	}
	return false;
}

int CRParamsArray::Append(CRParamsArray &RParamArr)
{
	bool bAdd;
	int i, j;
	for(i=0; i<RParamArr.GetSize(); i++)
	{
		bAdd = true;
		for(j=0; j<m_Data.GetSize(); j++)
		{
			if(RParamArr[i]->m_name.CompareNoCase(m_Data[j]->m_name)==0)
			{
				bAdd = false;
				break;
			}
		}
		if(bAdd)
			m_Data.Add(new CaplRParam(*RParamArr[i]));
	}

	return m_Data.GetSize();
}

bool CDataSource::TestHeader(CHeader *pHeader)
{
	if(m_header==NULL) return false;
	if(m_ReportMgr==NULL && pHeader==NULL) return false;

	if(pHeader)
	{
		int i;
		for(i=0; i<pHeader->GetSize(); i++)
		{
			if(m_header->Find(pHeader->GetColumn(i)->m_name)<0)
			{
				TRACE_TO_FILE(pHeader->GetColumn(i)->m_name);
				return false;
			}
		}

		return true;
	}
	else if(m_ReportMgr)
	{
		int i;
		for(i=0; i<m_ReportMgr->m_header->GetSize(); i++)
		{
			if(m_header->Find(m_ReportMgr->m_header->GetColumn(i)->m_name)<0)
			{
				TRACE_TO_FILE(m_ReportMgr->m_header->GetColumn(i)->m_name);
				return false;
			}
		}

		return true;
	}

	return false;
}

bool CDataSource::GetParametrFromDataSource(LPCTSTR lpszColumnName, CaplRParam *param, bool bArray)
{
	if(lpszColumnName==NULL) return false;
	if(param == NULL) return false;
	if(m_Data.GetSize()==0) return false;

	int i;
	int ind = m_header->Find(lpszColumnName);
	if(ind<0) return false;

	CString sVal;
	CStringArrayAplEx sValues;

	for(i=0; i<m_Data.GetSize(); i++)
	{
		sVal = GetAsString(GetAt(i, ind));
		if(sValues.Find(sVal)<0)
			sValues.Add(sVal);
	}

	sVal = _T("");
	for(i=0; i<sValues.GetSize(); i++)
	{
		if(sVal.IsEmpty())
			sVal = sValues[i];
		else
			sVal+=_T(", ")+sValues[i];
	}

	param->m_value = sVal;

	return true;
}

bool CDataSource::Load(CStringArray* arr)
{
	CStringArray s_arr;
	s_arr.Append(*arr);
	RemoveAll();

	if (arr->GetSize()<1)
		return false;
	CString str = s_arr.GetAt(0);
	s_arr.RemoveAt(0);

	int cols=0;
	bool bUseQuote = false;
	bool bAplDSF = false;
	bool bData = false;
	bool bParams = false;
	str.TrimLeft();
	str.TrimRight();
	if(str.CompareNoCase(_T("Data Source File"))==0)
	{
		do 
		{
			if (s_arr.GetSize()<=0)
				return false;
			str = s_arr.GetAt(0);
			s_arr.RemoveAt(0);
			str.TrimLeft();
			str.TrimRight();
			bData = str.CompareNoCase(_T("[Data]"))==0;
			bParams = str.CompareNoCase(_T("[Params]"))==0;
		} while(bData==false && bParams==false);

		bAplDSF = true;
	}

	if(bParams)
	{
		LoadParams(&s_arr);
		LoadData(&s_arr);
	}
	else if(bData)
	{
		LoadData(&s_arr);
		LoadParams(&s_arr);
	}

	return true;
}

bool CDataSource::LoadParams(CStringArray* arr)
{
	CString str, name, value;
	int ind;

	do
	{
		if (arr->GetSize()<=0)
			break;
		str = arr->GetAt(0);
		arr->RemoveAt(0);
		str.TrimLeft();
		str.TrimRight();
		if(str.IsEmpty())
			continue;
		if(str.CompareNoCase(_T("[Data]"))==0)
			break;
		if(str[0]==_T(';')) continue;
		ind = str.Find(_T("="));
		if(ind<0)
			continue;
		name = str.Left(ind);
		value = str.Right(str.GetLength()-ind-1);
		CDataSource::ReplaceBreaks(value);
		m_RParams.Add(name, value);
	} while(str.CompareNoCase(_T("[Data]"))!=0);

	return true;
}

bool CDataSource::LoadData(CStringArray* arr)
{
	CString str;
	bool bUseQuote = false;
	int cols=0;

	while(1)
	{
		if (arr->GetSize()<=0)
			return false;
		str = arr->GetAt(0);
		arr->RemoveAt(0);
		if(!str.IsEmpty()) break;
	}

	if(str[0]==_T('\"'))
		bUseQuote = true;
	CHeader* header = new CHeader(NULL);
	CString name;
	while(GetNextWord(str, name, bUseQuote))
	{
		header->AddColumn(name, aplSTRING);
		cols++;
	}
	m_cols = cols;
	CHeader* pHeader = SetHeader(header);
	if(pHeader)
		delete pHeader;
	CString tmp_val;
	while(arr->GetSize()>0)
	{
		str = arr->GetAt(0);
		arr->RemoveAt(0);
		str.TrimLeft();
		str.TrimRight();
		if(str.CompareNoCase(_T("[params]"))==0)
			break;
		CTableRow* row = new CTableRow;
		CString value;
		for(int i=0; i<cols;i++)
		{
			GetNextWord(str, value, bUseQuote);
			ReplaceBreaks(value);
			row->AddColumn(value);
		}
		AddRow(row);
	}

	return true;
}