/*
 *  
 *  $Id: openwithdialog.h $
 *  Ginkgo CADx Project
 *
 *  Copyright 2008-14 MetaEmotion S.L. All rights reserved.
 *  http://ginkgo-cadx.com
 *
 *  This file is licensed under LGPL v3 license.
 *  See License.txt for details
 *
 *
 */
#include <map>
#include "historytablemodel.h"
#include <main/controllers/controladorlog.h>
#include <set>
#include <wx/imaglist.h>
#include <resources/ginkgoresourcesmanager.h>

#define SIZE_ICONOS 16

#define ICONO_LOCAL_DATABASE 0
#define ICONO_LOCAL_LINKED 1
#define ICONO_WADO_LINKED 2
#define ICONO_LOCAL_DATABASE_RECENT 3
#define ICONO_LOCAL_LINKED_RECENT 4
#define ICONO_WADO_LINKED_RECENT 5

GNC::GUI::HistoryNode::HistoryNode(const GNC::GCS::IHistoryController::StudyModel& study): pParent(NULL)
{
	pStudy = GNC::GCS::Ptr<GNC::GCS::IHistoryController::StudyModel>(new GNC::GCS::IHistoryController::StudyModel(study));
}

GNC::GUI::HistoryNode::HistoryNode(const GNC::GCS::IHistoryController::SeriesModel& series, HistoryNode* parent):pParent(parent)
{
	pSeries = GNC::GCS::Ptr<GNC::GCS::IHistoryController::SeriesModel>(new GNC::GCS::IHistoryController::SeriesModel(series));
}

GNC::GUI::HistoryNode::HistoryNode(): pParent(NULL)
{
}

GNC::GUI::HistoryNode::~HistoryNode()
{
	for (TMapIndex::iterator it = seriesMap.begin(); it !=  seriesMap.end(); ++it) {
		delete (*it).second;
	}
}

bool GNC::GUI::HistoryNode::IsStudyModel() const
{
	return pStudy.IsValid();
}

bool GNC::GUI::HistoryNode::IsSeriesModel() const
{ 
	return pSeries.IsValid();
}

GNC::GUI::HistoryNode* GNC::GUI::HistoryNode::GetParent()
{
	return pParent;
}

//if creation datetime is < now - 2 h => si se ha recibido antes de dos horas
bool GNC::GUI::HistoryNode::IsRecent()
{
	wxDateTime creationDate;
	if (IsSeriesModel()) {
		creationDate.ParseFormat(wxString::FromUTF8( GetSeriesModel()->created_time.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
	} else {
		creationDate.ParseFormat(wxString::FromUTF8( GetStudyModel()->created_time.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
	}
	wxTimeSpan diff = wxDateTime::Now().Subtract(creationDate);
	return diff.GetHours() < 2;
}

void GNC::GUI::HistoryNode::UpdateStudyModel(const GNC::GCS::IHistoryController::StudyModel& studyModel, wxDataViewItemArray& itemsAdded, wxDataViewItemArray& itemsChanged)
{
	if (pStudy.IsValid()) {
		*pStudy = studyModel;
		//if it's expanded... retry...
		if (!seriesMap.empty()) {
			//insert or update series....
			GNC::GCS::ILocker lock(this);
			GNC::GCS::IHistoryController::SeriesModelList seriesList;
			GNC::GCS::IHistoryController::Instance()->GetSeriesFromStudy(GetStudyModel()->pk, seriesList);
			for(GNC::GCS::IHistoryController::SeriesModelList::const_iterator it = seriesList.begin(); it != seriesList.end(); ++it) {
				if (seriesMap.find((*it).pk) != seriesMap.end()) {
					seriesMap[(*it).pk]->UpdateSeriesModel((*it));
					itemsChanged.push_back(wxDataViewItem(seriesMap[(*it).pk]));
				} else {
					HistoryNode* seriesNode = new HistoryNode((*it), this);
					seriesMap[(*it).pk] = seriesNode;
					itemsAdded.push_back(wxDataViewItem(seriesMap[(*it).pk]));
				}
			}
		}
	}
}
			
void GNC::GUI::HistoryNode::UpdateSeriesModel(const GNC::GCS::IHistoryController::SeriesModel& seriesModel)
{
	if (pSeries.IsValid()) {
		*pSeries = seriesModel;
	}
}

const GNC::GCS::Ptr<GNC::GCS::IHistoryController::StudyModel>& GNC::GUI::HistoryNode::GetStudyModel()
{
	return pStudy;
}

const GNC::GCS::Ptr<GNC::GCS::IHistoryController::SeriesModel>& GNC::GUI::HistoryNode::GetSeriesModel()
{
	return pSeries;
}

unsigned int GNC::GUI::HistoryNode::GetChildren(wxDataViewItemArray &array ) 
{
	if (IsSeriesModel()) {
		return 0;
	} else if (IsStudyModel()) {
		ReloadSeries();
		GNC::GCS::ILocker lock(this);
		for (TMapIndex::const_iterator it = seriesMap.begin(); it != seriesMap.end(); ++it) {
			array.Add( wxDataViewItem( (void*) (*it).second) );
		}
		return seriesMap.size();
	} else {
		return 0;
	}
}

void GNC::GUI::HistoryNode::GetSeriesModel(SeriesModelList& seriesList)
{
	if (IsSeriesModel()) {
		seriesList.push_back(GetSeriesModel());
	} else if (IsStudyModel()) {
		ReloadSeries();
		GNC::GCS::ILocker lock(this);
		for (TMapIndex::const_iterator it = seriesMap.begin(); it != seriesMap.end(); ++it) {
			seriesList.push_back((*it).second->GetSeriesModel());
		}
	}

}

void GNC::GUI::HistoryNode::ReloadSeries()
{
	if (IsSeriesModel()) {
		return;
	} else {
		if (seriesMap.empty()) {
			GNC::GCS::ILocker lock(this);
			GNC::GCS::IHistoryController::SeriesModelList seriesList;
			GNC::GCS::IHistoryController::Instance()->GetSeriesFromStudy(GetStudyModel()->pk, seriesList);
			for(GNC::GCS::IHistoryController::SeriesModelList::const_iterator it = seriesList.begin(); it != seriesList.end(); ++it) {
				HistoryNode* seriesNode = new HistoryNode((*it), this);
				seriesMap[(*it).pk] = seriesNode;
			}
		}
	}
}

void GNC::GUI::HistoryNode::DeleteSeries(long seriesPk)
{
	GNC::GCS::ILocker lock(this);
	if (seriesMap.find(seriesPk) != seriesMap.end()) {
		delete seriesMap.find(seriesPk)->second;
		seriesMap.erase(seriesMap.find(seriesPk));
	}

}

/////////////////////////////
GNC::GUI::HistoryTableModel::HistoryTableModel()
{
	m_pImageList = new wxImageList(SIZE_ICONOS,SIZE_ICONOS,true);
	wxBitmap bmp;
	bmp = GinkgoResourcesManager::History::GetIcoLocalFile();
	if (bmp.IsOk()) {
		m_pImageList->Add(bmp);
	}
	bmp = GinkgoResourcesManager::History::GetIcoLocalLinked();
	if (bmp.IsOk()) {
		m_pImageList->Add(bmp);
	}
	bmp = GinkgoResourcesManager::History::GetIcoWadoLinked();
	if (bmp.IsOk()) {
		m_pImageList->Add(bmp);
	}	
	bmp = GinkgoResourcesManager::History::GetIcoLocalFileRecent();
	if (bmp.IsOk()) {
		m_pImageList->Add(bmp);
	}
	bmp = GinkgoResourcesManager::History::GetIcoLocalLinkedRecent();
	if (bmp.IsOk()) {
		m_pImageList->Add(bmp);
	}
	bmp = GinkgoResourcesManager::History::GetIcoWadoLinkedRecent();
	if (bmp.IsOk()) {
		m_pImageList->Add(bmp);
	}
}


GNC::GUI::HistoryTableModel::~HistoryTableModel()
{
	ClearStudyMap();
	delete m_pImageList;
}

void GNC::GUI::HistoryTableModel::ClearStudyMap()
{
	for (TMapIndex::iterator it = studyMap.begin(); it !=  studyMap.end(); ++it) {
		delete (*it).second;
	}
	studyMap.clear();
}

unsigned int GNC::GUI::HistoryTableModel::GetColumnCount() const
{
	return COL_HISTORY_MAX;
}

void GNC::GUI::HistoryTableModel::ReloadTree(const GNC::GCS::IHistoryController::StudyModelList& studyList, bool force)
{
	if (force) {
		wxDataViewItemArray toDeleteList;
		for (TMapIndex::iterator it = studyMap.begin(); it !=  studyMap.end(); ++it) {
			toDeleteList.push_back(wxDataViewItem((*it).second));
		}
		DeleteItems(toDeleteList);
		Cleared();
		//delete all and reload again...
		wxDataViewItemArray addList;
		ClearStudyMap();
		for (GNC::GCS::IHistoryController::StudyModelList::const_iterator it = studyList.begin(); it != studyList.end(); ++it) {
			HistoryNode* pNode = new HistoryNode((*it));
			studyMap[(*it).pk] = pNode;
			addList.push_back(wxDataViewItem((void*)pNode));
		}	
		ItemsAdded(wxDataViewItem(0), addList);
	} else {
		//check for changes...
		//remove nodes that doesn't come in this new list
		std::set<long> setOfpk;
		for(GNC::GCS::IHistoryController::StudyModelList::const_iterator it = studyList.begin(); it!= studyList.end(); ++it) {
			setOfpk.insert((*it).pk);
		}
		wxDataViewItemArray toDeleteList;
		for (TMapIndex::iterator itIndex = studyMap.begin(); itIndex != studyMap.end(); ++itIndex) {
			if (setOfpk.find((*itIndex).first) == setOfpk.end()) {
				toDeleteList.push_back(wxDataViewItem((void*)(*itIndex).second));
			}
		}
		
		ItemsDeleted(wxDataViewItem(0), toDeleteList);

		//delete nodes...
		for (wxDataViewItemArray::iterator it = toDeleteList.begin(); it != toDeleteList.end(); ++it) {
			HistoryNode* pNode = (HistoryNode*)(*it).GetID();
			studyMap.erase(studyMap.find(pNode->GetStudyModel()->pk));
			delete pNode;
		}

		//insert or update studies....
		wxDataViewItemArray itemsAdded;
		wxDataViewItemArray itemsChanged;
		for(GNC::GCS::IHistoryController::StudyModelList::const_iterator it = studyList.begin(); it!= studyList.end(); ++it) {
			if (studyMap.find((*it).pk) != studyMap.end()) {
				//update
				wxDataViewItemArray itemsAddedToStudy;
				studyMap[(*it).pk]->UpdateStudyModel((*it), itemsAddedToStudy, itemsChanged);
				if (!itemsAddedToStudy.empty()) {
					ItemsAdded(wxDataViewItem(studyMap[(*it).pk]), itemsAddedToStudy);
				}
			} else {
				//create
				HistoryNode* pNode = new HistoryNode((*it));
				studyMap[(*it).pk] = pNode;
				itemsAdded.push_back(wxDataViewItem(studyMap[(*it).pk]));
			}
		}
		ItemsChanged(itemsChanged);
		ItemsAdded(wxDataViewItem(NULL), itemsAdded);
	}	
}

void GNC::GUI::HistoryTableModel::DeleteItems(wxDataViewItemArray& selected)
{
	//first of all delete series Nodes...
	wxDataViewItemArray toDeleteList;
	for (wxDataViewItemArray::iterator it = selected.begin(); it != selected.end(); ++it) {
		if ((*it).IsOk()) {
			HistoryNode* pNode = (HistoryNode*) (*it).GetID();
			if (pNode->IsSeriesModel()) {
				ItemDeleted(wxDataViewItem(pNode->GetParent()), (*it));
				pNode->GetParent()->DeleteSeries(pNode->GetSeriesModel()->pk);
			} else {
				toDeleteList.push_back((*it));
			}
		}
	}
	//then delete studies Nodes...		
	ItemsDeleted(wxDataViewItem(0), toDeleteList);

	//delete nodes...
	for (wxDataViewItemArray::iterator it = toDeleteList.begin(); it != toDeleteList.end(); ++it) {
		HistoryNode* pNode = (HistoryNode*)(*it).GetID();
		studyMap.erase(studyMap.find(pNode->GetStudyModel()->pk));
		delete pNode;
	}
}

wxString GNC::GUI::HistoryTableModel::GetColumnType( unsigned int col ) const
{
	switch (col) {
	case COL_ICON:
		return wxT("wxIcon");
	case COL_AGE:
		return wxT("long");
	default:
		return wxT("string");
	}
}

void GNC::GUI::HistoryTableModel::GetValue( wxVariant &variant, const wxDataViewItem &item, unsigned int col ) const
{
	if (!item.IsOk()) {
		return;
	}
	//if it's a study...
	HistoryNode* pNode = (HistoryNode*)(item.GetID());
	if (pNode->IsStudyModel()) {
		const GNC::GCS::Ptr<GNC::GCS::IHistoryController::StudyModel> pModel = pNode->GetStudyModel();
		switch (col) {
			case COL_ICON:
			{
				int icono = 0;
				if (pNode->IsRecent()) {
					switch(pModel->location)
					{
					case GNC::GCS::IHistoryController::TL_LocalDatabase:
						icono = ICONO_LOCAL_DATABASE_RECENT;
						break;
					case GNC::GCS::IHistoryController::TL_LocalLinked:
						icono = ICONO_LOCAL_LINKED_RECENT;
						break;
					case GNC::GCS::IHistoryController::TL_WadoLinked:
						icono = ICONO_WADO_LINKED_RECENT;
						break;
					}
				} else {
					switch(pModel->location)
					{
					case GNC::GCS::IHistoryController::TL_LocalDatabase:
						icono = ICONO_LOCAL_DATABASE;
						break;
					case GNC::GCS::IHistoryController::TL_LocalLinked:
						icono = ICONO_LOCAL_LINKED;
						break;
					case GNC::GCS::IHistoryController::TL_WadoLinked:
						icono = ICONO_WADO_LINKED;
						break;
					}
				}
				variant << m_pImageList->GetIcon(icono);
			}
			break;
			case COL_PATIENT_NAME:
				variant = wxString::FromUTF8(pModel->pat_name.c_str());
				break;
			case COL_PATIENT_ID:
				variant = wxString::FromUTF8(pModel->pat_id.c_str());
				break;
			case COL_AGE:
				variant = GetAge(pModel->pat_bithdate, pModel->study_datetime);
				break;
			case COL_MODALITY:
				{
					std::ostringstream ostr;
					for (std::list<std::string>::const_iterator it = pModel->mods_in_study.begin(); it != pModel->mods_in_study.end(); ++it)
					{
						if (it != pModel->mods_in_study.begin()) {
							ostr << "/";
						}
						ostr << (*it);
					}
					variant = wxString::FromUTF8(ostr.str().c_str());
				}
				break;
			case COL_DESCRIPTION:
				variant = wxString::FromUTF8(pModel->study_desc.c_str());
				break;
			case COL_DATE_TIME_ACQUIRED:
				{
					wxDateTime studyDateTime;
					studyDateTime.ParseFormat(wxString::FromUTF8( pModel->study_datetime.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
					if(studyDateTime.IsValid()){
						variant = wxString(studyDateTime.Format(_("%m/%d/%Y %H:%M:%S"), wxDateTime::TimeZone(wxDateTime::GMT1)));
					}
					else {
						variant = _("00/00/0000 00:00:00");
					}
				}
				break;
			case COL_DATE_TIME_ADDED:
				{
					wxDateTime studyDateTime;
					studyDateTime.ParseFormat(wxString::FromUTF8( pModel->created_time.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
					if(studyDateTime.IsValid()){
						variant = wxString(studyDateTime.Format(_("%m/%d/%Y %H:%M:%S"), wxDateTime::TimeZone(wxDateTime::GMT1)));
					}
					else {
						variant = _("00/00/0000 00:00:00");
					}
				}
				break;
			case COL_ACCNUMBER:
				variant = wxString::FromUTF8(pModel->study_acc_no.c_str());
				break;
			default:
				LOG_ERROR("HistoryTableModel", "wrong column id " << col);
				break;
		}
	} else if (pNode->IsSeriesModel()){
		const GNC::GCS::Ptr<GNC::GCS::IHistoryController::SeriesModel> pModel = pNode->GetSeriesModel();
		switch (col) {
			case COL_ICON:
			{
				int icono = 0;
				switch(pModel->location)
				{
				case GNC::GCS::IHistoryController::TL_LocalDatabase:
					icono = ICONO_LOCAL_DATABASE;
					break;
				case GNC::GCS::IHistoryController::TL_LocalLinked:
					icono = ICONO_LOCAL_LINKED;
					break;
				case GNC::GCS::IHistoryController::TL_WadoLinked:
					icono = ICONO_WADO_LINKED;
					break;
				}
				variant << m_pImageList->GetIcon(icono);
			}
			break;
			case COL_PATIENT_NAME:
				variant = wxString::FromUTF8(pModel->series_desc.c_str());
				break;
			case COL_PATIENT_ID:
				variant = wxEmptyString;
				break;
			case COL_AGE:
				//nothing to do... variant = wxString(pStudyModel->pat_name);
				break;
			case COL_MODALITY:
				variant = wxString::FromUTF8(pModel->series_modality.c_str());
				break;
			case COL_DESCRIPTION:
				variant = wxString::FromUTF8(pModel->series_desc.c_str());
				break;
			case COL_DATE_TIME_ACQUIRED:
				{
					wxDateTime seriesDateTime;
					seriesDateTime.ParseFormat(wxString::FromUTF8( pModel->series_datetime.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
					if(seriesDateTime.IsValid()){
						variant = wxString(seriesDateTime.Format(_("%m/%d/%Y %H:%M:%S"), wxDateTime::TimeZone(wxDateTime::GMT1)));
					}
					else {
						variant = _("00/00/0000 00:00:00");
					}
				}
				break;
			case COL_DATE_TIME_ADDED:
				{
					wxDateTime seriesDateTime;
					seriesDateTime.ParseFormat(wxString::FromUTF8( pModel->created_time.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
					if(seriesDateTime.IsValid()){
						variant = wxString(seriesDateTime.Format(_("%m/%d/%Y %H:%M:%S"), wxDateTime::TimeZone(wxDateTime::GMT1)));
					}
					else {
						variant = _("00/00/0000 00:00:00");
					}
				}
				break;
			case COL_ACCNUMBER:
				//nothing to do variant = wxString(pStudyModel->pat_name);
				break;
			default:
				LOG_ERROR("HistoryTableModel", "wrong column id " << col);
				break;
		}
	} else {
		LOG_ERROR("HistoryTableModel", "invalid node");
	}
}



bool GNC::GUI::HistoryTableModel::SetValue( const wxVariant &/*variant*/,const wxDataViewItem &/*item*/, unsigned int /*col*/ )
{
	//model cant be modified...
	return false;
}

bool GNC::GUI::HistoryTableModel::IsEnabled( const wxDataViewItem &/*item*/, unsigned int/* col*/ ) const
{
	return true;
}

wxDataViewItem GNC::GUI::HistoryTableModel::GetParent( const wxDataViewItem &item ) const
{
	if (!item.IsOk()) {
		return wxDataViewItem(0);
	}
	//if it's a study...
	HistoryNode* pNode = (HistoryNode*)(item.GetID());
	return wxDataViewItem( (void*) pNode->GetParent() );
}

bool GNC::GUI::HistoryTableModel::IsContainer( const wxDataViewItem &item ) const
{
	if (!item.IsOk()) {
		return true;
	}
	HistoryNode* pNode = (HistoryNode*)(item.GetID());
	return pNode->IsStudyModel();
}

bool GNC::GUI::HistoryTableModel::HasContainerColumns (const wxDataViewItem &/*item*/) const
{
	return true;
}

bool GNC::GUI::HistoryTableModel::HasValue (const wxDataViewItem &item, unsigned col) const
{
	if (!item.IsOk()) {
		return false;
	}
	//if it's a study...
	HistoryNode* pNode = (HistoryNode*)(item.GetID());
	return (pNode->IsStudyModel()) ||
		(pNode->IsSeriesModel() && (col!=COL_PATIENT_ID && col != COL_AGE && col!=COL_ACCNUMBER));
}


unsigned int GNC::GUI::HistoryTableModel::GetChildren( const wxDataViewItem &parent, wxDataViewItemArray &array ) const
{
	HistoryNode *node = (HistoryNode*) parent.GetID();
    if (!node)
    {
		unsigned int count = studyMap.size();
		for (TMapIndex::const_iterator it = studyMap.begin(); it !=  studyMap.end(); ++it) {
			array.Add( wxDataViewItem( (void*) (*it).second) );
		}
		return count;        
	} else if (node->IsStudyModel()) {
		//return series from study...
		node->GetChildren(array);
		return array.Count();
	} else {
		//series doesn't have children
		return 0;
	}
}

int GNC::GUI::HistoryTableModel::Compare( const wxDataViewItem &item1, const wxDataViewItem &item2,
                 unsigned int column, bool ascending ) const
{
	if ((column == COL_DATE_TIME_ACQUIRED || column == COL_DATE_TIME_ADDED) && item1.IsOk() && item2.IsOk())
    {
		HistoryNode *node1 = (HistoryNode*) item1.GetID();
		HistoryNode *node2 = (HistoryNode*) item2.GetID();
		wxDateTime date1, date2;
		if (column == COL_DATE_TIME_ACQUIRED) {
			if (node1->IsSeriesModel()) {
				date1.ParseFormat(wxString::FromUTF8( node1->GetSeriesModel()->series_datetime.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
			} else {
				date1.ParseFormat(wxString::FromUTF8( node1->GetStudyModel()->study_datetime.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
			}
			if (node2->IsSeriesModel()) {
				date2.ParseFormat(wxString::FromUTF8( node2->GetSeriesModel()->series_datetime.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
			} else {
				date2.ParseFormat(wxString::FromUTF8( node2->GetStudyModel()->study_datetime.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
			}
		} else {
			if (node1->IsSeriesModel()) {
				date1.ParseFormat(wxString::FromUTF8( node1->GetSeriesModel()->created_time.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
			} else {
				date1.ParseFormat(wxString::FromUTF8( node1->GetStudyModel()->created_time.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
			}
			if (node2->IsSeriesModel()) {
				date2.ParseFormat(wxString::FromUTF8( node2->GetSeriesModel()->created_time.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
			} else {
				date2.ParseFormat(wxString::FromUTF8( node2->GetStudyModel()->created_time.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
			}
		}
		
		int res = 0;
		if (date1.IsValid() && date2.IsValid()) {
			wxTimeSpan diff = date1.Subtract(date2);
			if (diff.IsPositive()) {
				res = 1;
			} else {
				res = -1;
			}
		} else if (date1.IsValid()) {
			res = 1;
		} else if (date2.IsValid()) {
			res =-1;
		} else {
			res = 0;
		}
		if (!ascending) {
			if (res == 1) {
				res = -1;
			} else if (res == -1) {
				res = 1;
			}
		}
		return res;
    }

    return wxDataViewModel::Compare( item1, item2, column, ascending );
}


long GNC::GUI::HistoryTableModel::GetAge(const std::string& patBirthDate, const std::string& studyDateTime) const
{
	wxDateTime birthDate, studyDate;
	birthDate.ParseFormat(wxString::FromUTF8( patBirthDate.c_str() ).GetData(), wxT("%Y-%m-%d"), wxDefaultDateTime);
	studyDate.ParseFormat(wxString::FromUTF8( studyDateTime.c_str() ).GetData(), wxT("%Y-%m-%dT%H:%M:%S"), wxDefaultDateTime);
	if (!birthDate.IsValid() || !studyDate.IsValid()) {
		return -1;
	}
	if (studyDate.GetYear() == birthDate.GetYear()) {
		return 0;
	} else {
		if (studyDate.GetMonth() > birthDate.GetMonth()) {
			return studyDate.GetYear() - birthDate.GetYear();
		} else if (studyDate.GetMonth() < birthDate.GetMonth()) {
			return studyDate.GetYear() - birthDate.GetYear() -1;
		} else {
			if (studyDate.GetDay() >= birthDate.GetDay()) {
				return studyDate.GetYear() - birthDate.GetYear();
			} else {
				return studyDate.GetYear() - birthDate.GetYear() -1;
			}
		}
	}
}
