/*  
 * Copyright 2004-2009 unitarou <boss@unitarou.org>. 
 * All rights reserved.
 * 
 * This program and the accompanying materials are made available under the terms of 
 * the Common Public License v1.0 which accompanies this distribution, 
 * and is available at http://opensource.org/licenses/cpl.php
 * 
 * Contributors:
 *     unitarou - initial API and implementation
 */
package org.unitarou.yukinoshita.view.jface.wizard.pc;
import java.io.File;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;

import org.unitarou.io.Ios;
import org.unitarou.lang.ArgumentChecker;
import org.unitarou.lang.Log;
import org.unitarou.lang.LogFactory;
import org.unitarou.lang.LogLevel;
import org.unitarou.lang.Strings;
import org.unitarou.ml.Message;
import org.unitarou.sgf.util.pc.ProblemType;
import org.unitarou.swt.FolderPanel;
import org.unitarou.swt.ProgressPanel;
import org.unitarou.swt.UTable;
import org.unitarou.swt.UTableProvider;
import org.unitarou.util.SimpleTaskListener;
import org.unitarou.yukinoshita.Yukinoshita;
import org.unitarou.yukinoshita.context.ContextValue;
import org.unitarou.yukinoshita.context.CurrentContext;
import org.unitarou.yukinoshita.view.jface.util.SwtFactory;

/**
 * Ï·pÌp[^(áFÏ·ætH_[AVWðg¤©)ðÝè·éy[WÅ·B
 * @author UNITAROU &lt;boss@unitarou.org&gt;
 */
public class ConvertingWizardPage extends WizardPage {
	
	/** ±ÌNXÌK[ */
	static private final Log log_s_ = LogFactory.getLog(ConvertingWizardPage.class);
	
	/** ÛÇætH_[ÌftHgÅ· */
	static private final ContextValue<File> DES_FOLDER
			= new ContextValue<File>(
					ConvertingWizardPage.class, 
					"desFolder",  //$NON-NLS-1$
					Ios.getCurrentDirectory());

	/** uÎÛtH_:{0}v */
    static private final Message LB_SRC_FOLDER
    		= new Message(ConvertingWizardPage.class, "lbSrcFolder"); //$NON-NLS-1$
    
	/** uÛ¶ætH_v */
    static private final Message LB_DES_FOLDER
    		= new Message(ConvertingWizardPage.class, "lbDesFolder"); //$NON-NLS-1$

	/** uÛ¶ætH_Ìwèv */
    static private final Message NT_DLG_SPECIFY_FOLDER
    		= new Message(ConvertingWizardPage.class, "ntDlgSpecifyFolder"); //$NON-NLS-1$

	/** uwèµ½tH_ÈºÉÏ·³ê½t@CªÛ¶³êÜ·Bv */
    static private final Message MSG_DLG_SPECIFY_FOLDER
    		= new Message(ConvertingWizardPage.class, "msgDlgSpecifyFolder"); //$NON-NLS-1$
    
	/** ut^·é®«v */
    static private final Message LB_APPEND_TYPES
    		= new Message(ConvertingWizardPage.class, "lbAppendTypes"); //$NON-NLS-1$

	/** uÏ·Ýèv */
    static private final Message LB_CONFIG
    		= new Message(ConvertingWizardPage.class, "lbConfig"); //$NON-NLS-1$
    
	/** uÏ·(&C)v */
    static private final Message CLB_CONVERT
    		= new Message(ConvertingWizardPage.class, "clbConvert"); //$NON-NLS-1$

	/** uLmV^ÈOÌÝèðSÄí·éÌÅA»êÈOÌ®«ÍÇÁÅ«Ü¹ñBv */
    static private final Message MSG_CANT_APPEND_TYPES_BY_CONFIG
    		= new Message(ConvertingWizardPage.class, "msgCantAppendTypesByConfig"); //$NON-NLS-1$

	/** uÏ·ãt@CðÛ¶·é[gtH_[ðwèµÄ­¾³¢Bv */
    static private final Message MSG_SPECIFY_DES_FOLDER
    		= new Message(ConvertingWizardPage.class, "msgSpecifyDesFolder"); //$NON-NLS-1$

	/** uÊíÌtH_[ðwèµÄ­¾³¢Bv */
    static private final Message MSG_SPECIFY_NORMAL_FOLDER
    		= new Message(ConvertingWizardPage.class, "msgSpecifyNormalFolder"); //$NON-NLS-1$
    
    /** uóÔv */
    static private final Message LB_TABLE_STATUS
    		= new Message(ConvertingWizardPage.class, "lbTableStatus"); //$NON-NLS-1$
    
    /** u^Cvv*/
    static private final Message LB_TABLE_TYPE
			= new Message(ConvertingWizardPage.class, "lbTableType"); //$NON-NLS-1$
    
    /** ut@C¼v*/
    static private final Message LB_TABLE_FILE_NAME
			= new Message(ConvertingWizardPage.class, "lbTableFileName"); //$NON-NLS-1$

    /** u}Ôv*/
    static private final Message LB_TABLE_INDEX
			= new Message(ConvertingWizardPage.class, "lbTableIndex"); //$NON-NLS-1$

	/** 
	 * àG[ÉæèÏ·ªrI¹µÜµ½B
	 * ¡ãÌüPÌ½ßAOt@CðJ­ÒÉÁÄ¢½¾¯éÆK¢Å·B
	 */
    static private final Message MSG_ABORT_WIZARD
    		= new Message(ConvertingWizardPage.class, "msgAbortWizard"); //$NON-NLS-1$

    /** uÏ·ªI¹µÜµ½B\n®¹{^ð·ÆEBU[hÍI¹µÜ·Bv */
    static private final Message MSG_FINISH_WIZARD
    		= new Message(ConvertingWizardPage.class, "msgFinishWizard"); //$NON-NLS-1$

	/** uÎÛtH_ÆÛ¶ætH_ÍÊKwðwèµÄ­¾³¢Bv */
    static private final Message MSG_SPECIFY_ANOTHER_HIERARCHY
    		= new Message(ConvertingWizardPage.class, "msgSpecifyAnotherHierarchy"); //$NON-NLS-1$
    
	/** uÏ·{^ð·ÆÏ·ðJnµÜ·Bv */
    static private final Message MSG_START_CONVERTING
    		= new Message(ConvertingWizardPage.class, "msgStartConverting"); //$NON-NLS-1$

    /**
	 * Ï·ÎÛÆÈét@CÌ[gtH_[Å·B
	 */
	private File srcRoot_;
	
	/**
	 * Ï·ÎÛÆÈéR[hÌêÅ·B
	 */
	private  List<RootGameTreeRecord> srcRecords_;
	
	/**
	 * 
	 */
	private Label lbSourceRoot_;
	
	/**
	 * Ï·ÎÛÆÈéR[hÌêe[uÅ·B 
	 */
	private final UTable<RootGameTreeRecord> table_;

	/**
	 * Ï·µ½t@CðÛ¶·é[gtH_[ðwè·éplÅ·B 
	 */
	private final FolderPanel folderPanel_;
	
	/**
	 * Ï·ðJn·é{^Å·B 
	 */
	private Button btStart_;
	
	
	/**
	 * Ï·Ìi»ð\¦·éplÅ·B
	 */
	private final ProgressPanel progressPanel_;
	
	
	
	
	/**
	 * Ï·ÌZbeBOÌ{^ðÛ·é}bvÅ·B
	 */
	private final Map<TaskConfig, Button> configButtonMap_;
	
	/**
	 * 
	 */
	public ConvertingWizardPage() {
		super("convertingWizardPage"); //$NON-NLS-1$
		srcRoot_ = Ios.getCurrentDirectory();
		lbSourceRoot_ = null;
		table_ = new UTable<RootGameTreeRecord>(
				new LabelProvider(), SWT.FULL_SELECTION);
		folderPanel_ = new FolderPanel();
		folderPanel_.setLabelText(LB_DES_FOLDER.get());
		File folder = Yukinoshita.context(null).getAttribute(DES_FOLDER);
		folderPanel_.setup(NT_DLG_SPECIFY_FOLDER.get(), 
							MSG_DLG_SPECIFY_FOLDER.get(), 
							folder);
		progressPanel_ = new ProgressPanel();
		configButtonMap_ = new HashMap<TaskConfig, Button>();
		setPageComplete(false);
	}

	/* 
	 * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
	 */
	public void createControl(Composite parent) {
		Composite frame = new Composite(parent, SWT.NONE);
		frame.setLayout(SwtFactory.createThinInnerGrid(1, false));

		lbSourceRoot_ = new Label(frame, SWT.LEFT);
		lbSourceRoot_.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		
		table_.createContents(frame);
		Table table = table_.getControl();
		table.setLinesVisible(true);
		table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		Composite subFrame = new Composite(frame, SWT.NONE);
		subFrame.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		subFrame.setLayout(SwtFactory.createThinGrid(2, false));
		
		Group group = SwtFactory.createGroup(subFrame, SWT.NONE);
		group.setLayout(new FillLayout(SWT.VERTICAL));
		group.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
		group.setText(LB_APPEND_TYPES.get());
		
		SelectionListener selectionListener = new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				Button btSetting = (Button)e.widget;
				TaskConfig newSetting = (TaskConfig)btSetting.getData();
				updateSetting(newSetting, btSetting.getSelection());
			}
		};

		for (TaskConfig config : EnumSet.allOf(TaskConfig.class)) {
			ProblemType problemType = config.getProblemType();
			if (problemType == null) {
				continue;
			}
			Button button = new Button(group, SWT.CHECK);
			button.setText(problemType.toString());
			button.setData(config);
			button.setSelection(config.isDefault());
			button.addSelectionListener(selectionListener);
			configButtonMap_.put(config, button);
		}
		
		
		group = SwtFactory.createGroup(subFrame, SWT.NONE);
		group.setLayout(new FillLayout(SWT.VERTICAL));
		group.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
		group.setText(LB_CONFIG.get());

		for (TaskConfig config : EnumSet.allOf(TaskConfig.class)) {
			ProblemType problemType = config.getProblemType();
			if (problemType != null) {
				continue;
			}
			Button button = new Button(group, SWT.CHECK);
			button.setText(config.toString());
			button.setData(config);
			button.setSelection(config.isDefault());
			button.addSelectionListener(selectionListener);
			configButtonMap_.put(config, button);
		}

		Control panel = folderPanel_.createContents(frame);
		panel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		folderPanel_.getText().setEditable(false);
		folderPanel_.getText().addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				checkPageComplete();
			}
		});
		
		Composite pFrame = createProgressFrame(frame);
		pFrame.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		
		updateSetting(null, false);
		setControl(frame);
		setMessage(MSG_SPECIFY_DES_FOLDER.get());
	}

	private Composite createProgressFrame(Composite parent) {
		Composite frame = new Composite(parent, SWT.NONE);
		frame.setLayout(SwtFactory.createThinInnerGrid(2, false));
		
		Composite panel = progressPanel_.createContents(frame);
		panel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		btStart_ = new Button(frame, SWT.PUSH);
		btStart_.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, false, false));
		btStart_.setEnabled(false);
		btStart_.setText(CLB_CONVERT.get());
		btStart_.addSelectionListener(new SelectionAdapter() {
			/* 
			 * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
			 */
			@Override
			public void widgetSelected(SelectionEvent e) {
				startUpdate();
			}
			
		});
		
		return frame;
	}
	
	/**
	 * 
	 */
	private void startUpdate() {
		//TODO SÄÌ{^ðdisableÉ·éB
		btStart_.setEnabled(false);
		EnumSet<TaskConfig> configs = EnumSet.noneOf(TaskConfig.class);
		for (TaskConfig config : EnumSet.allOf(TaskConfig.class)) {
			if (configButtonMap_.get(config).getSelection()) {
				configs.add(config);
			}
		}
		TaskRunner runner = new TaskRunner(
				srcRoot_,
				folderPanel_.getFolder(),
				table_.getRecords(), configs, new TaskListenerImpl());
		new Thread(runner).start();
	}
	
	private class TaskListenerImpl implements SimpleTaskListener {

		/* 
		 * @see org.unitarou.util.TaskListener#setStatus(java.lang.String, int)
		 */
		public void setStatus(String label, int position) {
			progressPanel_.setStatus(label, position);
			getControl().getDisplay().syncExec(new Runnable() {
				public void run() {
					table_.update();
				}
			});
		}

		/* 
		 * @see org.unitarou.util.TaskListener#abort(java.lang.Exception)
		 */
		public void abort(Exception e) {
			log_s_.log(LogLevel.ERROR, e, "Error occurred in TaskRunner."); //$NON-NLS-1$
			getControl().getDisplay().syncExec(new Runnable() {
				public void run() {
					setMessage(MSG_ABORT_WIZARD.get(), IMessageProvider.ERROR);
					table_.update();
				}
			});
		}

		/* 
		 * @see org.unitarou.util.TaskListener#finished()
		 */
		public void finished() {
			getControl().getDisplay().syncExec(new Runnable() {
				public void run() {
					setMessage(MSG_FINISH_WIZARD.get(), IMessageProvider.INFORMATION);
					setPageComplete(true);
					table_.update();
				}
			});
		}

	}
	
	/**
	 * Ï·ÎÛÌR[hðwèµÜ·B
	 * @param srcRoot 
	 * @param records
	 */
	public void setRecords(File srcRoot, List<RootGameTreeRecord> records) {
		ArgumentChecker.throwIfNull(srcRoot, records);
		srcRoot_ = srcRoot;
		lbSourceRoot_.setText(LB_SRC_FOLDER.get(srcRoot_.getAbsolutePath()));
		progressPanel_.setStatus(Strings.EMPTY, 0);
		
		srcRecords_ = records;
		copySourceRecords();
		checkPageComplete();
	}
	
	/**
	 * {@link #srcRecords_}ÌgðRs[µÄ
	 * {@link #table_}ÉZbgµÜ·B 
	 */
	private void copySourceRecords() {
		List<RootGameTreeRecord> newRecords 
				= new ArrayList<RootGameTreeRecord>(srcRecords_.size());
		for (RootGameTreeRecord record : srcRecords_) {
			newRecords.add(new RootGameTreeRecord(record));
		}
		table_.clear();
		table_.addAll(newRecords);
	}
	
	/**
	 * Ï·ÝèªXV³ê½ÉA
	 * Î·éâèíÊÌ{^ðÏXµÜ·B
	 * @param newSetting NULLð·B
	 * @param selected 
	 */
	private void updateSetting(TaskConfig newSetting, boolean selected) {
		
		EnumSet<TaskConfig> set = EnumSet.noneOf(TaskConfig.class);
		for (TaskConfig setting : EnumSet.allOf(TaskConfig.class)) {
			Button button = configButtonMap_.get(setting);
			if (button.getSelection()) {
				set.add(setting);
			}
		}
		
		
		EnumSet<TaskConfig> canonicalized = TaskConfig.canonicalize(set);
		EnumSet<TaskConfig> enabled = TaskConfig.enableSet(canonicalized);
		for (TaskConfig setting : EnumSet.allOf(TaskConfig.class)) {
			Button button = configButtonMap_.get(setting);
			button.setSelection(canonicalized.contains(setting));
			button.setEnabled(enabled.contains(setting));
		}

		if (canonicalized.equals(set)) {
			checkPageComplete();
			return;
		}
		
		if (selected && TaskConfig.REMOVE_OTHER_TYPES.equals(newSetting)) {
			setMessage(
					MSG_CANT_APPEND_TYPES_BY_CONFIG.get(), 
					IMessageProvider.INFORMATION);
		}
	}
	
	/**
	 * 
	 */
	private void checkPageComplete() {
		CurrentContext context = Yukinoshita.context(null);
		context.setAttribute(DES_FOLDER, folderPanel_.getStartFolder());
		
		File folder = folderPanel_.getFolder();
		if (folder == null) { // Û¶ætH_¢wè
			setMessage(MSG_SPECIFY_DES_FOLDER.get());
			setErrorMessage(null);
			btStart_.setEnabled(false);
			setPageComplete(false);
			return;
		}
	
		if (!Ios.isNormalDirectory(folder)) {
			setErrorMessage(MSG_SPECIFY_NORMAL_FOLDER.get());
			btStart_.setEnabled(false);
			setPageComplete(false);
			return;
		}
		
		// ÇÝoµ³ÆÛ¶æÉeqÖWª éê
		if (Ios.isChild(srcRoot_, folder) || Ios.isChild(folder, srcRoot_))	{
			setErrorMessage(MSG_SPECIFY_ANOTHER_HIERARCHY.get());
			btStart_.setEnabled(false);
			setPageComplete(false);
			return;
		}
		
		// Ï·Â\B
		if (!btStart_.getEnabled()) {
			copySourceRecords();
			setMessage(MSG_START_CONVERTING.get());
			setErrorMessage(null);
			btStart_.setEnabled(true);
			setPageComplete(false);
		}
	}		
	
	/**
	 * {@link ConvertingWizardPage#table_}pÌvoC_[Å·B
	 */
	private class LabelProvider implements UTableProvider<RootGameTreeRecord> {

		/*
		 * @see org.unitarou.swt.UTableProvider#getColumnSize()
		 */
		public int getColumnSize() {
			return 4;
		}

		/*
		 * @see org.unitarou.swt.UTableProvider#getSotableColums()
		 */
		public boolean[] getSotableColums() {
			return new boolean[]{true, true, true, false};
		}

		/*
		 * @see org.unitarou.swt.UTableProvider#compareTo(java.lang.Object,
		 *      java.lang.Object, int)
		 */
		public int compareTo(RootGameTreeRecord obj1, RootGameTreeRecord obj2, int index) {
			switch (index) {
			case 0:
				return obj1.getStatus().compareTo(obj2.getStatus());
				
			case 1:
				EnumSet<ProblemType> t1 = obj1.getProblemTypes(), 
									 t2 = obj2.getProblemTypes();
				int s1 = t1.size();
				int s2 = t2.size();
				if ((s1 == 0 && s2 == 0) || (s1 != s2)) {
					return s1 - s2;
				}
				for (Iterator<ProblemType> ip1 = t1.iterator(), 
					                    	ip2 = t2.iterator();
				 							ip1.hasNext();)
				{
					ProblemType p1 = ip1.next(),
					p2 = ip2.next();
					int c = p1.compareTo(p2);
					if (c != 0) {
						return c;
					}
				}
				return 0;

			case 2:
				File file1 = obj1.getFileSnapshot().file();
				File file2 = obj2.getFileSnapshot().file();
				return file1.compareTo(file2);

				
			default:	
				return 0;
			}
		}



		/*
		 * @see org.unitarou.swt.UTableProvider#set(org.eclipse.swt.widgets.TableItem,
		 *      java.lang.Object)
		 */
		public void set(TableItem tableItem, RootGameTreeRecord target) {
			tableItem.setText(0, String.valueOf(target.getStatus()));
			tableItem.setText(1, target.getProblemTypes().toString());

			String rPath = Ios.calcRelativePath(
					srcRoot_.getAbsolutePath(), 
					target.getFileSnapshot().file().getAbsolutePath());
			tableItem.setText(2, rPath);
			
			tableItem.setText(3, String.valueOf(target.getRgtIndex() + 1));
		}

		/*
		 * @see org.unitarou.swt.UTableProvider#setup(org.eclipse.swt.widgets.TableColumn[])
		 */
		public void setup(TableColumn[] tableColumns) {
			tableColumns[0].setText(LB_TABLE_STATUS.get());
			tableColumns[0].setMoveable(true);
			tableColumns[1].setText(LB_TABLE_TYPE.get());
			tableColumns[1].setMoveable(true);
			tableColumns[2].setText(LB_TABLE_FILE_NAME.get());
			tableColumns[2].setMoveable(true);
			tableColumns[3].setText(LB_TABLE_INDEX.get());
			tableColumns[3].setMoveable(true);
		}
	}
}
