package j3deditor.bin.editor.tools;

import j3deditor.bin.components.PopupDialog;

import j3deditor.bin.hierarchy.J3DeAppearance;

import j3deditor.bin.hierarchy.J3DeTexture;

import j3deditor.bin.util.ProgressIndicator;
import j3deditor.bin.util.Utilities;

import java.awt.Color;
import java.awt.FileDialog;
import java.awt.Image;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.io.File;

import javax.media.j3d.Texture;

import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.SwingWorker;


/**
 * Dialog for adding a texture to an object.
 * 
 * @author Risto Seene
 */
public class TextureDialog extends PopupDialog{

	private J3DeAppearance appearance;

	private Image[] images;

	private ImageIcon[] icons;


	private JRadioButton t2d;

	private JRadioButton t3d;

	private JLabel image;

	private JComboBox mmMode;

	private JTextField depth;

	private JComboBox dLevel;

	private JComboBox format;

	private int levels = 1;

	private int currentLevel = 0;

	private String imgPath;


	/**
	 * Creates an instance of <code>TextureDialog</code>
	 * which can add a texture to the specified appearance node.
	 * 
	 * @param appearance
	 * 	appearance node new texture will be added to
	 */
	public TextureDialog(J3DeAppearance appearance){
		this.initDialog(Utilities.image.getImage("icons/texture.png"), Utilities.translator.translate(this.getClass(), "title"), 380, 340);
		this.appearance = appearance;

		this.initContent();
		this.setVisible(true);
	}

	/**
	 * Initializes the content of this dialog.
	 */
	private void initContent(){
		this.setLayout(null);

		t2d = new JRadioButton(Utilities.translator.translate(this.getClass(), "texture2d"), true);
		t2d.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					if(t2d.isSelected()){
						changeImageCount(1);
						depth.setText("1");
						depth.setEnabled(false);
						dLevel.setEnabled(false);
					}
				}
			});
		t2d.setBounds(20, 20, 120, (int)(t2d.getPreferredSize().getHeight()));
		t3d = new JRadioButton("", false);
		String t3dInfo = Utilities.translator.translate(this.getClass(), "texture3d");
		if(!Utilities.userdata.getBooleanData("tex3Dsupport")){
			t3dInfo = t3dInfo + " (" + Utilities.translator.translate(this.getClass(), "notSupported") + ")";
			t3d.setEnabled(false);
		}
		else {
			t3d.addActionListener(new ActionListener(){

					public void actionPerformed(ActionEvent e){
						if(t3d.isSelected()){
							depth.setEnabled(true);
							dLevel.setEnabled(true);
						}
					}
				});
		}
		t3d.setText(t3dInfo);
		t3d.setBounds(140, 20, (int)(t3d.getPreferredSize().getWidth()), (int)(t3d.getPreferredSize().getHeight()));
		ButtonGroup bGroup = new ButtonGroup();
		bGroup.add(t2d);
		bGroup.add(t3d);
		this.add(t2d);
		this.add(t3d);

		image = new JLabel();
		image.setOpaque(true);
		image.setHorizontalAlignment(JLabel.CENTER);
		image.setBackground(Color.gray);
		image.setBounds(20, 50, 120, 120);
		this.add(image);

		mmMode = new JComboBox();
		String[] mmModes = J3DeTexture.getMipMapModes();
		for(int i = 0; i < mmModes.length; i++){
			mmMode.addItem(mmModes[i]);
		}
		mmMode.setBounds(150, 50, 200, (int)(mmMode.getPreferredSize().getHeight()));
		mmMode.setToolTipText(Utilities.translator.translate(this.getClass(), "mmMode"));
		this.add(mmMode);

		depth = new JTextField("1");
		depth.setEnabled(false);
		depth.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						int lvl = Integer.parseInt(depth.getText());
						if(lvl < 1){
							throw new Exception();
						}
						levels = lvl;
						changeImageCount(levels);
					} catch(Throwable t){
						Utilities.out.throwWarning(Utilities.translator.translate(TextureDialog.class, "invalid"));
					}
					depth.setText(levels + "");
				}
			});
		depth.setBounds(150, 90, 90, (int)(depth.getPreferredSize().getHeight()));
		depth.setToolTipText(Utilities.translator.translate(this.getClass(), "depth"));
		this.add(depth);

		dLevel = new JComboBox();
		dLevel.setEnabled(false);
		dLevel.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					try{
						int level = dLevel.getSelectedIndex();
						if(level >= 0 && level < levels){
							currentLevel = level;
							image.setIcon(icons[currentLevel]);
						}
					} catch(Throwable t){
						Utilities.out.throwWarning(t);
					}
				}
			});
		dLevel.setBounds(250, 90, 100, (int)(depth.getPreferredSize().getHeight()));
		dLevel.setToolTipText(Utilities.translator.translate(this.getClass(), "dLevel"));
		this.add(dLevel);

		JButton load = new JButton(Utilities.translator.translate(this.getClass(), "loadImg"));

		load.setBounds(150, 140, 200, (int)(load.getPreferredSize().getHeight()));
		load.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					String info = Utilities.translator.translate(TextureDialog.class, "loadImg");
					FileDialog fd = new FileDialog(new JFrame(), info, FileDialog.LOAD);
					fd.setVisible(true);
					String file = fd.getDirectory() + fd.getFile();
					if(fd.getDirectory() != null && fd.getFile() != null){
						loadImage(file);
					}
				}
			});
		this.add(load);

		this.changeImageCount(1);

		format = new JComboBox();
		String[] formats = J3DeTexture.getFormats();
		for(int i = 0; i < formats.length; i++){
			format.addItem(formats[i]);
		}
		try{
			format.setSelectedItem("RGB");
		} catch(Throwable t){
		}
		format.setBounds(150, 200, 200, (int)(format.getPreferredSize().getHeight()));
		this.add(format);

		JLabel fLabel = new JLabel(Utilities.translator.translate(this.getClass(), "format"), JLabel.RIGHT);
		fLabel.setBounds(20, 200, 120, (int)(format.getPreferredSize().getHeight()));
		this.add(fLabel);

		JButton ok = new JButton(Utilities.translator.translate(this.getClass(), "ok"));
		ok.addActionListener(new ActionListener(){

				public void actionPerformed(ActionEvent e){
					createTexture();
				}
			});
		ok.setBounds(200, 280, 150, (int)(ok.getPreferredSize().getHeight()));
		this.add(ok);
	}


	/**
	 * Changes the number of texture images.
	 * 
	 * @param newCount
	 * 	new image count
	 */
	private void changeImageCount(int newCount){
		this.levels = newCount;
		Image[] imgs = new Image[newCount];
		ImageIcon[] ics = new ImageIcon[newCount];

		if(this.images != null && this.icons != null){
			for(int i = 0; i < newCount; i++){
				if(this.images.length > i && this.images[i] != null){
					imgs[i] = this.images[i];
				}
				if(this.icons.length > i && this.icons[i] != null){
					ics[i] = this.icons[i];
				}
			}
		}

		this.images = imgs;
		this.icons = ics;

		if(currentLevel > (levels - 1)){
			currentLevel = 0;
			this.image.setIcon(this.icons[0]);
		}

		if(dLevel != null){
			dLevel.removeAllItems();
			for(int i = 0; i < levels; i++){
				dLevel.addItem(i);
			}
			dLevel.setSelectedIndex(currentLevel);
		}
	}

	/**
	 * Loads an image from file.
	 * 
	 * @param file
	 * 	image file
	 */
	private void loadImage(String file){
		this.imgPath = file;

		SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>(){

			public Void doInBackground(){
				try{
					Image img = Utilities.image.getImage(new File(imgPath));
					if(img != null){
						ImageIcon ii = new ImageIcon(img);
						int width = ii.getIconWidth();
						int height = ii.getIconHeight();

						if((width & (width - 1)) != 0 || (height & (height - 1)) != 0){
							img = resizeImage(img, powerOfTwo(width), powerOfTwo(height));
						}

						images[currentLevel] = img;
						icons[currentLevel] = getPreviewImage(img);

						image.setIcon(icons[currentLevel]);
					}
				} catch(Throwable t){
					Utilities.out.throwError(t);
				}
				setModal(true);
				return null;
			}
		};
		String info = Utilities.translator.translate(this.getClass(), "loadingImg");
		worker.addPropertyChangeListener(new ProgressIndicator(info));
		this.setModal(false);
		worker.execute();
	}

	/**
	 * Creates preview image.
	 * 
	 * @param img
	 * 	image which preview image will be created
	 * @return preview image
	 */
	private ImageIcon getPreviewImage(Image img){
		ImageIcon ii = new ImageIcon(img);
		int width = ii.getIconWidth();
		int height = ii.getIconHeight();

		if(width == height){
			img = this.resizeImage(img, 120, 120);
		}
		else if(width > height){
			height = (int)(((double)height) / ((double)width) * 120);
			width = 120;
			img = this.resizeImage(img, width, height);
		}
		else {
			width = (int)(((double)width) / ((double)height) * 120);
			height = 120;
			img = this.resizeImage(img, width, height);
		}

		return new ImageIcon(img);
	}

	/**
	 * Creates the thexture.
	 */
	private void createTexture(){
		SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>(){

			public Void doInBackground(){
				try{
					for(int i = 0; i < images.length; i++){
						if(images[i] == null){
							Utilities.out.throwWarning("Images on all levels must be set!");
							setModal(true);
							return null;
						}
					}

					int type = J3DeTexture.TEXTURE_2D;
					if(t3d.isEnabled() && t3d.isSelected()){
						type = J3DeTexture.TEXTURE_3D;
					}
					Texture texture = J3DeTexture.createTexture(type, images, mmMode.getSelectedIndex(), format.getSelectedIndex());

					if(texture != null){
						appearance.setTexture(texture);
					}

					dispose();
				} catch(Throwable t){
					Utilities.out.throwError(t);
					setModal(true);
				}

				return null;
			}
		};
		String info = Utilities.translator.translate(this.getClass(), "creatingTex");
		worker.addPropertyChangeListener(new ProgressIndicator(info));
		this.setModal(false);
		worker.execute();
	}


	/**
	 * Changes the size of the specified image.
	 * 
	 * @param image
	 * 	image which size will be changed
	 * @param width
	 * 	new width
	 * @param height
	 * 	new height
	 * @return resized image
	 */
	private Image resizeImage(Image image, int width, int height){
		if(width < 1){
			width = 1;
		}
		if(height < 1){
			height = 1;
		}
		return image.getScaledInstance(width, height, Image.SCALE_SMOOTH);
	}

	/**
	 * Returns the nearest value which is power of two.
	 * 
	 * @param value
	 * 	value
	 * @return the nearest value which is power of two
	 */
	private int powerOfTwo(int value){
		double log2 = (Math.log(value) / Math.log(2));
		int result = (int)(Math.pow(2, Math.round(log2)));
		if(result < 1){
			result = 1;
		}
		return result;
	}

}
