package siprp.medicina.processo.ui;

import static siprp.medicina.processo.logic.MedicinaProcessoLogic.*;
import static siprp.medicina.processo.logic.MedicinaProcessoLogic.CREATE_EXAME;
import static siprp.medicina.processo.logic.MedicinaProcessoLogic.LOAD_TRABALHADOR;
import static siprp.medicina.processo.logic.MedicinaProcessoLogic.SELECT_TREE_NODE;
import info.clearthought.layout.TableLayout;
import info.clearthought.layout.TableLayoutConstraints;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import leaf.LeafWindow;
import leaf.LeafLogic.LeafLogicActionBinding;
import leaf.LeafLogic.LeafUIActionBinding;
import leaf.LeafWindow.ActionActivation;

import org.apache.cayenne.CayenneDataObject;

import siprp.database.cayenne.objects.Trabalhadores;
import siprp.database.cayenne.objects.TrabalhadoresConsultas;
import siprp.database.cayenne.objects.TrabalhadoresConsultasDatas;
import siprp.database.cayenne.objects.TrabalhadoresConsultasDatasEmails;
import siprp.database.cayenne.objects.TrabalhadoresConsultasDatasObservacoes;
import siprp.database.cayenne.objects.TrabalhadoresEcds;
import siprp.database.cayenne.objects.TrabalhadoresEcdsDatas;
import siprp.database.cayenne.objects.TrabalhadoresEcdsDatasEmails;
import siprp.database.cayenne.objects.TrabalhadoresEcdsDatasObservacoes;
import siprp.database.cayenne.objects.TrabalhadoresProcesso;
import siprp.medicina.MarcacaoConsultaExtendedPanel;
import siprp.medicina.processo.ProcessoDataProvider;
import siprp.medicina.processo.estrutura.ConsultaMutableTreeNode;
import siprp.medicina.processo.estrutura.DataMutableTreeNode;
import siprp.medicina.processo.estrutura.ECDsMutableTreeNode;
import siprp.medicina.processo.estrutura.EstruturaProcessoRenderer;
import siprp.medicina.processo.estrutura.MailMutableTreeNode;
import siprp.medicina.processo.estrutura.MarcacaoMutableTreeNode;
import siprp.medicina.processo.estrutura.ObservacoesMutableTreeNode;
import siprp.medicina.processo.estrutura.ProcessoMutableTreeNode;
import siprp.medicina.processo.estrutura.TrabalhadorMutableTreeNode;

public class ProcessoTreePanel extends JPanel
{
	private static final long serialVersionUID = 1L;

	private final LeafWindow parentWindow;

	private final JScrollPane mainScroll = new JScrollPane();;

	protected TrabalhadorMutableTreeNode rootNode;

	private final Map<CayenneDataObject, DefaultMutableTreeNode> nodeByObject = new HashMap<CayenneDataObject, DefaultMutableTreeNode>();

	@ActionActivation(onSelect = SELECT_TREE_NODE, onChange = "")
	public JTree mainTree;

	protected ProcessoDataProvider provider;

	public ProcessoTreePanel(LeafWindow parentWindow)
	{
		super();
		this.parentWindow = parentWindow;
		try
		{
			provider = ProcessoDataProvider.getProvider();

		} catch( Exception e )
		{
			provider = null;
			e.printStackTrace();
		}
		setupLayout();
		setupComponents();
		placeComponents();
	}

	private void setupLayout()
	{
		double[] cols = new double[] {
			TableLayout.FILL
		};
		double[] rows = new double[] {
			TableLayout.FILL
		};
		TableLayout layout = new TableLayout( cols, rows );
		this.setLayout( layout );
	}

	private void setupComponents()
	{
		this.setBorder( BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), "Processos" ) );
		mainScroll.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED );
		mainScroll.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
		rootNode = new TrabalhadorMutableTreeNode( "" );
		mainTree = new JTree( rootNode );
		mainTree.setExpandsSelectedPaths( true );
		mainTree.setCellRenderer( new EstruturaProcessoRenderer() );
		mainScroll.setViewportView( mainTree );
	}

	private void placeComponents()
	{
		this.add( mainScroll, new TableLayoutConstraints( 0, 0 ) );
	}

	@LeafUIActionBinding(action = LOAD_TRABALHADOR)
	public void setTrabalhador( Trabalhadores trabalhador )
	{
		clear();
		if( trabalhador == null )
		{
			return;
		}
		rootNode.setUserObject( trabalhador );
		List<DefaultMutableTreeNode> nodes = loadProcessos( trabalhador );
		for( DefaultMutableTreeNode node : nodes )
		{
			addNodeTo( node, rootNode );
		}
		((DefaultTreeModel) mainTree.getModel()).nodeStructureChanged( rootNode );
		int count = mainTree.getRowCount();
		for( int n = count - 1; n >= 0; n-- )
		{
			mainTree.expandRow( n );
		}
	}

	@LeafUIActionBinding(action = CREATE_PROCESSO)
	public void createProcessoNode( TrabalhadoresProcesso processo )
	{
		if( processo != null )
		{
			ProcessoMutableTreeNode node = new ProcessoMutableTreeNode( processo );
			addAndRefresh( node, rootNode );
		}
	}

	@LeafUIActionBinding(action = APAGAR_PROCESSO)
	public void removeProcessoNode( TrabalhadoresProcesso processo )
	{
		if( processo != null )
		{
			ProcessoMutableTreeNode node = new ProcessoMutableTreeNode( processo );
			removeNode( node );
		}
	}

	@LeafUIActionBinding(action = CREATE_CONSULTA)
	public void createConsultaNode( TrabalhadoresConsultas consulta )
	{
		if( consulta != null )
		{
			DefaultMutableTreeNode parentNode = nodeByObject.get( consulta.getToTrabalhadoresProcesso() );
			ConsultaMutableTreeNode node = new ConsultaMutableTreeNode( consulta );
			node.setData( consulta.getData() );
			addAndRefresh( node, parentNode );
		}
	}

	@LeafUIActionBinding(action = CREATE_CONSULTA_MARCACAO)
	public void createConsultaMarcacaoNode( TrabalhadoresConsultasDatas marcacao )
	{
		if( marcacao != null )
		{
			DefaultMutableTreeNode parentNode = nodeByObject.get( marcacao.getToTrabalhadoresConsultas() );
			DataMutableTreeNode node = new DataMutableTreeNode( marcacao );
			addAndRefresh( node, parentNode );
		}
	}

	@LeafUIActionBinding(action = CREATE_CONSULTA_MARCACAO_EMAIL)
	public void createConsultaMarcacaoEmailNode( TrabalhadoresConsultasDatasEmails email )
	{
		if( email != null )
		{
			DefaultMutableTreeNode parentNode = nodeByObject.get( email.getToTrabalhadoresConsultasDatas() );
			MailMutableTreeNode node = new MailMutableTreeNode( email );
			addAndRefresh( node, parentNode );
		}
	}

	@LeafUIActionBinding(action = CREATE_CONSULTA_MARCACAO_OBSERVACOES)
	public void createConsultaMarcacaoObservacoesNode( TrabalhadoresConsultasDatasObservacoes observacoes )
	{
		if( observacoes != null )
		{
			DefaultMutableTreeNode parentNode = nodeByObject.get( observacoes.getToTrabalhadoresConsultasDatas() );
			ObservacoesMutableTreeNode node = new ObservacoesMutableTreeNode( observacoes );
			addNodeTo( node, parentNode );
			((DefaultTreeModel) mainTree.getModel()).nodeStructureChanged( parentNode );
			mainTree.setSelectionPath( new TreePath( node.getPath() ) );
		}
	}

	@LeafUIActionBinding(action = CREATE_EXAME)
	public void createExameNode( TrabalhadoresEcds exame )
	{
		if( exame != null )
		{
			DefaultMutableTreeNode parentNode = nodeByObject.get( exame.getToTrabalhadoresProcesso() );
			ECDsMutableTreeNode node = new ECDsMutableTreeNode( exame );
			node.setData( exame.getData() );
			addAndRefresh( node, parentNode );
		}
	}

	@LeafUIActionBinding(action = CREATE_EXAME_MARCACAO)
	public void createExameMarcacaoNode( TrabalhadoresEcdsDatas marcacao )
	{
		if( marcacao != null )
		{
			DefaultMutableTreeNode parentNode = nodeByObject.get( marcacao.getToTrabalhadoresEcds() );
			DataMutableTreeNode node = new DataMutableTreeNode( marcacao );
			addAndRefresh( node, parentNode );
		}
	}

	@LeafUIActionBinding(action = CREATE_EXAME_MARCACAO_EMAIL)
	public void createExameMarcacaoEmailNode( TrabalhadoresEcdsDatasEmails email )
	{
		if( email != null )
		{
			DefaultMutableTreeNode parentNode = nodeByObject.get( email.getToTrabalhadoresEcdsDatas() );
			MailMutableTreeNode node = new MailMutableTreeNode( email );
			addAndRefresh( node, parentNode );
		}
	}

	@LeafUIActionBinding(action = CREATE_EXAME_MARCACAO_OBSERVACOES)
	public void createConsultaMarcacaoObservacoesNode( TrabalhadoresEcdsDatasObservacoes observacoes )
	{
		if( observacoes != null )
		{
			DefaultMutableTreeNode parentNode = nodeByObject.get( observacoes.getToTrabalhadoresEcdsDatas() );
			ObservacoesMutableTreeNode node = new ObservacoesMutableTreeNode( observacoes );
			addNodeTo( node, parentNode );
			((DefaultTreeModel) mainTree.getModel()).nodeStructureChanged( parentNode );
			mainTree.setSelectionPath( new TreePath( node.getPath() ) );
		}
	}

	@LeafUIActionBinding(action = REFRESH)
	public void refresh()
	{
		DefaultMutableTreeNode selected = getSelectedNode();
		if( selected != null )
		{
			// ((DefaultTreeModel) mainTree.getModel()).nodeStructureChanged(
			// rootNode );
			mainTree.setSelectionPath( new TreePath( rootNode.getPath() ) );
			mainTree.setSelectionPath( new TreePath( selected.getPath() ) );
		}
		revalidate();
		repaint();
	}

	protected List<DefaultMutableTreeNode> loadProcessos( Trabalhadores trabalhador )
	{
		List<DefaultMutableTreeNode> result = new ArrayList<DefaultMutableTreeNode>();
		List<TrabalhadoresProcesso> processos = trabalhador.getTrabalhadoresProcessoArray();
		for( TrabalhadoresProcesso trabalhadoresProcesso : processos )
		{
			result.add( loadProcesso( trabalhadoresProcesso ) );
		}
		return result;
	}

	private ProcessoMutableTreeNode loadProcesso( TrabalhadoresProcesso processo )
	{
		ProcessoMutableTreeNode node = new ProcessoMutableTreeNode( processo );
		node.removeAllChildren();
		for( TrabalhadoresConsultas current : (List<TrabalhadoresConsultas>) processo.getTrabalhadoresConsultasArray() )
		{
			addNodeTo( loadConsulta( current ), node );
		}
		for( TrabalhadoresEcds current : (List<TrabalhadoresEcds>) processo.getTrabalhadoresEcdsArray() )
		{
			addNodeTo( loadExame( current ), node );
		}

		return node;
	}

	private ECDsMutableTreeNode loadExame( TrabalhadoresEcds exame )
	{
		ECDsMutableTreeNode node = new ECDsMutableTreeNode( exame );
		node.setData( exame.getData() );
		node.removeAllChildren();
		List<TrabalhadoresEcdsDatas> marcacoes = exame.getTrabalhadoresEcdsDatasArray();
		for( TrabalhadoresEcdsDatas marcacao : marcacoes )
		{
			addNodeTo( loadExameMarcacao( marcacao ), node );
		}
		return node;
	}

	private DataMutableTreeNode loadExameMarcacao( TrabalhadoresEcdsDatas marcacao )
	{
		DataMutableTreeNode node = new DataMutableTreeNode( marcacao );
		for( TrabalhadoresEcdsDatasEmails email : (List<TrabalhadoresEcdsDatasEmails>) marcacao.getTrabalhadoresEcdsDatasEmailsArray() )
		{
			addNodeTo( new MailMutableTreeNode( email ), node );
		}
		for( TrabalhadoresEcdsDatasObservacoes observacoes : (List<TrabalhadoresEcdsDatasObservacoes>) marcacao.getTrabalhadoresEcdsDatasObservacoesArray() )
		{
			addNodeTo( new ObservacoesMutableTreeNode( observacoes ), node );
		}
		return node;
	}

	private ConsultaMutableTreeNode loadConsulta( TrabalhadoresConsultas consulta )
	{
		ConsultaMutableTreeNode node = new ConsultaMutableTreeNode( consulta );
		node.setData( consulta.getData() );
		node.removeAllChildren();
		List<TrabalhadoresConsultasDatas> marcacoes = consulta.getTrabalhadoresConsultasDatasArray();
		for( TrabalhadoresConsultasDatas marcacao : marcacoes )
		{
			addNodeTo( loadMarcacao( marcacao ), node );
		}
		return node;
	}

	private DataMutableTreeNode loadMarcacao( TrabalhadoresConsultasDatas marcacao )
	{
		DataMutableTreeNode node = new DataMutableTreeNode( marcacao );
		for( TrabalhadoresConsultasDatasEmails email : (List<TrabalhadoresConsultasDatasEmails>) marcacao.getTrabalhadoresConsultasDatasEmailsArray() )
		{
			addNodeTo( new MailMutableTreeNode( email ), node );
		}
		for( TrabalhadoresConsultasDatasObservacoes observacoes : (List<TrabalhadoresConsultasDatasObservacoes>) marcacao.getTrabalhadoresConsultasDatasObservacoesArray() )
		{
			addNodeTo( new ObservacoesMutableTreeNode( observacoes ), node );
		}
		return node;
	}

	private void addNodeTo( DefaultMutableTreeNode node, DefaultMutableTreeNode to )
	{
		if( node.getUserObject() != null && node.getUserObject() instanceof CayenneDataObject )
		{
			nodeByObject.put( (CayenneDataObject) node.getUserObject(), node );
		}
		to.add( node );
	}

	private void addAndRefresh( DefaultMutableTreeNode node, DefaultMutableTreeNode parentNode )
	{
		addNodeTo( node, parentNode );
		refresh( node, parentNode );
	}

	private void removeNode( DefaultMutableTreeNode node )
	{
		if( node != null )
		{
			DefaultMutableTreeNode parent = (DefaultMutableTreeNode) node.getParent();
			if( parent == null )
			{
				parent = rootNode;
			}
			node.removeFromParent();
			nodeByObject.remove( node.getUserObject() );
			refresh( parent, rootNode );
		}
	}

	private void refresh( DefaultMutableTreeNode newSelection, DefaultMutableTreeNode parentNode )
	{
		((DefaultTreeModel) mainTree.getModel()).nodeStructureChanged( parentNode );
		mainTree.setSelectionPath( new TreePath( newSelection.getPath() ) );
	}

	private DefaultMutableTreeNode getSelectedNode()
	{
		DefaultMutableTreeNode result = null;
		Object[] nodes = mainTree.getSelectionPath() == null ? null : mainTree.getSelectionPath().getPath();
		if( nodes != null && nodes.length > 0 && nodes[nodes.length - 1] instanceof DefaultMutableTreeNode )
		{
			result = (DefaultMutableTreeNode) nodes[nodes.length - 1];
		}
		return result;
	}

	public void clear()
	{
		rootNode.setUserObject( "" );
		mainTree.clearSelection();
		mainTree.setRootVisible( true );
		rootNode.removeAllChildren();
		((DefaultTreeModel) mainTree.getModel()).nodeStructureChanged( rootNode );
	}
}
