/*-
 * #%L
 * Fiji distribution of ImageJ for the life sciences.
 * %%
 * Copyright (C) 2007 - 2017 Fiji developers.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-2.0.html>.
 * #L%
 */
package spim.fiji.plugin.apply;

import bdv.BigDataViewer;
import bdv.viewer.ViewerFrame;

import java.awt.Button;
import java.awt.Checkbox;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Label;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowEvent;
import java.text.DecimalFormat;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.SwingUtilities;

import spim.vecmath.Transform3D;
import net.imglib2.realtransform.AffineTransform3D;

public class BigDataViewerTransformationWindow
{
	final AffineTransform3D t;
	final protected Timer timer;
	
	protected boolean isRunning = true;
	protected boolean wasCancelled = false;
	protected boolean ignoreScaling = true;

	public BigDataViewerTransformationWindow( final BigDataViewer bdv )
	{
		this.t = new AffineTransform3D();
		final Frame frame = new Frame( "Current Global Transformation" );
		frame.setSize( 400, 200 );

		/* Instantiation */
		final GridBagLayout layout = new GridBagLayout();
		final GridBagConstraints c = new GridBagConstraints();

		final Label text1 = new Label( "1.00000   0.00000   0.00000   0.00000", Label.CENTER );
		final Label text2 = new Label( "0.00000   1.00000   0.00000   0.00000", Label.CENTER );
		final Label text3 = new Label( "0.00000   0.00000   1.00000   0.00000", Label.CENTER );

		text1.setFont( new Font( Font.MONOSPACED, Font.PLAIN, 14 ) );
		text2.setFont( new Font( Font.MONOSPACED, Font.PLAIN, 14 ) );
		text3.setFont( new Font( Font.MONOSPACED, Font.PLAIN, 14 ) );

		final Button apply = new Button( "Apply Transformation" );
		final Button cancel = new Button( "Cancel" );
		final Checkbox ignoreScale = new Checkbox( "Ignore scaling factor from BigDataViewer", ignoreScaling );

		/* Location */
		frame.setLayout( layout );

		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 0;

		frame.add( text1, c );

		++c.gridy;
		frame.add( text2, c );

		++c.gridy;
		frame.add( text3, c );

		c.insets = new Insets( 20,0,0,0 );
		++c.gridy;
		frame.add( ignoreScale, c );

		c.insets = new Insets( 20,0,0,0 );
		++c.gridy;
		frame.add( apply, c );

		c.insets = new Insets( 0,0,0,0 );
		++c.gridy;
		frame.add( cancel, c );

		apply.addActionListener( new ApplyButtonListener( frame, bdv ) );
		cancel.addActionListener( new CancelButtonListener( frame, bdv ) );
		ignoreScale.addItemListener( new ItemListener(){ public void itemStateChanged( final ItemEvent arg0 ) { ignoreScaling = ignoreScale.getState(); } });

		frame.setVisible( true );

		timer = new Timer();
		timer.schedule( new BDVChecker( bdv, text1, text2, text3 ), 500 );
	}

	public AffineTransform3D getTransform() { return t; }
	public boolean isRunning() { return isRunning; }
	public boolean wasCancelled() { return wasCancelled; }

	protected void close( final Frame parent, final BigDataViewer bdv )
	{
		if ( parent != null )
			parent.dispose();

		isRunning = false;
	}

	protected class CancelButtonListener implements ActionListener
	{
		final Frame parent;
		final BigDataViewer bdv;

		public CancelButtonListener( final Frame parent, final BigDataViewer bdv )
		{
			this.parent = parent;
			this.bdv = bdv;
		}
		
		@Override
		public void actionPerformed( final ActionEvent arg0 ) 
		{ 
			wasCancelled = true;
			close( parent, bdv );
		}
	}

	protected class BDVChecker extends TimerTask
	{
		final BigDataViewer bdv;
		final Label text1, text2, text3;

		public BDVChecker(
				final BigDataViewer bdv,
				final Label text1,
				final Label text2,
				final Label text3 )
		{
			this.bdv = bdv;
			this.text1 = text1;
			this.text2 = text2;
			this.text3 = text3;
		}

		@Override
		public void run()
		{
			if ( isRunning )
			{
				if ( bdv != null )
					bdv.getViewer().getState().getViewerTransform( t );

				t.set( 0, 0, 3 );
				t.set( 0, 1, 3 );
				t.set( 0, 2, 3 );

				if ( ignoreScaling )
				{
					final double[] m = new double[ 16 ];
					int i = 0;
					for ( int row = 0; row < 3; ++row )
						for ( int col = 0; col < 4; ++col )
							m[ i++ ] = t.get( row, col );
	
					m[ 15 ] = 1;
	
					final Transform3D trans = new Transform3D( m );	
					trans.setScale( 1 );
					trans.get( m );

					i = 0;
					for ( int row = 0; row < 3; ++row )
						for ( int col = 0; col < 4; ++col )
							t.set( m[ i++ ], row, col );
				}

				final DecimalFormat df = new DecimalFormat( "0.00000" );

				text1.setText(
						df.format( t.get( 0, 0 ) ).substring( 0, 7 ) + "   " + df.format( t.get( 0, 1 ) ).substring( 0, 7 ) + "   " +
						df.format( t.get( 0, 2 ) ).substring( 0, 7 ) + "   " + df.format( t.get( 0, 3 ) ).substring( 0, 7 ) );

				text2.setText(
						df.format( t.get( 1, 0 ) ).substring( 0, 7 ) + "   " + df.format( t.get( 1, 1 ) ).substring( 0, 7 ) + "   " +
						df.format( t.get( 1, 2 ) ).substring( 0, 7 ) + "   " + df.format( t.get( 1, 3 ) ).substring( 0, 7 ) );

				text3.setText(
						df.format( t.get( 2, 0 ) ).substring( 0, 7 ) + "   " + df.format( t.get( 2, 1 ) ).substring( 0, 7 ) + "   " +
						df.format( t.get( 2, 2 ) ).substring( 0, 7 ) + "   " + df.format( t.get( 2, 3 ) ).substring( 0, 7 ) );

				// Reschedule myself (new instance is required, why?)
				timer.schedule( new BDVChecker( bdv,text1, text2, text3 ), 500 );
			}
		}
		
	}
	
	protected class ApplyButtonListener implements ActionListener
	{
		final Frame parent;
		final BigDataViewer bdv;

		public ApplyButtonListener( final Frame parent, final BigDataViewer bdv )
		{
			this.parent = parent;
			this.bdv = bdv;
		}
		
		@Override
		public void actionPerformed( final ActionEvent arg0 ) 
		{ 
			wasCancelled = false;
			close( parent, bdv );
		}
	}

	public static void disposeViewerWindow( final BigDataViewer bdv )
	{
		try
		{
			SwingUtilities.invokeAndWait( new Runnable()
			{
				@Override
				public void run()
				{
					final ViewerFrame frame = bdv.getViewerFrame();
					final WindowEvent windowClosing = new WindowEvent( frame, WindowEvent.WINDOW_CLOSING );
					frame.dispatchEvent( windowClosing );
				}
			} );
		}
		catch ( final Exception e )
		{}
	}

}