/*
 *  
 *  $Id: gvistacompleja.cpp $
 *  Ginkgo CADx Project
 *
 *  Copyright 2008-12 MetaEmotion S.L. All rights reserved.
 *  http://ginkgo-cadx.com
 *
 *  This file is licensed under LGPL v3 license.
 *  See License.txt for details
 *
 *
 */
#include <list>
#include <wx/wx.h>
#include <wx/msgdlg.h>
#include <wx/menu.h>
#include <wx/dnd.h>

#include "gvistacompleja.h"
#include "gvistasimple.h"
#include "simpleviewexportdelegate.h"
#include "../vistas/vista2d.h"
#include <api/internationalization/internationalization.h>
#include <visualizator/estudios/overlay.h>
#include <resources/ginkgoresourcesmanager.h>
#include <resources/visualizatorresourcesmanager.h>

#include <api/autoptr.h>

#include "../eventos/visualizatorevents.h"
#include <api/controllers/icontroladorlog.h>
#include <api/controllers/icontroladorvistas.h>
#include <eventos/modificacionimagen.h>
#include <eventos/modificacionfichero.h>
#include <eventos/modificacionwidget.h>
#include <eventos/mensajes.h>
#include <eventos/render.h>
#include <api/iwidgetsmanager.h>
#include "../estudios/visualizatorstudy.h"

#ifdef __DEPRECATED
#undef __DEPRECATED
#endif

#include <vtkVISUManagement/vtkLookupTableManager.h>
#include <vtk/widgets/widgetsactor.h>
#include <vtkCamera.h>
#include <vtkPointData.h>
#include <vtkImageBlend.h>
#include <vtkImageMathematics.h>
#include <api/westilo.h>

#include <api/imodelointegracion.h>
#include <api/controllers/imodulecontroller.h>
#include <api/controllers/icontroladorpermisos.h>
#include <api/controllers/icommandscontroller.h>
#include <main/controllers/hangingprotocolcontroller.h>

#include <main/entorno.h>

#include <commands/comandodestruirvista.h>

#include <cairo/cairo.h>
#include <widgets/openglhelper.h>
//#include <widgets/wprogreso.h>
#include <widgets/wanotacionesquina.h>


#include <export/tools/iherramientascore.h>
#include <visualizator/export/tools/ivisualizatortools.h>
#include <api/toolsystem/itoolcontroller.h>
#include <api/toolsystem/itool.h>
#include <visualizator/wxvtk/previewpanelvisualizator.h>

#define ID_BOTON_PLAY_STOP 0
#define ID_BOTON_VER 1
#define ID_BOTON_LAYOUT 2
#define ID_BOTON_SINCRONIZAR 3
#define ID_BOTON_DESENCAJAR 4

//esta clase sirve para las barras de herramientas de abajo
class BarraHerramientasBotones:public wxAuiToolBar{
public:
	BarraHerramientasBotones(wxWindow* pParent, const wxColor& color = wxColor(0x40, 0x40, 0x40)): wxAuiToolBar(pParent,wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_TB_DEFAULT_STYLE)
	{
		SetToolBitmapSize(wxSize(16,16));
		AUI_NAMESPACE wxAuiDefaultToolBarArt* pToolArt= new AUI_NAMESPACE wxAuiDefaultToolBarArt();
#if defined(USE_PATCHED_LIBS) && !defined(__WXOSX__)
		pToolArt->SetBaseColor(color);
#endif
		SetArtProvider(pToolArt);
	}

	~BarraHerramientasBotones()
	{
	}
};

//esta clase sirve para no tener que definir identificadores en las opciones de periodo en el cine
class ItemFrameRate: protected wxEvtHandler, public wxMenuItem {
public:
	ItemFrameRate(wxMenu *parentMenu,const wxString& text,GVistaCompleja* pVista,int periodo):wxMenuItem(parentMenu,wxID_ANY,text,wxEmptyString,wxITEM_CHECK)
	{
		m_pVista = pVista;
		m_periodo = periodo;
		int id = wxMenuItem::GetId();
		#ifdef __WXMSW__
		parentMenu->GetParent()->Connect(id,wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( ItemFrameRate::OnMenuClick),NULL,this);
		#else
		parentMenu->Connect(id,wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( ItemFrameRate::OnMenuClick),NULL,this);
		#endif
	}
	~ItemFrameRate(){
		m_pVista =NULL;
		int id = wxMenuItem::GetId();
		#ifdef __WXMSW__
		GetMenu()->GetParent()->Disconnect(id,wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( ItemFrameRate::OnMenuClick ),NULL,this);
		#else
		GetMenu()->Disconnect(id,wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( ItemFrameRate::OnMenuClick ),NULL,this);
		#endif
	}

	void OnMenuClick(wxCommandEvent &event) {
		m_pVista->SetPeriodo(m_periodo);
		event.Skip(false);
	}


	GVistaCompleja* m_pVista;
	int m_periodo;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class TimerPlay:public wxTimer
{
public:
	TimerPlay(GVistaCompleja *pVista){
		m_pVista=pVista;
	}

	~TimerPlay() {}

	virtual void Notify(){
		m_pVista->OnTimer();
	}

	GVistaCompleja *m_pVista;
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class GVistaComplejaOverlaysDelegate: public GNKVisualizator::IOverlaysContract
{
public:
	GVistaComplejaOverlaysDelegate(GVistaCompleja* gView):GView(gView)
	{
	}
	~GVistaComplejaOverlaysDelegate()
	{
	}
	
protected:
	GVistaCompleja* GView;
};


class GVistaComplejaWindowLayoutDelegate: public GNC::GCS::IWindowLayoutContract
{
public:
	GVistaComplejaWindowLayoutDelegate(GVistaCompleja* gView):GView(gView)
	{
	}
	~GVistaComplejaWindowLayoutDelegate()
	{
	}

	//contrato layout
	virtual void SetWindowLayout(int filas, int columnas)
	{
		if(filas <1 || columnas < 1)
		{
			//layout invalido
			return;
		}
		bool incrementado = false;

		const int maxSlices = m_MaxSlice - m_MinSlice +1;
		if(filas*columnas > maxSlices) {
			//layout invalido aunque podría ser valido...
			filas = (int)std::ceil((double)maxSlices/columnas);
		}
		GView->Freeze();

		int filasActuales = GView->m_pSizerSeries->GetRows();
		int columnasActuales = GView->m_pSizerSeries->GetCols();
		int tamAnterior = GView->m_VistasSimples.size();
		int nuevoTam = filas*columnas;

		GView->m_pSizerSeries->SetCols(columnas);
		GView->m_pSizerSeries->SetRows(filas);


		//FUNDAMENTAL PARA QUE los viewers se destruyan correctamente y sin leaks ya que Viewer es smart pointer
		GView->IVista->VisualizatorStudy->Viewer = NULL;
		if(tamAnterior > nuevoTam) {
			//hay que borrar
			for(int i = 0; i< tamAnterior-nuevoTam; i++) {
				GVistaCompleja::TListaGVistasSimples::iterator pVista = GView->m_VistasSimples.begin();
				GView->m_pManager->LiberarRecursos((*pVista)->ViewInteractor2D);
				GView->m_pSizerSeries->Detach((*pVista));
				(*pVista)->Destroy();

				GView->m_VistasSimples.erase(pVista);
			}
		} else {
			int interpolationMode = VTK_NEAREST_INTERPOLATION;
			for (unsigned int i = 0; i < GView->m_VistasSimples.size(); ++i) {
				if (i == 0) {				
					interpolationMode = GView->m_VistasSimples[i]->ViewImage2D->GetInterpolationMode();
				}
				GView->m_VistasSimples[i]->ViewImage2D->ResetZoom(false, 0);
			}

			//hay que crear
			for(unsigned int i = 0; i< (unsigned int) (nuevoTam - tamAnterior) && GView->m_VistasSimples.size() < (unsigned int) maxSlices; i++) {
				incrementado = true;
				GView->AddVistaSimple();
			}
			for (unsigned int i = 1; i < GView->m_VistasSimples.size(); ++i) {
				GView->m_VistasSimples[i]->ViewImage2D->SetInterpolationMode(interpolationMode);
			}
			//por si la posicion actual es invalida
			GView->m_posicion = 0;
		}
		//FUNDAMENTAL PARA QUE FUNCIONE LA HERRAMIENTA DE MAPAS DE COLOR
		GView->IVista->VisualizatorStudy->Viewer = GView->m_VistasSimples[0]->ViewImage2D;
		GView->ActualizarMaxMinSlider();
		GView->Layout();
		//nos quedamos en la posicion
		GView->GoToSlice(GView->m_posicion,false,false,false);
		GView->Thaw();
		if ( (filas > 1 || columnas > 1) && incrementado) { // condición para evitar doble render. Ya se comprueba por otro lado en la creación (con 1 vista).
			bool noerror = true;
			for(GVistaCompleja::TListaGVistasSimples::iterator it = GView->m_VistasSimples.begin(); noerror && it !=GView-> m_VistasSimples.end(); ++it) {
				GVistaSimple* vs = *it;
				vs->ViewInteractor2D->Render();
				long idTextura = vs->ViewImage2D->GetImageTexture();
				if (idTextura == 0 || !glIsTexture(idTextura)) {
					noerror = false;
				}
			}
			if (!noerror) {
				SetWindowLayout(filasActuales, columnasActuales);
				wxMessageBox(_("It has exhausted the system's video memory and has not been able to assign the grid configuration. Close some studies to free memory."), _("Error configuring the grid"), wxOK, NULL);
				return;
			}
		}
	}
	
	virtual int GetRows()
	{
		return GView->m_pSizerSeries->GetRows();
	}

	virtual int GetColumns()
	{
		return GView->m_pSizerSeries->GetCols();
	}
	
protected:
	GVistaCompleja* GView;
};

class GVistaComplejaDelegate: public GNC::GCS::ISliderContract,  public GNC::GCS::IWindowLevelContract, public GNC::GCS::IRuleContract
{
public:
	GVistaComplejaDelegate(GVistaCompleja* gView):GView(gView)
	{
	}
	~GVistaComplejaDelegate()
	{
	}

	//contrato widgets
	vtkGinkgoImageViewer* GetViewerActivo()
	{
		// TODO Excepciones
		vtkGinkgoImageViewer* viewer = GView->m_VistasSimples[0]->ViewImage2D;
		for (GVistaCompleja::TListaGVistasSimples::iterator it = GView->m_VistasSimples.begin(); viewer == NULL && it != GView->m_VistasSimples.end(); ++it)
		{
			if (GView->m_pManager->GetRendererActivo() == (*it)->ViewInteractor2D)
			{
				viewer = (*it)->ViewImage2D;
			}
		}
		return viewer;
	}


	GNC::GCS::IWidgetsManager* GetManager()
	{ 
		return GView->m_pManager; 
	}

	void GetAllViewers(std::list<vtkGinkgoImageViewer*>& viewerList)
	{
		for (GVistaCompleja::TListaGVistasSimples::iterator it = GView->m_VistasSimples.begin(); it != GView->m_VistasSimples.end(); ++it) {
			viewerList.push_back((*it)->ViewImage2D);
		}
	}
	//

	void SetSlice(int slicenum)
	{
		return GView->GoToSlice(slicenum, false);
	}

	int GetSlice() const
	{
		return GView->GetCurrentSlice();
	}

	int GetNumberOfSlices() const
	{
		return GView->IVista->VisualizatorStudy->GetSliceNumber();
	}

	virtual ListaWindowLevels& GetWindowLevelList()
	{
		return m_listaWindowLevels;
	}
	virtual std::string GetLabelCurrent()
	{
		return m_labelCurrent;
	}
	virtual void SetLabelCurrent(const std::string& labelCurrent)
	{
		m_labelCurrent = labelCurrent;
	}

	virtual bool SupportsCallibration() {
		return true;
	}

protected:
	GVistaCompleja* GView;
	ListaWindowLevels m_listaWindowLevels;
	std::string m_labelCurrent;
};

//contracts dellegate

/////////////////////////////////////DRAG&DROP/////////////////////////////////////////
namespace GNKVisualizator {
	namespace GUI {
		class wxDropTargetPreviewPanelVisualizator: public wxDropTarget
		{
		public:
			wxDropTargetPreviewPanelVisualizator(GVistaCompleja* gView): GView(gView)
			{
				SetDataObject(new GNKVisualizator::GUI::wxDnDPreviewPanelVisualizator);
				SetDefaultAction(wxDragCopy);
			}

			~wxDropTargetPreviewPanelVisualizator()
			{
			}

			virtual wxDragResult OnDragOver(wxCoord x, wxCoord y,
												wxDragResult def)
			{ 
				wxPoint coords(x, y);
				coords = GView->ClientToScreen(coords);
				wxPoint clientPreview = GView->m_pPanelPreview->ScreenToClient(coords);
				wxPoint client = GView->ScreenToClient(coords);
				if (GView->m_pPanelPreview->HitTest(clientPreview) != wxHT_WINDOW_INSIDE && GView->HitTest(client) == wxHT_WINDOW_INSIDE) {
					return wxDragCopy;
				}

			  return wxDragNone; 
			}

			virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)
			{
				if ( !GetData() )
					  return wxDragNone;
				GNKVisualizator::GUI::wxDnDPreviewPanelVisualizator *dobj = (GNKVisualizator::GUI::wxDnDPreviewPanelVisualizator *)m_dataObject;
				
				wxPoint coords(x, y);
				coords = GView->ClientToScreen(coords);
				 
				wxPoint client = GView->ScreenToClient(coords);
				wxPoint clientPreview = GView->m_pPanelPreview->ScreenToClient(coords);
				if (GView->m_pPanelPreview->HitTest(clientPreview) != wxHT_WINDOW_INSIDE && GView->HitTest(client) == wxHT_WINDOW_INSIDE) {
					GView->LoadSeries(dobj->GetSeriesPk());
					return wxDragCopy;
				}
				return wxDragNone;
			}

		private:
			GVistaCompleja* GView;
		};
	}
}
//////////////////////////////////////////////END DRAG&DROP////////////////////////////////////////////


GVistaCompleja::GVistaCompleja(GNKVisualizator::Vista2D* pIVista) : VistaComplejaBase(pIVista->GetVisualizatorStudy()->ParentWindow ),
	IVista(pIVista),
	Delegate(new GVistaComplejaDelegate(this)),
	OverlaysDelegate(new GVistaComplejaOverlaysDelegate(this)),
	WindowLayoutDelegate(new GVistaComplejaWindowLayoutDelegate(this)),
	m_pManager (GNC::GCS::IEntorno::Instance()->NewWidgetsManager(IVista)),
	ExportDelegate(new GNKVisualizator::SimpleViewExportContractDelegate(this)),
	m_pPanelPreview(new GNKVisualizator::GUI::PreviewPanelVisualizator(pIVista, this))
{
	IVista->GetVisualizatorStudy()->Window = this;
	m_hasBeenRendered = false;
	m_ReproducirBucle = false;
	m_PeriodoMs = 500;
	m_posicion = m_pSliderSlice->GetValue();

	// Setup del manejador de widgets */
	

	m_playTimer = new TimerPlay(this);

	//toolbar
	GNC::GCS::IToolController* pToolBar = IVista->GetToolController();
	//widgets...
	pToolBar->AddTool(HCore_Pointer, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HCore_Zoom, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HCore_Slider, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HCore_Pan, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HCore_Angulo, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HCore_Rule, (GNC::GCS::IRuleContract*) Delegate);
	pToolBar->AddTool(HCore_Nota, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HCore_Marcado, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HCore_WindowLevel, (GNC::GCS::IWindowLevelContract*) Delegate);
	//view menu...
	pToolBar->AddTool(HCore_CornerAnnotations, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HCore_DetachWindow, (GNC::GCS::IViewContract*) IVista);
	pToolBar->AddTool(HCore_MetricGrid, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HCore_PixelsBluring, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HCore_Reset, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HCore_WindowLayout, WindowLayoutDelegate);
	pToolBar->AddTool(HCore_RotateAndFlip, (GNC::GCS::ISliderContract*) Delegate);
	//tools
	pToolBar->AddTool(HCore_ShowDicomTags, (GNC::GCS::IViewContract*) IVista);
	pToolBar->AddTool(HCore_ShowHistory, (GNC::GCS::IViewContract*) IVista);
	pToolBar->AddTool(HCore_ColorMap, (GNC::GCS::ISliderContract*) Delegate);
	pToolBar->AddTool(HGNKVisualizator_Overlays, OverlaysDelegate);
	pToolBar->AddTool(HCore_ImageExport, ExportDelegate);
	pToolBar->AddTool(HCore_ImagePrint, ExportDelegate);
	pToolBar->AddTool(HCore_Deshacer, (GNC::GCS::IViewContract*) IVista);

	m_pSizerTools->Add(pToolBar->CreateNewToolBar(this, GNC::GCS::ITool::TFamiliaVisualizacion), 0, wxEXPAND);
	m_pSizerTools->Add(pToolBar->CreateNewToolBar(this, GNC::GCS::ITool::TFamiliaMedicion), 0, wxEXPAND);
	m_pSizerTools->Add(pToolBar->CreateNewToolBar(this, GNC::GCS::ITool::TFamiliaAnotacion), 0, wxEXPAND);

	{
		GNC::GCS::IToolController::TIdList listids;
		listids.push_back(HCore_WindowLayout);
		listids.push_back(HCore_ShowHistory);
		wxWindow* pwxToolBar = pToolBar->CreateNewToolBar(this, listids, false);
		m_pSizerTools->Add(pwxToolBar, 1, wxEXPAND);
	}

	//preview series and drag & drop	
	m_pSizerView->Insert(0, m_pPanelPreview, 0, wxEXPAND);
	SetDropTarget(new GNKVisualizator::GUI::wxDropTargetPreviewPanelVisualizator(this));		

	//se añaden los botones de ver/cine
	m_pBarraIzquierda = new BarraHerramientasBotones(m_pPanelManipulacion);
	m_pBarraIzquierda->AddTool(ID_BOTON_VER,_("Display Options"),GNKVisualizator::ResourcesManager::ToolIcons::GetIcoMenuImagen(),_("Display Options"));
	m_pBarraIzquierda->Connect(ID_BOTON_VER,wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( GVistaCompleja::OnMenuVerClick),NULL,this);

	m_pBarraIzquierda->Realize();
	m_pSizerIzquierda->Add(m_pBarraIzquierda,0, wxALIGN_CENTER_VERTICAL|wxEXPAND,0);

	{
		GNC::GCS::IToolController::TIdList toolsLeft;
		toolsLeft.push_back(HCore_DetachWindow);
		toolsLeft.push_back(HCore_WindowLayout);
		wxWindow* toolBarLeft = pToolBar->CreateNewToolBar(m_pPanelManipulacion, toolsLeft);
		m_pSizerIzquierda->Add(toolBarLeft,0, wxALIGN_CENTER_VERTICAL|wxEXPAND,0);		
	}

	m_pBarraCine = new BarraHerramientasBotones(m_pPanelManipulacion);
	m_pBarraCine->AddTool(ID_BOTON_PLAY_STOP,_("Play"),GinkgoResourcesManager::CineBar::GetIcoPlay(),_("Play"));
	m_pBarraCine->Connect(ID_BOTON_PLAY_STOP,wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( GVistaCompleja::OnPlayStopClick),NULL,this);
	m_pBarraCine->SetToolDropDown(ID_BOTON_PLAY_STOP, true);
	m_pBarraCine->Connect(ID_BOTON_PLAY_STOP,wxEVT_COMMAND_AUITOOLBAR_TOOL_DROPDOWN, wxAuiToolBarEventHandler( GVistaCompleja::OnMenuCineClick),NULL,this);

	m_pBarraCine->Realize();
	m_pSizerIzquierda->Add(m_pBarraCine,0, wxALIGN_CENTER_VERTICAL|wxEXPAND,0);

	//se añaden los de sincronizar/dessincronizar
	m_pBarraDerecha = new BarraHerramientasBotones(m_pPanelManipulacion);
	m_pBarraDerecha->AddTool(ID_BOTON_SINCRONIZAR,_("Synchronize/Des synchronize"),GNKVisualizator::ResourcesManager::SynchronizationBar::GetIcoDessincronizar(),_("Synchronize/Des synchronize"),wxITEM_CHECK);
	m_pBarraDerecha->Connect(ID_BOTON_SINCRONIZAR,wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler( GVistaCompleja::OnSincronizarClick),NULL,this);
	m_pBarraDerecha->ToggleTool(ID_BOTON_SINCRONIZAR,false);

	m_pBarraDerecha->Realize();
	m_pSizerDerecho->Add(m_pBarraDerecha,0, wxALIGN_CENTER_VERTICAL|wxEXPAND,0);

	//Connect(wxEVT_CHILD_FOCUS, wxChildFocusEventHandler(GVistaCompleja::OnFocus),NULL,this);
	Connect(wxEVT_KEY_DOWN,wxKeyEventHandler(GVistaCompleja::OnKeyDown),NULL,this);
	Connect(wxEVT_CHAR,wxKeyEventHandler(GVistaCompleja::OnKeyDown),NULL,this);
	Connect(wxEVT_MOUSEWHEEL,wxMouseEventHandler(GVistaCompleja::OnMouseWheel),NULL,this);

	GNC::GCS::Events::EventoModificacionImagen ev2(IVista);
	GNC::GCS::Events::EventoModificacionWidget ev3(IVista);
	GNC::GCS::Events::EventoRender ev4(IVista);
	GNKVisualizator::Events::EventoMostrarOverlay evt5(IVista);

	GNC::GCS::IEntorno::Instance()->GetControladorEventos()->Registrar(this, ev2);
	GNC::GCS::IEntorno::Instance()->GetControladorEventos()->Registrar(this, ev3);
	GNC::GCS::IEntorno::Instance()->GetControladorEventos()->Registrar(this, ev4);
	GNC::GCS::IEntorno::Instance()->GetControladorEventos()->Registrar(this, evt5);

	AddVistaSimple();

	//FUNDAMENTAL PARA QUE FUNCIONE LA HERRAMIENTA DE MAPAS DE COLOR
	IVista->VisualizatorStudy->Viewer = m_VistasSimples[0]->ViewImage2D;

	//m_pManager->InsertarWidget(new GNC::GCS::Widgets::WProgreso(m_VistasSimples.front()->ViewInteractor2D, m_pManager, -1, "progreso", 666));
	m_pManager->EnableAnimation();

	Layout();
}

GVistaCompleja::~GVistaCompleja()
{
	IVista->GetToolController()->FinalizeToolController();
	IVista->ComienzaDestruccion();
	//FUNDAMENTAL PARA QUE los viewers se destruyan correctamente y sin leaks ya que Viewer es smart pointer
	for(TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
		(*it)->UnRefIVista();
	}

	IVista->VisualizatorStudy->Viewer = NULL;

	if (IVista != NULL) {
		GNC::GCS::IEntorno::Instance()->GetControladorComandos()->AbortarComandosDeOwner(IVista);
	}

	//Disconnect(wxEVT_CHILD_FOCUS, wxChildFocusEventHandler(GVistaCompleja::OnFocus),NULL,this);
	this->Disconnect(wxEVT_KEY_DOWN,wxKeyEventHandler(GVistaCompleja::OnKeyDown),NULL,this);
	this->Disconnect(wxEVT_MOUSEWHEEL,wxMouseEventHandler(GVistaCompleja::OnMouseWheel),NULL,this);

	if(m_ListaSincronizacion.size() >0){
		//si esta reproduciendose se para
		Stop();
		//
		DesSincronizar();
	}

	if(m_playTimer!= NULL) {
		delete m_playTimer;
		m_playTimer = NULL;
	}

	m_pManager->EliminarTodosLosWidgets(false);

	m_VistasSimples.clear();

	GNC::GCS::IEntorno::Instance()->FreeWidgetsManager(m_pManager);
	m_pManager = NULL;
	
/*TODO	GNKVisualizator::IReconstructionTool* hReconstruction = GNC::GCS::IEntorno::Instance()->GetControladorHerramientas()->ObtenerHerramientaConcreta<GNKVisualizator::IReconstructionTool>(GNKVisualizator::IReconstructionTool::ID);
	if (hReconstruction != NULL) {
		((GNC::GCS::IContratable<GNKVisualizator::IReconstructionContract>*)hReconstruction)->DesSubscribirsLosDeLaVista(IVista);
	}*/

	IVista->Lock();
	delete IVista;
}

void GVistaCompleja::AddVistaSimple()
{
	GVistaSimple* pVistaSimple = new GVistaSimple(IVista,this);
	m_VistasSimples.push_back(pVistaSimple);
	m_pSizerSeries->Add(pVistaSimple,1,wxEXPAND);


	GNC::GCS::IWidgetsRenderer* pR = pVistaSimple->ViewInteractor2D;
	pR->SetManager(m_pManager);
	pR->SetIOverlay(pVistaSimple);
	//m_pManager->SetRendererActivo(pR);
	std::ostringstream os;
	os << "Renderer " << pVistaSimple << std::endl;
	pR->sid = os.str();
	////

	WidgetRepresentation* pWidgetsRepresentation = WidgetRepresentation::New();
	pWidgetsRepresentation->SetRenderer(pVistaSimple->ViewInteractor2D->FindPokedRenderer(0,0));
	pVistaSimple->ViewInteractor2D->FindPokedRenderer(0,0)->AddViewProp(pWidgetsRepresentation);
	pR->SetRepresentation(pWidgetsRepresentation);
	pWidgetsRepresentation->SetWidgetsManager(m_pManager);
	pWidgetsRepresentation->SetWidgetsRenderer(pR);
	pWidgetsRepresentation->Delete();

	pVistaSimple->ViewImage2D->SetWindowLevelFrom(m_VistasSimples[0]->ViewImage2D);
	pVistaSimple->ViewImage2D->SetLookupTable(m_VistasSimples[0]->ViewImage2D->GetLookupTable(), m_VistasSimples[0]->ViewImage2D->GetIdLookupTable());
	pVistaSimple->ViewImage2D->SetZoom(m_VistasSimples[0]->ViewImage2D->GetZoom());
	pVistaSimple->ViewImage2D->CopyCameraStatus(m_VistasSimples[0]->ViewImage2D);

	for (unsigned int i = 0; i < m_VistasSimples.size(); i++) {
		GNC::GCS::IWidgetsRenderer* pR = m_VistasSimples[i]->ViewInteractor2D;
		pR->SetVID(i, false);
		std::ostringstream os;
		os << "Renderer " << i << std::endl;
		pR->sid = os.str();
	}
}

void GVistaCompleja::LoadSeries(long seriesPk)
{
	IVista->VisualizatorStudy->InitializeContext(seriesPk);

	for(TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) 
	{
		(*it)->setEstudioReferido(IVista->GetEstudio());
	}
	IniciarPipeline();
}

void GVistaCompleja::IniciarPipeline(const GNC::GCS::Ptr<GNC::GCS::HangingProtocol>& hangingProtocol)
{
	m_pManager->EliminarTodosLosWidgets(false);
	m_pPanelManipulacion->Enable(true);


	GNC::GCS::Ptr<std::list<GNKVisualizator::TOverlay> > overlays(IVista->VisualizatorStudy->GetOverlaysImagenActiva());
	OverlaysDelegate->Setup(overlays, IVista);
	GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(new GNKVisualizator::Events::EventoRefrescarListaOverlays(IVista,OverlaysDelegate->GetOverlayList()));

	//contratos que son 1:1 con gvistacompleja
	//esto es correcto porque en la creacion se mete siempre una vista simple

	Delegate->Setup(*IVista->GetEstudio()->GetTagsActiveImage());


	///////////recorrer las vistas simples y pasarles las imagenes	
	if(!m_VistasSimples.empty()) {
		int i = 0;
		for(TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
			GVistaSimple* pvs = *it;
			pvs->EstudioReferido->SetActiveIndex(i);
			pvs->SetOverlays(OverlaysDelegate->GetOverlayList(), OverlaysDelegate->m_EstadoOverlays);

			//despues se establece el WL
			if (Delegate->HasDefaultWindowLevel()) {
				GNC::GCS::IWindowLevelContract::WindowLevel wl = Delegate->GetAndSetDefaultWindowLevel();
				pvs->ViewImage2D->SetDefaultWindowLevel(wl.m_window, wl.m_level);
			}
			else {
				pvs->ViewImage2D->SetAutoDefaultWindowLevel();
			}
			i++;
		}
		m_pManager->SetRendererActivo( (* (m_VistasSimples.begin()) )->ViewInteractor2D );

		WindowLayoutDelegate->Setup(0, IVista->GetEstudio()->GetSliceNumber() - 1);

		ActualizarMaxMinSlider();
	}

	//contrato window level, hay que preguntarle al viewer de la primera vista porque el estudioatencionprimaria no tiene viewer
	IVista->GetToolController()->Enable(HCore_WindowLevel, m_VistasSimples[0]->ViewImage2D->GetNumberOfComponents() == 1);

	if (hangingProtocol.IsValid()) {
		if (hangingProtocol->getLayout().IsValid()) {
			if (hangingProtocol->getLayout()->getMatcher().IsValid()) {
				for (std::vector<long>::iterator it = IVista->VisualizatorStudy->OpenedSeries.begin(); it != IVista->VisualizatorStudy->OpenedSeries.end(); ++it)
				{
					if (hangingProtocol->getLayout()->getMatcher()->MatchesSeries((*it))) 
					{
						LoadSeries(*it);
					}
				}
				WindowLayoutDelegate->SetWindowLayout(hangingProtocol->getLayout()->getRows(), hangingProtocol->getLayout()->getColumns());
			}
		} else {
			LOG_WARN("HangingProtocolDelegate", "trying to build an invalid layout");
		}
	} else {
		GoToSlice(0,false,false);
		GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(new GNC::GCS::Events::EventoModificacionImagen(IVista,GNC::GCS::Events::EventoModificacionImagen::ImagenCargada));
	}
	m_pManager->CrearWidgetAnotador(this);
}



void GVistaCompleja::DetenerPipeline()
{
	m_pManager->OcultarTodosLosWidgets(true, 666);
	m_pManager->DisableAnimation();
	m_pPanelManipulacion->Enable(true);
	m_pManager->Render();
}

void GVistaCompleja::OnCargaFinalizada()
{
	m_pPanelPreview->LoadModel(this, IVista->VisualizatorStudy);
	m_pManager->OcultarTodosLosWidgets(true, 666);
	m_pManager->DisableAnimation();
	m_pManager->Render();
}


void GVistaCompleja::OnPaint(wxPaintEvent& event)
{
	//std::cout << "GVistaCompleja::OnPaint()" << std::endl;
	VistaComplejaBase::OnPaint(event);
	if(GNC::GCS::IEntorno::Instance()->GetControladorVistas()->GetVistaActiva() == IVista)
	{
		wxPaintDC dc(this);
		wxColour colorLineaSeleccion(219, 219, 0, 255);
		dc.SetBrush(wxBrush(colorLineaSeleccion,wxTRANSPARENT));
		dc.SetPen(wxPen(colorLineaSeleccion, 3, wxSOLID));
		dc.DrawRectangle(wxRect(wxPoint(1, 1), wxPoint(dc.GetSize().x-1, dc.GetSize().y-1)));
	}
	//la comprobacion de la textura, se hace la primera vez que se pinta
	if(!m_hasBeenRendered ) {
		if(IVista->EstaCargada()) {
			for(GVistaCompleja::TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
				GVistaSimple* vs = *it;
				try {
					vs->ViewInteractor2D->Render();
					long idTextura = vs->ViewImage2D->GetImageTexture();
					if (idTextura != 0) {
						if ( !glIsTexture(idTextura) ) {
							LOG_ERROR("GVistaCompleja", "Textura no cargada en memoria de video")
							GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(new GNC::GCS::Events::EventoMensajes(NULL,_Std("Error: You have exhausted the system's video memory and has not been able to load the image. Close some studies to free memory."),GNC::GCS::Events::EventoMensajes::PopUpMessage,false));
							GADAPI::ComandoDestruirVistaParams* pParams = new GADAPI::ComandoDestruirVistaParams(IVista);
							GNC::GCS::IEntorno::Instance()->GetControladorComandos()->ProcessAsync(_Std("Destroy View"),new GADAPI::ComandoDestruirVista(pParams),NULL);
						}
					} else {
						//se comprobara mas tarde porque la textura todavia no tiene id
						return;
					}
				}catch(const std::bad_alloc&)
				{
					LOG_ERROR("GVistaCompleja","bad_alloc, error al reservar memoria física");
					GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(new GNC::GCS::Events::EventoMensajes(NULL,_Std("Error: System out of memory. Close some studies to free memory."),GNC::GCS::Events::EventoMensajes::PopUpMessage,false));
					GADAPI::ComandoDestruirVistaParams* pParams = new GADAPI::ComandoDestruirVistaParams(IVista);
					GNC::GCS::IEntorno::Instance()->GetControladorComandos()->ProcessAsync(_Std("Destroy View"),new GADAPI::ComandoDestruirVista(pParams),NULL);
				}
				catch(...)
				{
					LOG_ERROR("GVistaCompleja","excepción general al comprobar errores de memoria");
					GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(new GNC::GCS::Events::EventoMensajes(NULL,_Std("Error: Internal error."),GNC::GCS::Events::EventoMensajes::PopUpMessage,false));
					GADAPI::ComandoDestruirVistaParams* pParams = new GADAPI::ComandoDestruirVistaParams(IVista);
					GNC::GCS::IEntorno::Instance()->GetControladorComandos()->ProcessAsync(_Std("Destroy View"),new GADAPI::ComandoDestruirVista(pParams),NULL);
				}
			}
			m_hasBeenRendered = true;
		}
	}
}

void GVistaCompleja::OnMenuVerClick(wxCommandEvent& )
{
	IVista->OnFocus();
	wxMenu ImageMenu ;
	IVista->GetToolController()->AppendsToolInMenu(this, &ImageMenu, GNC::GCS::ITool::TSeriesMenu);
	m_pBarraIzquierda->SetToolSticky(ID_BOTON_VER, true);
	wxRect rect = m_pBarraIzquierda->GetToolRect(ID_BOTON_VER);
	wxPoint pt = m_pBarraIzquierda->ClientToScreen(rect.GetBottomLeft());
	pt = ScreenToClient(pt);

	PopupMenu(&ImageMenu, pt);
	// make sure the button is "un-stuck"
	m_pBarraIzquierda->SetToolSticky(ID_BOTON_VER, false);
}


void GVistaCompleja::OnMenuCineClick(wxAuiToolBarEvent& evt)
{
	if (evt.IsDropDownClicked())
	{
		wxAuiToolBar* tb = static_cast<wxAuiToolBar*>(evt.GetEventObject());
		tb->SetToolSticky(evt.GetId(), true);

		IVista->OnFocus();
		//se crea el menu
		wxMenu menuVer;
		wxMenu* pMenuFrameRate = new wxMenu();
		menuVer.AppendSubMenu(pMenuFrameRate,_("Speed"));
		//importante si se añade un titulo añadir un periodo!!
		wxString titulos[8] = {wxT("20 fps"), wxT("8 fps"),wxT("4 fps"),wxT("2 fps"),wxT("1 fps"),wxT("0.5 fps"),wxT("0.2 fps"),wxT("0.1 fps")};
		int periodos[8] = {50,125,250,500,1000,2000,5000,10000};
		for(int i=0; i < 8; i++)
		{
			wxMenuItem* pItem = new ItemFrameRate(pMenuFrameRate,titulos[i],this,periodos[i]);
			pMenuFrameRate->Append(pItem);
			if(periodos[i] == m_PeriodoMs) {
				pItem->Check(true);
			}
		}

		wxMenuItem* pItem = new wxMenuItem(&menuVer,wxID_ANY,_("Loop"),_("Repeat on end"),wxITEM_CHECK);
		#ifdef __WXMSW__
		pItem->SetBitmaps(GinkgoResourcesManager::CineBar::GetIcoBucle(),GinkgoResourcesManager::CineBar::GetIcoNoBucle());
		//#else
		//pItem->SetBitmap(GinkgoResourcesManager::CineBar::GetIcoBucle());
		#endif
		//este apend suelta por consola: 'InsertMenuItem()' failed with error 0x00000057, es un buf arreglado pero no afecta negativamente,
		// es xq solo tiene un bitmap mas info:
		//http://trac.wxwidgets.org/ticket/9878
		menuVer.Append(pItem);
		//
		menuVer.Check(pItem->GetId(),m_ReproducirBucle);
		menuVer.Connect(pItem->GetId(),wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( GVistaCompleja::OnBucleClick ),NULL,this);

		if(!m_playTimer->IsRunning()){
			pItem = new wxMenuItem(&menuVer,wxID_ANY,_("Play"),_("Start playback"));
			#ifdef __WXMSW__
			pItem->SetBitmaps(GinkgoResourcesManager::CineBar::GetIcoPlay());
			#else
			pItem->SetBitmap(GinkgoResourcesManager::CineBar::GetIcoPlay());
			#endif
			menuVer.Append(pItem);
			menuVer.Connect(pItem->GetId(),wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( GVistaCompleja::OnPlayStopClick ),NULL,this);
		} else {
			pItem = new wxMenuItem(&menuVer,wxID_ANY,_("Stop"),_("End playback"));
			#ifdef __WXMSW__
			pItem->SetBitmaps(GinkgoResourcesManager::CineBar::GetIcoPause());
			#else
			pItem->SetBitmap(GinkgoResourcesManager::CineBar::GetIcoPause());
			#endif
			menuVer.Append(pItem);
			menuVer.Connect(pItem->GetId(),wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( GVistaCompleja::OnPlayStopClick ),NULL,this);
		}

		wxRect rect = tb->GetToolRect(evt.GetId());
		wxPoint pt = tb->ClientToScreen(rect.GetBottomLeft());
		pt = ScreenToClient(pt);


		PopupMenu(&menuVer, pt);

		// make sure the button is "un-stuck"
		tb->SetToolSticky(evt.GetId(), false);
	}
}


void GVistaCompleja::ActualizarMaxMinSlider()
{
	//depende de m_MinSlice y m_MaxSlice;
	//tb depende del numero de vistasimples
	int minSlider = WindowLayoutDelegate->m_MinSlice;
	int maxSlider = ((WindowLayoutDelegate->m_MaxSlice - WindowLayoutDelegate->m_MinSlice) +1)  - (m_pSizerSeries->GetRows() * m_pSizerSeries->GetCols());
	if (minSlider < maxSlider) {
		m_pSliderSlice->SetRange(minSlider, maxSlider);
		m_pSliderSlice->Enable(true);
		if( !m_pBarraCine->IsShown() ) {
			m_pBarraCine->SetToolBitmap(ID_BOTON_PLAY_STOP, GinkgoResourcesManager::CineBar::GetIcoPlay());
			m_pBarraCine->SetToolShortHelp(ID_BOTON_PLAY_STOP, _("Play"));
			m_pBarraCine->Enable(true);
			m_pBarraCine->Show(true);
		}
		m_pSliderSlice->Show(true);
		m_pBarraDerecha->Show(true);
		m_pBarraDerecha->Refresh();
	} else {
		m_pSliderSlice->SetRange(0, 1);
		if(!m_ListaSincronizacion.empty())
		{
			Sincronizar(false);
		}
		if(m_playTimer->IsRunning()) {
			Stop();
		}
		m_pBarraCine->Hide();
		m_pSliderSlice->Show(false);
		m_pBarraDerecha->Show(false);
	}
}

void GVistaCompleja::SetPeriodo(int milisegundos)
{
	if(milisegundos>0) {
		m_PeriodoMs = milisegundos;
		if(m_playTimer->IsRunning()){
			m_playTimer->Stop();
			m_playTimer->Start(m_PeriodoMs);
		}
	}
}

void GVistaCompleja::OnFocus(wxChildFocusEvent &)
{
	IVista->OnFocus();
}

void GVistaCompleja::OnSize(wxSizeEvent &)
{
	Layout();
}

void GVistaCompleja::OnZSliderScroll (wxScrollEvent& event) {
	int pos = event.GetPosition() - m_posicion;

	if(pos!=0){
		GoToSlice(event.GetPosition(), false);
	}
	event.Skip(false);
}

void GVistaCompleja::OnKeyDown( wxKeyEvent& event )
{
	switch(event.GetKeyCode()){
		case WXK_RIGHT:
			{
				GoToSlice(1);
			}
			break;
		case WXK_LEFT:
			{
				GoToSlice(-1);
			}
			break;
		default:
			event.ResumePropagation(1);
			event.Skip(true);
	}
}

void GVistaCompleja::OnMouseWheel(wxMouseEvent& event)
{
	if (event.ControlDown())
	{
		if (event.ShiftDown()) {
			for (TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
				if(event.GetWheelRotation() > 0)
				{
					(*it)->GoToTSlice(-1);
				}
				else
				{
					(*it)->GoToTSlice(1);
				}
			}
		} else {
			if(event.GetWheelRotation() > 0)
			{
				GoToSlice(-1);
			}
			else
			{
				GoToSlice(1);
			}
		}
	} 
}

void GVistaCompleja::OnPlayStopClick(wxCommandEvent &){
	if(!m_playTimer->IsRunning()) {
		Play();
	} else {
		Stop();
	}
}

void GVistaCompleja::Play()
{
	if(!m_ReproducirBucle && m_pSliderSlice->GetValue() == m_pSliderSlice->GetMax()){
		GoToSlice(0,false);
	}
	m_playTimer->Start(m_PeriodoMs);
	m_pBarraCine->SetToolBitmap(ID_BOTON_PLAY_STOP, GinkgoResourcesManager::CineBar::GetIcoStop());
	m_pBarraCine->SetToolShortHelp(ID_BOTON_PLAY_STOP, _("Stop"));
	m_pBarraCine->Refresh();

	AtencionPrimaria::Events::EventoSincronizacion* evt = new AtencionPrimaria::Events::EventoSincronizacion(IVista,AtencionPrimaria::Events::EventoSincronizacion::Play);
	GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(evt);
}

void GVistaCompleja::Stop()
{
	//stop
	m_playTimer->Stop();
	m_pBarraCine->SetToolBitmap(ID_BOTON_PLAY_STOP, GinkgoResourcesManager::CineBar::GetIcoPlay());
	m_pBarraCine->SetToolShortHelp(ID_BOTON_PLAY_STOP, _("Play"));
	m_pBarraCine->Refresh();

	AtencionPrimaria::Events::EventoSincronizacion* evt = new AtencionPrimaria::Events::EventoSincronizacion(IVista,AtencionPrimaria::Events::EventoSincronizacion::Stop);
	GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(evt);
}

void GVistaCompleja::OnBucleClick(wxCommandEvent &){
	m_ReproducirBucle = !m_ReproducirBucle;
}

void GVistaCompleja::OnTimer()
{
	if((m_pSliderSlice->GetValue() +1 ) >m_pSliderSlice->GetMax() && m_ReproducirBucle){
		GoToSlice(0,false);
	} else {
		GoToSlice(1);

		if(m_pSliderSlice->GetValue()  == m_pSliderSlice->GetMax() && !m_ReproducirBucle){
			Stop();
		}
	}
}

//region "Realizacion de la interfaz IAnnotator"
std::string GVistaCompleja::GetTopLeftAnnotation(GNC::GCS::Contexto3D* c)
{
	GNC::GCS::IWidgetsRenderer* r = ( c == NULL? NULL : c->GetRenderer());
	if (r == NULL) {
		return m_VistasSimples[0]->GetTopLeftAnnotation(c);
	} else {
		for (TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
			if ( (*it)->ViewInteractor2D == r) {
				return (*it)->GetTopLeftAnnotation(c);
			}
		}
	}
	return "";
}

std::string GVistaCompleja::GetTopRightAnnotation(GNC::GCS::Contexto3D* c)
{
	GNC::GCS::IWidgetsRenderer* r = ( c == NULL? NULL : c->GetRenderer());
	if (r == NULL) {
		return m_VistasSimples[0]->GetTopRightAnnotation(c);
	} else {
		for (TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
			if ( (*it)->ViewInteractor2D == r) {
				return (*it)->GetTopRightAnnotation(c);
			}
		}
	}
	return "";
}

std::string GVistaCompleja::GetBottomLeftAnnotation(GNC::GCS::Contexto3D* c)
{
	GNC::GCS::IWidgetsRenderer* r = ( c == NULL? NULL : c->GetRenderer());
	if (r == NULL) {
		return m_VistasSimples[0]->GetBottomLeftAnnotation(c);
	} else {
		for (TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
			if ( (*it)->ViewInteractor2D == r) {
				return (*it)->GetBottomLeftAnnotation(c);
			}
		}
	}
	return "";
}

std::string GVistaCompleja::GetBottomRightAnnotation(GNC::GCS::Contexto3D* c)
{
	GNC::GCS::IWidgetsRenderer* r = ( c == NULL? NULL : c->GetRenderer());
	if (r == NULL) {
		return m_VistasSimples[0]->GetBottomRightAnnotation(c);
	} else {
		for (TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
			if ( (*it)->ViewInteractor2D == r) {
				return (*it)->GetBottomRightAnnotation(c);
			}
		}
	}
	return "";
}

std::string GVistaCompleja::GetAnotacionPosicion(GNC::GCS::Vector* pPosicion)
{
	GNC::GCS::IWidgetsRenderer* r = m_pManager->GetRendererActivo();
	for (TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
		if ( (*it)->ViewInteractor2D == r) {
			return (*it)->GetAnotacionPosicion(pPosicion);
		}
	}
	return "";
}
//endregion

void GVistaCompleja::ActualizarImagen()
{
	//m_pImagenOriginal->Modified();
	GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(new GNC::GCS::Events::EventoModificacionImagen(IVista,GNC::GCS::Events::EventoModificacionImagen::ImagenModificada, -1));
}

//----------DIALOGO SINCRONIZACION...-------------
class DialogoSincronizacion: public DialogoSincronizacionBase {
public:
	typedef std::vector<GNKVisualizator::Vista2D*>	TipoListaVistas;
	DialogoSincronizacion(wxWindow* parent, TipoListaVistas listaVistas):
			DialogoSincronizacionBase(parent),
			m_lista(listaVistas)
	{
		for(TipoListaVistas::iterator it = listaVistas.begin(); it!=listaVistas.end(); ++it){
			m_pCheckListVistas->AppendString(wxString::FromUTF8((*it)->GetTitulo().c_str()));
		}
		Layout();
	}

	~DialogoSincronizacion(){

	}

	void OnAceptarClick(wxCommandEvent &event){
		EndModal(wxID_OK);
	}

	TipoListaVistas GetVistasSeleccionadas(){
		TipoListaVistas resultado;
		wxArrayInt selecciones;
		for(unsigned int i=0; i<m_pCheckListVistas->GetCount();i++){
			//se busca la vista
			for(TipoListaVistas::iterator itVistas = m_lista.begin(); itVistas!= m_lista.end(); ++itVistas){
				if(m_pCheckListVistas->IsChecked(i)){
					resultado.push_back(m_lista[i]);
					break;
				}
			}
		}
		return resultado;
	}

protected:
	TipoListaVistas m_lista;
};

//---------- end DIALOGO SINCRONIZACION...-------------

void GVistaCompleja::OnSincronizarClick(wxCommandEvent &event)
{
	bool enabled = m_pBarraDerecha->GetToolToggled(ID_BOTON_SINCRONIZAR);
	Sincronizar(enabled);
}

void GVistaCompleja::Sincronizar(bool sincronizar)
{
	//si esta toogled...
	if(!sincronizar) {
		//dessincronizar
		//si esta reproduciendose se para
		Stop();
		//
		DesSincronizar();
	} else {
		//sincronizar
		typedef GNC::GCS::IControladorVistas::TipoListaVistas TipoListaIVistas;

		TipoListaVista2D lista;
		{
			TipoListaIVistas listaTmp = GNC::GCS::IEntorno::Instance()->GetControladorVistas()->GetVistas();

			//pillamos las vistas simples
			for(TipoListaIVistas::iterator it = listaTmp.begin(); it!= listaTmp.end(); ++it){
				GNKVisualizator::Vista2D* v = dynamic_cast<GNKVisualizator::Vista2D*> ((*it));
				if(v!=NULL && v!=IVista && v->GVista->EsSincronizable()){
					lista.push_back(v);
				}
			}
		}

		DialogoSincronizacion* dialogoSincronizacion = new DialogoSincronizacion(this, lista);
		int answer = dialogoSincronizacion->ShowModal();
		if (answer == wxID_OK) {
			//se pillan las vistas seleccionadas
			lista = dialogoSincronizacion->GetVistasSeleccionadas();
			if(!lista.empty()){
				//nos metemos en la lista
				lista.push_back(IVista);
				//y ahora sincronizamos a tol mundo
				for(DialogoSincronizacion::TipoListaVistas::iterator it = lista.begin(); it!= lista.end(); ++it){
					(*it)->GVista->Sincronizar(lista);
				}
				m_pBarraDerecha->ToggleTool(ID_BOTON_SINCRONIZAR,true);
				m_pBarraDerecha->Refresh();
			} else {
				m_pBarraDerecha->ToggleTool(ID_BOTON_SINCRONIZAR,false);
				m_pBarraDerecha->Refresh();
			}
		}
	}
}

void GVistaCompleja::Sincronizar(const TipoListaVista2D& lista)
{
	//si esta reproduciendose se para
	Stop();
	//se suscribe a los eventos de los brothers
	for(TipoListaVista2D::const_iterator it = lista.begin(); it!= lista.end(); ++it){
		if((*it) != IVista){
			AtencionPrimaria::Events::EventoSincronizacion evento((*it));
			GNC::GCS::IEntorno::Instance()->GetControladorEventos()->Registrar(this,evento);
			m_ListaSincronizacion.push_back((*it));
		}
	}
	m_pBarraDerecha->ToggleTool(ID_BOTON_SINCRONIZAR,true);
	m_pBarraDerecha->Refresh();
}

bool GVistaCompleja::EsSincronizable(){
	return m_ListaSincronizacion.empty() && m_pSliderSlice->IsShown();
}

void GVistaCompleja::DesSincronizar(){
	for(TipoListaVista2D::iterator it = m_ListaSincronizacion.begin(); it!= m_ListaSincronizacion.end(); ++it){
		AtencionPrimaria::Events::EventoSincronizacion eventoOtro((*it));
		GNC::GCS::IEntorno::Instance()->GetControladorEventos()->DesRegistrar(this,eventoOtro);
	}
	m_ListaSincronizacion.clear();

	m_pBarraCine->Enable(true);
	m_pBarraCine->Refresh();

	//dessincronizamos
	AtencionPrimaria::Events::EventoSincronizacion* evento = new AtencionPrimaria::Events::EventoSincronizacion(IVista,AtencionPrimaria::Events::EventoSincronizacion::Dessincronizar);
	GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(evento);

	m_pBarraDerecha->ToggleTool(ID_BOTON_SINCRONIZAR,false);
	m_pBarraDerecha->Refresh();
}

void GVistaCompleja::GoToSlice(int pos, bool relativa, bool propagar, bool render){
	if (WindowLayoutDelegate->m_MaxSlice == WindowLayoutDelegate->m_MinSlice) {
		return;
	}
	int posicion;

	if(relativa){
		posicion = m_pSliderSlice->GetValue() + pos;
	}else{
		posicion = pos;
	}

	posicion = std::max(m_pSliderSlice->GetMin(), std::min(m_pSliderSlice->GetMax(), posicion));

	//si la posicion + las que se muestran en el layout actual es mayor al número de slices de la serie, no se avanza

	if(posicion + ( (int) m_VistasSimples.size() ) > IVista->GetEstudio()->GetSliceNumber()) {
		return;
	}

	m_pSliderSlice->SetValue(posicion);
	m_posicion = posicion;

	//se reestablece el window/level antes del evento de modificación porque ese evento lo escucha la herramienta
	Delegate->Setup(*IVista->GetEstudio()->GetTagsImage(m_posicion));

	//se le indica al manager el vid
	IVista->GetEstudio()->ActiveFileIndex = posicion;
	for(TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
		(*it)->GoToSlice(posicion,false,false);

		//despues se establece el WL
		if (Delegate->HasCurrentWindowLevel()) {
			GNC::GCS::IWindowLevelContract::WindowLevel wl = Delegate->GetCurrentWindowLevel();
			(*it)->ViewImage2D->SetDefaultWindowLevel(wl.m_window, wl.m_level);
		} else if (Delegate->HasDefaultWindowLevel()) {
			GNC::GCS::IWindowLevelContract::WindowLevel wl = Delegate->GetAndSetDefaultWindowLevel();
			(*it)->ViewImage2D->SetDefaultWindowLevel(wl.m_window, wl.m_level);			
		} else {
			(*it)->ViewImage2D->SetAutoDefaultWindowLevel();
		}

		GNC::GCS::Ptr<GNKVisualizator::TListaOverlays> pLista = IVista->VisualizatorStudy->GetOverlaysImagen(posicion);
		(*it)->SetOverlays(pLista,OverlaysDelegate->m_EstadoOverlays);

		posicion++;
	}
		
	
	GNC::GCS::Events::EventoModificacionImagen* pEvt = new GNC::GCS::Events::EventoModificacionImagen(IVista,GNC::GCS::Events::EventoModificacionImagen::SliceCambiado, -1, render);
	GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(pEvt);

	if(propagar){
		GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(new AtencionPrimaria::Events::EventoSincronizacion(IVista, AtencionPrimaria::Events::EventoSincronizacion::Scroll,pos,relativa));
	}
}

int GVistaCompleja::GetCurrentSlice() const
{
	return m_posicion;
}

void GVistaCompleja::PreProcesarEvento(GNC::GCS::Events::IEvent* evt, std::list<GNC::GCS::Events::IEvent*>& lista)
{
	if (evt == NULL) {
		std::cerr << "Error: Evento nulo" << std::endl;
		return;
	}
	switch (evt->GetCodigoEvento()) {
		case ginkgoEVT_GNKVisualizator_EventoSincronizacion:
			{
				AtencionPrimaria::Events::EventoSincronizacion* pEvt = dynamic_cast<AtencionPrimaria::Events::EventoSincronizacion*>(evt);
				if (pEvt == NULL) {
					std::cerr << "Error al interpretar evento como evento de sincronización: Evento = " << evt << std::endl;
					return;
				}
				if (pEvt->GetTipoEvento() != AtencionPrimaria::Events::EventoSincronizacion::Scroll) {
					return;
				}
				int posicion = pEvt->GetPosicion();
				if(pEvt->EsRelativa()){
					posicion += m_pSliderSlice->GetValue();
				}
				posicion = std::max(m_pSliderSlice->GetMin(), std::min(m_pSliderSlice->GetMax(), posicion));
			}
			break;
		case ginkgoEVT_Core_ModificacionImagen:
			{
				GNC::GCS::Events::EventoModificacionImagen* pEvt = dynamic_cast<GNC::GCS::Events::EventoModificacionImagen*>(evt);
				if (pEvt == NULL) {
					std::cerr << "Error al interpretar evento como evento de modificación de imagen: Evento = " << evt << std::endl;
					return;
				}
				switch (pEvt->GetTipo()) {
					case GNC::GCS::Events::EventoModificacionImagen::ImagenRecalibrada:
						{
							lista.push_back(new GNC::GCS::Events::EventoModificacionImagen(IVista,GNC::GCS::Events::EventoModificacionImagen::ImagenCargada, -1));
							lista.push_back(new GNC::GCS::Events::EventoModificacionFichero(IVista,GNC::GCS::Events::EventoModificacionFichero::FicheroModificado));
						}
						break;
					default:
						break;
				}
				if (pEvt->GetApilarRender()) {
					lista.push_back(new GNC::GCS::Events::EventoRender(IVista,pEvt->GetSliceAfectado()));
				}
			}
			break;
		case ginkgoEVT_Core_ModificacionWidget:
			{
				GNC::GCS::Events::EventoModificacionWidget* pEvt = dynamic_cast<GNC::GCS::Events::EventoModificacionWidget*>(evt);
				if (pEvt == NULL) {
					std::cerr << "Error al interpretar evento como evento de modificación de widget: Evento = " << evt << std::endl;
					return;
				}
				// TODO: Este flujo es muy mejorable
				lista.push_back(new GNC::GCS::Events::EventoModificacionFichero(IVista,GNC::GCS::Events::EventoModificacionFichero::FicheroModificado,IVista));
			}
			break;
		case ginkgoEVT_GNKVisualizator_EventoMostrarOverlay:
			{
				lista.push_back(new GNC::GCS::Events::EventoRender(IVista));
			}
			break;
		default:
			break;
	}
}

void GVistaCompleja::ProcesarEvento(GNC::GCS::Events::IEvent *evt)
{
	switch (evt->GetCodigoEvento()) {
		case ginkgoEVT_GNKVisualizator_EventoSincronizacion:
			{
				AtencionPrimaria::Events::EventoSincronizacion* pEvt = dynamic_cast<AtencionPrimaria::Events::EventoSincronizacion*>(evt);
				if(pEvt!=NULL){
					if(pEvt->GetTipoEvento() == AtencionPrimaria::Events::EventoSincronizacion::Scroll){
						GoToSlice(pEvt->GetPosicion(),pEvt->EsRelativa(),false);
					} else if (pEvt->GetTipoEvento() == AtencionPrimaria::Events::EventoSincronizacion::Play) {
						m_pBarraCine->Enable(false);
						m_pBarraCine->Refresh();
					} else if (pEvt->GetTipoEvento() == AtencionPrimaria::Events::EventoSincronizacion::Stop) {
						m_pBarraCine->Enable(true);
						m_pBarraCine->Refresh();
					} else if (pEvt->GetTipoEvento() == AtencionPrimaria::Events::EventoSincronizacion::Dessincronizar) {
						for(TipoListaVista2D::iterator it = m_ListaSincronizacion.begin(); it!= m_ListaSincronizacion.end(); ++it){
							if((*it) == pEvt->GetVista()){
								GNC::GCS::IEntorno::Instance()->GetControladorEventos()->DesRegistrar(this,*pEvt);
								m_ListaSincronizacion.erase(it);
								if(m_ListaSincronizacion.empty()){
									m_pBarraDerecha->ToggleTool(ID_BOTON_SINCRONIZAR,false);
									m_pBarraDerecha->Refresh();
								}
								return;
							}
						}
					}
				}
			}
			break;

			case ginkgoEVT_Core_ModificacionImagen:
			{
				GNC::GCS::Events::EventoModificacionImagen* pEvt = dynamic_cast<GNC::GCS::Events::EventoModificacionImagen*>(evt);
				if (pEvt == NULL) {
					std::cerr << "Error al interpretar evento como evento de modificación de imagen: Evento = " << evt << std::endl;
					return;
				}
				switch (pEvt->GetTipo()) {
					case GNC::GCS::Events::EventoModificacionImagen::ImagenModificada:
					case GNC::GCS::Events::EventoModificacionImagen::ImagenCargada:
					case GNC::GCS::Events::EventoModificacionImagen::MapaModificado:
					case GNC::GCS::Events::EventoModificacionImagen::SliceCambiado:
							{
								for(TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
									(*it)->ViewImage2D->Modified();
								}
								GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(new GNC::GCS::Events::EventoModificacionImagen(IVista,GNC::GCS::Events::EventoModificacionImagen::AnotacionesEstaticasModificadas));
							}
							break;
					case GNC::GCS::Events::EventoModificacionImagen::ImagenRecalibrada:
						{
							
								int i = 0;
								for(TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it, i++) {
									m_VistasSimples[i]->GetEstudio()->RecalibrateActiveImage(pEvt->GetNewSpacing(), pEvt->GetNewOrigin());

									//esto es necesario para establecer bien las coordenadas cuando se cambia el origen
									(*it)->GoToSlice(m_posicion + i,false,false);
									pEvt->SetSlice(-1);
									(*it)->RefrescarOverlays(OverlaysDelegate->m_EstadoOverlays);
								}
								//se vuelve hacer setup del contrato mapa de color
							//	GNKVisualizator::IContratoMapaColor::Setup(m_pImagenOriginal);
								
							
						}
					break;
					case GNC::GCS::Events::EventoModificacionImagen::VisualizacionImagenModificada:
						//para que pille bien el window/level
						for(TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
							if (pEvt->AutoCalculeWindowLevel()) {
								(*it)->ViewImage2D->SetAutoDefaultWindowLevel();
								(*it)->ViewImage2D->ResetToDefaultWindowLevel();
								Delegate->SetUserDefinedWindowLevel((*it)->ViewImage2D->GetWindow(), (*it)->ViewImage2D->GetLevel());
							} else {
								if(pEvt->ResetWindowLevel()) {
									if (Delegate->HasDefaultWindowLevel()) {
										GNC::GCS::IWindowLevelContract::WindowLevel wl = Delegate->GetAndSetDefaultWindowLevel();
										(*it)->ViewImage2D->SetDefaultWindowLevel(wl.m_window, wl.m_level);			
									} else {
										(*it)->ViewImage2D->SetAutoDefaultWindowLevel();
									}

									(*it)->ViewImage2D->ResetToDefaultWindowLevel();
								} else {
									(*it)->ViewImage2D->SetWindow(pEvt->GetWindow());
									(*it)->ViewImage2D->SetLevel(pEvt->GetLevel());
									//(*it)->ViewImage2D->UpdateImage();
									for(TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
																							  //(*it)->ViewInteractor2D->Refresh(false);
																							  //(*it)->ViewInteractor2D->Render();
									}
								}
							}
							if(pEvt->ResetCurrentPoint())
							{
								/*
								double* bounds  = (*it)->ViewImage2D->GetInput()->GetBounds();
								const double* currentPoint = (*it)->ViewImage2D->GetCurrentPoint();
								double  pos[3] =
								{
									(bounds[0]+bounds[1])/2.0,
									(bounds[2]+bounds[3])/2.0,
									currentPoint[2]
								};

								(*it)->ViewImage2D->SetCurrentPoint(pos);
								*/
							}
							if(pEvt->ResetZoom())
							{
								(*it)->ViewImage2D->ResetZoom(false, pEvt->GetResetZoomMode());
							}
							//(*it)->ViewImage2D->Modified();
						}
						GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(new GNC::GCS::Events::EventoModificacionImagen(IVista,GNC::GCS::Events::EventoModificacionImagen::AnotacionesEstaticasModificadas));
					break;
					case GNC::GCS::Events::EventoModificacionImagen::ImagenDescargada:
					break;
					case GNC::GCS::Events::EventoModificacionImagen::AnotacionesEstaticasModificadas:

					break;
				}
			}//fin modificacion
			break;

		case ginkgoEVT_Core_Render:
			{
				for(TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
					(*it)->ViewInteractor2D->Refresh(false);
				}
			}
			break;
		case ginkgoEVT_Core_ModificacionFichero:
			{
				GNC::GCS::Events::EventoModificacionFichero* pEvt = new GNC::GCS::Events::EventoModificacionFichero();
				pEvt->SetVista(IVista);
				GNC::GCS::IEntorno::Instance()->GetControladorEventos()->ProcesarEvento(pEvt);
			}
			break;
		case ginkgoEVT_GNKVisualizator_EventoMostrarOverlay:
			{
				for(TListaGVistasSimples::iterator it = m_VistasSimples.begin(); it != m_VistasSimples.end(); ++it) {
					(*it)->RefrescarOverlays(OverlaysDelegate->m_EstadoOverlays);
					(*it)->ViewImage2D->Modified();
				}
			}
			break;
	}
}

/*TODO
void GVistaCompleja::GetImage(GNC::GCS::IContratoExportacionImages::ImageType::Pointer& img, int index, bool conWidgets, const GNC::GCS::Vector& size)
{
	if (!IVista->VisualizatorStudy->Viewer->IsInstalledAndInitialized())
		return ;

	GNKVisualizator::ImprimeVistaSimple imprimeVistaSimple(m_pManager, IVista->VisualizatorStudy, index);

	if(IVista->VisualizatorStudy->Viewer->GetNumberOfComponents() == 3) {
		imprimeVistaSimple.PrintImageRGB(img,conWidgets, size);
	} else {
		vtkScalarsToColors* pTabla = GetLookupTable();
		if(pTabla != NULL) {
			imprimeVistaSimple.PrintImage(img, conWidgets, pTabla, size);
		}
	}
}*/
//
//void GVistaCompleja::SetReconstructionMode(GNKVisualizator::Reconstruction::ReconstructionMode mode)
//{
//
//	switch (mode) {
//		case GNKVisualizator::Reconstruction::RM_SurfaceRendering:
//			{
//				typedef MedicalViewer::Reconstruction::GUI::wxSurfaceRendering TReconstructionWin;
//				TReconstructionWin* win = new TReconstructionWin(this, GNKVisualizator::IReconstructionContract::Estudio);
//				win->Show(true);				
//			}
//			break;
//		case GNKVisualizator::Reconstruction::RM_VolumeRendering:
//			{
//				typedef MedicalViewer::Reconstruction::GUI::wxVolumeRendering TReconstructionWin;
//				TReconstructionWin* win = new TReconstructionWin(this, GNKVisualizator::IReconstructionContract::Estudio, GetViewerActivo()->GetWindow(), GetViewerActivo()->GetLevel());
//				win->Show(true);
//			}
//			break;
//		case GNKVisualizator::Reconstruction::RM_OrthogonalMPR:
//		case GNKVisualizator::Reconstruction::RM_MIP3D:
//		case GNKVisualizator::Reconstruction::RM_3D_Endoscopy:
//		case GNKVisualizator::Reconstruction::RM_MaxMode:
//			wxMessageBox(_("Reconstruction mode not implemented:\n"), _("Error"), wxICON_ERROR);
//			break;
//		default:
//			wxMessageBox(_("Reconstruction mode not supported:\n"), _("Error"), wxICON_ERROR);
//			break;
//			
//	}	
//}
