package lu.tudor.santec.gecamed.usermanagement.gui;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingConstants;

import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Act;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Invoice;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.TrashedAct;
import lu.tudor.santec.gecamed.billing.ejb.session.beans.InvoiceBean;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.InvoiceInterface;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialogImpl;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

public class ActAmountDialog extends GECAMedBaseDialogImpl implements ActionListener
{
	/* ======================================== */
	// 		CONSTANTS
	/* ======================================== */
	
	private static final long	serialVersionUID	= 1L;
	
	private static final int	MAX_ACTS			= 1024;
	
	private static final int	MAX_INVOICES		= 256;
	
	
	
	/* ======================================== */
	// 		MEMBERS
	/* ======================================== */
	
	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(ActAmountDialog.class.getName());
	
	
	private JLabel			progressLabel;
	
	private JCheckBox		negativeAmountsBox;
	
	private	JProgressBar	progressBar;
	
	private JButton			startProgressButton;
	
	private JButton			abortProgressButton;
	
	private SetActAmounts	worker;
	
	
	
	/* ======================================== */
	// 		CONSTRUCTORS
	/* ======================================== */
	
	public ActAmountDialog()
	{
		super(MainFrame.getInstance(), 
				Translatrix.getTranslationString("GeneralSettings.ActAmountDialog.title"),
				OK_BUTTON_MODE);
//		super.setModalityType(ModalityType.MODELESS);
		super.setModal(false);
		
		JLabel			description;
		CellConstraints	cc	= new CellConstraints();
		
		
		worker				= new SetActAmounts();
		
		mainPanel.setLayout(new FormLayout("5px, p, min(5px;p):g, p, 5px", 
				"5px,p," +	// description
				"10px,p," +	// recalculate negative amounts box
				"15px,p,"+	// progress description
				"5px,p," +	// progress bar
				"5px,p," +	// buttons
				"5px"));
		
		description			= new JLabel(Translatrix.getTranslationString(
				"GeneralSettings.ActAmountDialog.description"));
		
		negativeAmountsBox	= new JCheckBox(Translatrix.getTranslationString(
				"GeneralSettings.ActAmountDialog.recalculateNegativeAmounts"));
		negativeAmountsBox.setOpaque(false);
		
		
		progressLabel		= new JLabel(" ");
		progressLabel.setOpaque(false);
		
		progressBar			= new JProgressBar(SwingConstants.HORIZONTAL);
		progressBar.setStringPainted(true);
		progressBar.setBackground(Color.WHITE);
//		progressBar.setForeground(GECAMedColors.c_TabSelection);
//		UIManager.put("ProgressBar.selectionBackground", Color.WHITE);
//		UIManager.put("ProgressBar.selectionForeground", GECAMedColors.c_TabSelection);
		progressBar.setString(Translatrix.getTranslationString(
				"GeneralSettings.ActAmountDialog.pressStart"));
		
		startProgressButton	= new JButton(Translatrix.getTranslationString(
				"GeneralSettings.ActAmountDialog.start"));
		startProgressButton.addActionListener(this);
		
		abortProgressButton	= new JButton(Translatrix.getTranslationString(
				"GeneralSettings.ActAmountDialog.abort"));
		abortProgressButton.addActionListener(this);
		abortProgressButton.setEnabled(false);
		
		mainPanel.add(description,			cc.xyw(2, 2, 3));
		mainPanel.add(negativeAmountsBox,	cc.xyw(2, 4, 3));
		mainPanel.add(progressLabel,		cc.xyw(2, 6, 3));
		mainPanel.add(progressBar,			cc.xyw(2, 8, 3));
		mainPanel.add(startProgressButton,	cc.xyw(2,10, 1));
		mainPanel.add(abortProgressButton,	cc.xyw(4,10, 1));
		
		pack();
		setLocationRelativeTo(getOwner());
	}
	
	
	
	/* ======================================== */
	// 		CLASS BODY
	/* ======================================== */
	
	public void actionPerformed(ActionEvent e)
	{
		Object	source	= e.getSource();
		
		if (source == startProgressButton
				&& !worker.isAlive())
		{
			worker	= new SetActAmounts();
			worker.start();
		}
		else if (source == abortProgressButton
				&& worker.isAlive() && !worker.isInterrupted())
		{
			abortThreat();
		}
	}
	
	
	@Override
	public void okActionCalled()
	{
		abortThreat();
		setVisible(false);
	}
	
	
	@Override
	public void closeActionCalled()
	{
		abortThreat();
		setVisible(false);
	}
	
	
	
	/* ======================================== */
	// 		HELP METHODS
	/* ======================================== */
	
	private void abortThreat ()
	{
		Cursor	oldCursor	= super.getCursor();
		try
		{
			// set wait cursor
			super.setCursor(new Cursor(Cursor.WAIT_CURSOR));
			
			// advice the thread to stop after the current operation ...
			worker.run = false;
			// ... and wait, until the thread is stopped
			worker.join();
		}
		catch (InterruptedException e)
		{
			logger.log(Level.ERROR, e.getMessage(), e);
		}
		finally
		{
			// reset old cursor
			super.setCursor(oldCursor);
		}
	}
	
	
	
	/* ======================================== */
	// 		CLASS: SetActAmounts
	/* ======================================== */
	
	private class SetActAmounts extends Thread
	{
		/* ======================================== */
		// 		MEMBERS
		/* ======================================== */
		
		private boolean				run;
		
		private InvoiceInterface	manager;
		
		
		
		public SetActAmounts()
		{
			run		= false;
			manager	= (InvoiceInterface) ManagerFactory.getRemote(InvoiceBean.class);
		}
		
		
		
		/* ======================================== */
		// 		CLASS BODY
		/* ======================================== */
		
		@Override
		public void run()
		{
			List<?>		acts		= null;
			Iterator<?>	iter;
			Object		next;
			Act			act			= null;
			TrashedAct	trashedAct	= null;
			int			total;
			int			current;
			int			errors;
			String		errorMsg;
			boolean		queryTrashed	= false;
			Integer		id;
			String		step;
			
			
			// at first enable & disable the buttons
			startProgressButton.setEnabled(false);
			abortProgressButton.setEnabled(true);
			
			progressLabel.setText(Translatrix.getTranslationString(
					"GeneralSettings.ActAmountDialog.preparing"));
			
			
			
			run			= true;
			current		= 0;
			total		= manager.getNoOfActsWithoutSetAmount();
			errors		= 0;
			errorMsg	= null;
			step		= "%" + Translatrix.getTranslationString("GeneralSettings.ActAmountDialog.step", 
					new String[] { "1", "2" });
			
			progressBar.setMinimum(current);
			progressBar.setMaximum(total);
			progressBar.setValue(current);
			
			progressBar.setString(new StringBuilder()
					.append(current)
					.append(" | ")
					.append(total)
					.append(" - ")
					.append((int)(progressBar.getPercentComplete()*100))
					.append("% ")
					.append(errorMsg != null ? errorMsg : "")
					.toString());
			
			/* ---------------------------------------- */
			// Calculate amount of acts 
			/* ---------------------------------------- */
			try
			{
				while (run && current < total)
				{
					progressLabel.setText(Translatrix.getTranslationString(
							"GeneralSettings.ActAmountDialog.progressLoading"));
					
					
					if (!queryTrashed)
					{
						acts	= manager.getActsWithoutSetAmount(MAX_ACTS);
						
						if (acts == null || acts.isEmpty())
							queryTrashed = true;
					}
					
					if (queryTrashed)
					{
						acts	= manager.getTrashedActsWithoutSetAmount(MAX_ACTS);
					}
					else if (acts.size() < MAX_ACTS)
					{
						queryTrashed = true;
					}
					
					progressLabel.setText(Translatrix.getTranslationString(
							"GeneralSettings.ActAmountDialog.inProgress"));
					
					for (iter = acts.iterator(); run && iter.hasNext(); )
					{
						next	= iter.next();
						try
						{
							if (next instanceof Act)
							{
								act	= (Act) next;
								act.monetize();
								manager.saveAct(act);
							}
							else if (next instanceof TrashedAct)
							{
								trashedAct	= (TrashedAct) next;
								id			= trashedAct.getId();
								act			= trashedAct.recreateAct();
								act.monetize();
								trashedAct	= new TrashedAct(act);
								trashedAct.setId(id);
								manager.saveTrashedAct(trashedAct);
							}
						}
						catch (Exception e)
						{
							e.printStackTrace();
							errors++;
							if (next instanceof Act && act != null)
								logger.warn("Error while handling act with ID = "+act.getId()+"\n"+e.getMessage());
							if (next instanceof TrashedAct && trashedAct != null)
								logger.warn("Error while handling trashed act with ID = "+trashedAct.getId()+"\n"+e.getMessage());
							errorMsg	= Translatrix.getTranslationString(
									"GeneralSettings.ActAmountDialog.errors", 
									new String[] { String.valueOf(errors) });
						}
						
						if (iter.hasNext())
							current++;
						progressBar.setValue(current);
						progressBar.setString(new StringBuilder()
								.append(current)
								.append(" | ")
								.append(total)
								.append(" - ")
								.append((int)(progressBar.getPercentComplete() * 100))
								.append(negativeAmountsBox.isSelected() ? step : "% ")
								.append(errorMsg != null ? errorMsg : "")
								.toString());
					}
				}
				
				
				if (negativeAmountsBox.isSelected())
				{
					/* ---------------------------------------- */
					// recalculate negative amounts
					// (invoices & acts)
					/* ---------------------------------------- */
					List<Invoice>		invoices	= null;
					Invoice				invoice		= null;
					Iterator<Invoice>	invoiceIter;
					
					total	= manager.getNoOfInvoicesWithNegativeAmounts();
					current	= 0;
					step	= Translatrix.getTranslationString(
							"GeneralSettings.ActAmountDialog.step", new String[] { "2", "2" });
					progressBar.setMaximum(total);
					progressBar.setValue(current);
					
					while (run && current < total)
					{
						progressLabel.setText(Translatrix.getTranslationString(
								"GeneralSettings.ActAmountDialog.progressLoading"));
						
						invoices	= manager.getInvoicesWithNegativeAmount(MAX_INVOICES);
						progressLabel.setText(Translatrix.getTranslationString(
								"GeneralSettings.ActAmountDialog.inProgress"));
						
						for (invoiceIter = invoices.iterator(); run && invoiceIter.hasNext(); )
						{
							try
							{
								invoice	= invoiceIter.next();
								invoice.monetize();
								manager.saveInvoice(invoice);
							}
							catch (Exception e)
							{
								e.printStackTrace();
								errors++;
								logger.warn("Error while handling invoice with ID = "+(invoice != null ? invoice.getId() : "unknown")+"\n"+e.getMessage());
								errorMsg	= Translatrix.getTranslationString(
										"GeneralSettings.ActAmountDialog.errors", 
										new String[] { String.valueOf(errors) });
							}
							
							if (invoiceIter.hasNext())
								current++;
							progressBar.setValue(current);
							progressBar.setString(new StringBuilder()
									.append(current)
									.append(" | ")
									.append(total)
									.append(" - ")
									.append((int)(progressBar.getPercentComplete() * 100))
									.append("%")
									.append(step)
									.append(errorMsg != null ? errorMsg : "")
									.toString());
						}
					}
				}
			}
			catch (Exception e)
			{
				logger.log(Level.ERROR, "Error while trying to set the amount of all acts.", e);
			}
			finally
			{
				if (run)
					progressLabel.setText(Translatrix.getTranslationString(
							"GeneralSettings.ActAmountDialog.progressDone"));
				else
					progressLabel.setText(Translatrix.getTranslationString(
							"GeneralSettings.ActAmountDialog.progressAbort"));
				
				// at last enable & disable the buttons
				abortProgressButton.setEnabled(false);
				startProgressButton.setEnabled(true);
			}
		}
	}
}
