/*
 *
 *  $Id: entorno.cpp $
 *  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 <string>
#include <sstream>
#include <cstdio>
#include <wx/filename.h>
#include <wx/dir.h>
#include <wx/stdpaths.h>
#include <wx/event.h>

#include <api/globals.h>
#include <api/controllers/icontroladorlog.h>
#include <api/controllers/icontroladorpermisos.h>
#include <api/observers/iextensionsobserver.h>
#include <api/observers/iviewsobserver.h>
#include <api/internationalization/internationalization.h>

#include <main/license.h>

#include <api/helpers/aetwildcards.h>
#include <api/ubication.h>

#include <main/controllers/pacscontroller.h>
#include <main/controllers/controladorvistas.h>
#include <main/controllers/commandcontroller.h>
#include <main/controllers/controladoreventos.h>
#include <main/controllers/controladorextensiones.h>
#include <main/controllers/controladorcarga.h>
#include <main/controllers/controladoracciones.h>
#include <main/controllers/controladorinternacionalizacion.h>
#include <main/controllers/configurationcontroller.h>
#include <main/controllers/integrationcontroller.h>
#include <main/managers/widgetsmanager.h>


#include <dcmtk/dcmjpeg/djdecode.h>
#include <dcmtk/dcmjpeg/djencode.h>
#include <dcmtk/dcmdata/dcrledrg.h>
#include <dcmtk/dcmdata/dcrleerg.h>

#include "entorno.h"

// Definicion de variables static
#define GINKGO_LOCALAET    "GINKGO_%IP4"
#if defined GINKGO_PRO
#include <ginkgopro/ginkgo_pro_defines.h>
#else
#define EULA "This is a open source Ginkgo CADx Framework build. MetaEmotion S.L does not provide support so eximes from all responsabilities derived from its usage.\n\nWARRANTY DISCLAIMER\n\n" \
	"The software, procedures, information and data made available by MetaEmotion S.L. under current conditions are provided \"AS IS\", without warranties of any kind except for contracted services, in which cases the specific contract conditions will be applied.\n" \
	"MetaEmotion S.L. expressly disclaims any representations and warranties, including without limitation, the implied warranties of merchantability and fitness for a particular purpose.\n\n" \
	"MetaEmotion S.L. shall have no liability for:\n" \
    " * Any loss or injury caused, in whole or in part, by its actions, omissions, or negligence, or for contingencies beyond its control, in procuring, compiling, or delivering the Information or\n" \
    " * Any errors, omissions, or inaccuracies in the Information regardless of how caused, or delays or interruptions in delivery of the Information; or\n" \
	" * Any decision made or action taken or not taken in reliance upon the Information furnished hereunder.\n\n" \
	"Under no circumstances shall the authors be liable for direct, indirect, special, incidental, consequential damages or misdiagnosis\n" \
    "resulting from the use, misuse, or inability to use the software, even if the authors have been advised of the possibility of such damages.\n" \
	"This software musn't be sold without prior approval of the authors.\n" \
	"\nThe user assumes the entire risk of using this program as well as conditions above."\
	"\nGinkgo CADx is licensed under the LGPL v3 license. You can find the legal terms of the license at http://www.gnu.org/copyleft/lgpl.html"
#endif

//helper
bool RemoveTempDir(const wxString& dirPath)
{
	//borra en cascada
	if(!wxRmdir(dirPath)){
		//vaciar
		wxDir dir;
		if (dir.Open(dirPath)) {
			wxString fileName;
			bool cont = dir.GetFirst(&fileName);
			while (cont) {
				fileName=dir.GetName()+ wxFileName::GetPathSeparator(wxPATH_NATIVE) +fileName;
				if(wxDir::Exists(fileName)){
					if (RemoveTempDir(fileName)) {
						cont = dir.GetFirst(&fileName);
					} else {
						cont = dir.GetNext(&fileName);
					}
				}else{
					#ifdef _WIN32
					_chmod(TOPATH(fileName).c_str(), _S_IWRITE);
					#else 
					chmod(TOPATH(fileName).c_str(), 0644);
					#endif
					if (wxRemoveFile(fileName)) {
						cont = dir.GetFirst(&fileName);
					} else {
						cont = dir.GetNext(&fileName);
					}
				}
			}
		}
	}
	if(wxDir::Exists(dirPath)){
		return wxRmdir(dirPath);
	}
	return true;
}

GNC::Entorno* GNC::Entorno::m_pInstancia = NULL;

GNC::Entorno::Entorno()
{
	DJEncoderRegistration::registerCodecs(
		ECC_lossyYCbCr,
		EUC_default, // UID generation (never create new UID's)
		OFFalse, // verbose
		0, 0, 0, true, ESS_444, true); // optimize huffman table
	DJDecoderRegistration::registerCodecs();

	DcmRLEEncoderRegistration::registerCodecs();
	DcmRLEDecoderRegistration::registerCodecs();

	m_pApp = NULL;
	m_pVentanaPrincipal = NULL;
	m_pVentanaRaiz = NULL;
	m_GinkgoLicenseMessage = "";
	
	m_isChildInstance = false;

	std::ostringstream os;
	os << GINKGO_VERSION << "." << GINKGO_REVISION << "." << GINKGO_BUILD;
	if (sizeof(void*) == 4) {
		os << " 32 bits";
	}
	else if (sizeof(void*) == 8) {
		os << " 64 bits";
	}
	std::sscanf(GINKGO_VERSION, "%u.%u.%*c", &m_mayorVersionNumber, &m_minorVersionNumber);

	m_GinkgoVersion = os.str();

	m_GinkgoCopyRight = GINKGO_COPYRIGHT;
	
	wxFileName executable_path = wxStandardPaths::Get().GetExecutablePath();
	//executable_path.SetFullName(wxEmptyString);
	
	wxString pluginsDir;
	wxString resourcesDir;
	wxString langDir;

	
	#if defined(_WINDOWS)
		pluginsDir = executable_path.GetPath() + wxFileName::GetPathSeparator() + wxT("Plugins");
		langDir = executable_path.GetPath() + wxFileName::GetPathSeparator() + wxT("lang");
		resourcesDir = executable_path.GetPath();
	#elif defined(LINUX)
		#if defined(CUSTOM_PACKAGE)
			pluginsDir = executable_path.GetPath() + wxFileName::GetPathSeparator() + wxT("Plugins");
			resourcesDir = executable_path.GetPath();
			langDir = executable_path.GetPath() + wxFileName::GetPathSeparator() + wxT("lang");
		#else
			pluginsDir = wxStandardPaths::Get().GetPluginsDir() + wxFileName::GetPathSeparator() + wxT("Plugins");
			resourcesDir = wxStandardPaths::Get().GetResourcesDir();
			langDir = wxStandardPaths::Get().GetResourcesDir() + wxFileName::GetPathSeparator() + wxT("lang");
		#endif
		
	#else
		pluginsDir = wxStandardPaths::Get().GetPluginsDir();
		langDir = wxStandardPaths::Get().GetResourcesDir() + wxFileName::GetPathSeparator() + wxT("lang");
		resourcesDir = wxStandardPaths::Get().GetResourcesDir();
	#endif
	
	m_GinkgoPluginsDir = TOPATH(pluginsDir);
	m_GinkgoLanguageDir = TOPATH(langDir);
	m_GinkgoResourcesDir = TOPATH(resourcesDir);
	
	//std::cout << "PLUGINS_DIR = " << m_GinkgoPluginsDir.c_str() << std::endl;
	//std::cout << "LANGUAGE_DIR = " << m_GinkgoLanguageDir.c_str() << std::endl;

	wxString wxTempDir = wxStandardPaths::Get().GetTempDir() + wxFileName::GetPathSeparator() + wxT("GinkgoCADx");
	m_GinkgoTempDir = TOPATH(wxTempDir);
	if (!wxDirExists(wxTempDir)) {
		#ifdef _WIN32
		wxMkdir(wxTempDir,511);
		#else
		wxMkDir(wxTempDir.ToUTF8(), 0770);
		#endif
	}
	
	m_GinkgoUserDir = TOPATH(wxStandardPaths::Get().GetUserDataDir());
	
	{
		wxString directorioUser = FROMPATH(m_GinkgoUserDir);
		//se crea el user dir
		if(!wxDirExists(directorioUser)) {
			#ifdef _WIN32
			wxMkdir(directorioUser,511);
			#else
			wxMkDir(directorioUser.ToUTF8(), 0770);
			#endif
		}
	}

	// Carga de ubicaciones
	GNC::GCS::ConfigurationController::TListGroups locations;
	GNC::GCS::ConfigurationController::Instance()->readGroupGeneral("/GinkgoCore/Locations", locations);
	for (GNC::GCS::ConfigurationController::TListGroups::iterator it = locations.begin(); it != locations.end(); ++it) {
		std::string title, path, descr;
		bool monitorize, cleanbefore, cleanafter;
		(*it).readStringValue("Title", title);
		(*it).readStringValue("Path", path);
		(*it).readStringValue("Description", descr);
		(*it).readBoolValue("Monitorize", monitorize, false);
		(*it).readBoolValue("CleanBefore", cleanbefore, false);
		(*it).readBoolValue("CleanAfter", cleanafter, false);

		m_Ubicaciones[title] = new GIL::Ubicacion(title, path, descr, monitorize, cleanbefore, cleanafter);
	}

	GIL::IntegrationController::Instance();
}

GNC::Entorno::~Entorno()
{
	GIL::IntegrationController::FreeInstance();
	DJEncoderRegistration::cleanup();
	DJDecoderRegistration::cleanup();

	DcmRLEEncoderRegistration::cleanup();
	DcmRLEDecoderRegistration::cleanup();

	if (!ExtensionsObservers.empty()) {
		LOG_WARN("Core", "Extensions observers list not empty");
	}
	
	if (!ViewsObservers.empty()) {
		LOG_WARN("Core", "Views observers list not empty");
	}

	//remove temp dir
	if (!m_isChildInstance) {
		RemoveTempDir(FROMPATH(m_GinkgoTempDir));	
	}

	m_pApp = NULL;
	m_pVentanaPrincipal = NULL;
	m_pVentanaRaiz = NULL;

	for (MapaUbicaciones::iterator it = m_Ubicaciones.begin(); it != m_Ubicaciones.end(); ++it) {
		delete (*it).second;
	}
	m_Ubicaciones.clear();
}

GNC::Entorno* GNC::Entorno::Instance()
{
	if (m_pInstancia == NULL) {
		m_pInstancia = new GNC::Entorno();
	}
	return m_pInstancia;
}

void GNC::Entorno::FreeInstance()
{
	if (m_pInstancia != NULL) {
		delete m_pInstancia;
	}
	m_pInstancia = NULL;
}

/** Gets the GinkgoCADx version full string **/
const std::string& GNC::Entorno::GetGinkgoVersionString()
{
	return m_GinkgoVersion;
}

/** Gets the GinkgoCADx version string **/
std::string GNC::Entorno::GetGinkgoVersion()
{
	return std::string(GINKGO_VERSION);
}

/** Gets the revision mark (incremental) **/
int GNC::Entorno::GetGinkgoRevision()
{
	return GINKGO_REVISION;
}

/** Gets the build mark (incremental) **/
std::string GNC::Entorno::GetGinkgoBuild()
{
	return std::string(GINKGO_BUILD);
}

/** Gets the mayor version number **/
int GNC::Entorno::GetGinkgoMayorVersionNumber()
{
	return m_mayorVersionNumber;
}

/** Gets the mayor version minor **/
int GNC::Entorno::GetGinkgoMinorVersionNumber()
{
	return m_minorVersionNumber;
}


/** Gets the application name */
std::string GNC::Entorno::GetApplicationName()
{
	return std::string(PRODUCT_NAME);
}

/** Gets the application subtitle */
std::string GNC::Entorno::GetApplicationSubTitle()
{
	return PRODUCT_SUBTITLE;
}

/** Gets the application credits */
std::string GNC::Entorno::GetApplicationCredits()
{
	return std::string(CREDITS);
}

/** Gets the application eula */
std::string GNC::Entorno::GetApplicationEula()
{
	return std::string(EULA);
}

const std::string& GNC::Entorno::GetGinkgoCopyRight()
{
	return m_GinkgoCopyRight;
}

const std::string& GNC::Entorno::GetLicenseMessage()
{
	return m_GinkgoLicenseMessage;
}

void GNC::Entorno::SetLicenseMessage(const std::string& license)
{
	m_GinkgoLicenseMessage = license;
}

int GNC::Entorno::ParseSVNBuild(const char* str)
{
	int num = -1;
	std::string beg;
	std::string end;
	std::stringstream ss(str);
	ss >> beg >> num >> end;
	return num;
}



const std::string& GNC::Entorno::GetGinkgoTempDir()
{
	return m_GinkgoTempDir;
}

std::string GNC::Entorno::CreateGinkgoTempDir()
{
	wxString wxNuevoDirTemp;
	do {
		wxNuevoDirTemp = FROMPATH(GetGinkgoTempDir()) + wxFileName::GetPathSeparator(wxPATH_NATIVE) + wxT("_gnktmp_") + wxString::Format(wxT("%d"), (int) rand());
	} while(wxDir::Exists(wxNuevoDirTemp));

	#ifdef _WIN32
			wxFileName::Mkdir(wxNuevoDirTemp);
	#else
			wxFileName::Mkdir(wxNuevoDirTemp.c_str(), 0777);
	#endif

	return std::string(TOPATH(wxNuevoDirTemp));
}

void GNC::Entorno::RemoveDirRecursive(const std::string& path)
{
	wxString pathWx = FROMPATH(path);
	if (wxDirExists(pathWx)) {
		RemoveTempDir(pathWx);
	}
}

std::string GNC::Entorno::CreateGinkgoTempFile()
{
	std::string directory = CreateGinkgoTempDir();
	std::ostringstream ostr;
	ostr << directory << (char)wxFileName::GetPathSeparator() << "tmp";
	return ostr.str();
}

const std::string& GNC::Entorno::GetGinkgoLanguageDir()
{
	return m_GinkgoLanguageDir;
}

const std::string& GNC::Entorno::GetGinkgoResourcesDir()
{
	return m_GinkgoResourcesDir;
}

std::string GNC::Entorno::CrearDirectorioTemporal()
{
	wxString dirTmp;
	do {
		std::ostringstream ostr;
		ostr << GetGinkgoTempDir().c_str() << (char)wxFileName::GetPathSeparator() << "_gnktmp_" << rand();
		dirTmp = FROMPATH(ostr.str());
	} while (wxDirExists(dirTmp));

	#if defined(_WINDOWS)
	wxMkDir(dirTmp);
	#else
	wxMkDir(dirTmp.ToUTF8(), 0770);
	#endif

	std::string resultado(TOPATH(dirTmp));
	return resultado;
}

const std::string& GNC::Entorno::GetPluginsPath()
{
	return m_GinkgoPluginsDir;
}

const std::string& GNC::Entorno::GetGinkgoUserDir()
{
	return m_GinkgoUserDir;
}

std::string GNC::Entorno::GetDicomLocalAET()
{
	std::string value;
	GNC::GCS::ConfigurationController::Instance()->readStringGeneral("/GinkgoCore/PACS/Local", "AET", value, GINKGO_LOCALAET);

	return ParseAET(value);
}

std::string GNC::Entorno::ParseAET(const std::string& AET)
{
	return GNC::GCS::AETWildcards::Parse(AET);
}

wxApp* GNC::Entorno::GetApp()
{
	return m_pApp;
}

void GNC::Entorno::YieldApp()
{
	//DEPRECATED
	//m_pApp->Yield();
}


void GNC::Entorno::SetApp(wxApp* app)
{
	m_pApp = app;
}

VentanaPrincipal* GNC::Entorno::GetVentanaPrincipal()
{
	return m_pVentanaPrincipal;
}

void GNC::Entorno::SetVentanaPrincipal(VentanaPrincipal* ventana)
{
	m_pVentanaPrincipal = ventana;
}

wxWindow* GNC::Entorno::GetVentanaRaiz()
{
	return m_pVentanaRaiz;
}

void GNC::Entorno::SetVentanaRaiz(wxWindow* ventana)
{
	m_pVentanaRaiz = ventana;
}

GNC::GCS::ICommandController* GNC::Entorno::GetCommandController()
{
	return GNC::GCS::ICommandController::Instance();
}

GIL::DICOM::IPACSController* GNC::Entorno::GetPACSController()
{
	return GIL::DICOM::PACSController::Instance();
}

GNC::GCS::IControladorVistas* GNC::Entorno::GetControladorVistas()
{
	return GNC::GCS::ControladorVistas::Instance();
}

GNC::GCS::IEventsController* GNC::Entorno::GetControladorEventos()
{
	return GNC::GCS::ControladorEventos::Instance();
}

GNC::GCS::IControladorCarga* GNC::Entorno::GetControladorCarga()
{
	return GNC::GCS::ControladorCarga::Instance();
}

GNC::GCS::IControladorPermisos* GNC::Entorno::GetControladorPermisos()
{
	return GNC::GCS::IControladorPermisos::Instance();
}

GNC::GCS::IControladorExtensiones* GNC::Entorno::GetControladorExtensiones()
{
	return GNC::ControladorExtensiones::Instance();
}

GNC::GCS::IControladorAcciones* GNC::Entorno::GetControladorAcciones()
{
	return GNC::GCS::ControladorAcciones::Instance();
}

GNC::GCS::IControladorInternacionalizacion* GNC::Entorno::GetControladorInternacionalizacion()
{
	return GNC::GCS::ControladorInternacionalizacion::Instance();
}

GNC::GCS::IConfigurationController* GNC::Entorno::GetConfigurationController()
{
	return GNC::GCS::ConfigurationController::Instance();
}

GNC::GCS::IWidgetsManager* GNC::Entorno::NewWidgetsManager(GNC::GCS::IVista* pVista)
{
	return new GNC::WidgetsManager(pVista);
}

void GNC::Entorno::FreeWidgetsManager(GNC::GCS::IWidgetsManager* pManager)
{
	if (pManager != NULL) {
		delete pManager;
	}
}


//------------------------------------------------------------------------------
//region "Extensions observers registry interface"

void GNC::Entorno::RegisterExtensionsObserver(GNC::GCS::IExtensionsObserver* observer)
{
	GNC::GCS::ILocker lock(ExtensionsObserversLock);

	for (ExtensionsObserversList::const_iterator it = ExtensionsObservers.begin(); it != ExtensionsObservers.end(); ++it) {
		if (*it == observer) {
			LOG_WARN("Core", "Extensions observer already registered");
			return;
		}
	}
	ExtensionsObservers.push_back(observer);
}

void GNC::Entorno::UnRegisterExtensionsObserver(GNC::GCS::IExtensionsObserver* observer)
{
	GNC::GCS::ILocker lock(ExtensionsObserversLock);
	ExtensionsObservers.remove(observer);
}

//endregion

//------------------------------------------------------------------------------
//region "Views observers registry interface"
void GNC::Entorno::RegisterViewsObserver(GNC::GCS::IObservadorVistas* /*observer*/)
{
	GNC::GCS::ILocker lock(ViewsObserversLock);
	//TODO
}

void GNC::Entorno::UnRegisterViewsObserver(GNC::GCS::IObservadorVistas* /*observer*/)
{
	GNC::GCS::ILocker lock(ViewsObserversLock);
	//TODO
}
//endregion
