/*
 * 作成日: 2008/08/05
 * 著作権: Copyright (c) 2005 ZIGEN
 * ライセンス：Eclipse Public License - v 1.0
 * 原文：http://www.eclipse.org/legal/epl-v10.html
 */
package zigen.plugin.db.ext.s2jdbc.wizards;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.jface.viewers.IStructuredSelection;
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.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PartInitException;

import zigen.plugin.db.DbPlugin;
import zigen.plugin.db.DbPluginConstant;
import zigen.plugin.db.core.IDBConfig;
import zigen.plugin.db.core.TableColumn;
import zigen.plugin.db.core.TablePKColumn;
import zigen.plugin.db.core.Transaction;
import zigen.plugin.db.ext.s2jdbc.Activator;
import zigen.plugin.db.ext.s2jdbc.entity.OneToManySearcher;
import zigen.plugin.db.ext.s2jdbc.entity.OneToOneSearcher;
import zigen.plugin.db.ext.s2jdbc.entity.rule.DefaultEntityMappingFactory;
import zigen.plugin.db.ext.s2jdbc.entity.rule.IEntityMappingFactory;
import zigen.plugin.db.ext.s2jdbc.util.CodeCreatorUtil;
import zigen.plugin.db.ext.s2jdbc.util.CommentUtil;
import zigen.plugin.db.ext.s2jdbc.util.PropertyNameUtil;
import zigen.plugin.db.ui.internal.Column;
import zigen.plugin.db.ui.internal.DataBase;
import zigen.plugin.db.ui.internal.ITable;
import zigen.plugin.db.ui.internal.TreeLeaf;
import zigen.plugin.db.ui.jobs.ConnectDBJob;
import zigen.plugin.db.ui.util.ResourceUtil;
import zigen.plugin.db.ui.views.ColumnSearchAction;
import zigen.plugin.db.ui.views.TreeContentProvider;
import zigen.plugin.db.ui.views.TreeView;

public class NewEntityWizardPage extends AbstractWizardPage {
	
	protected static final String LS = System.getProperty("line.separator"); //$NON-NLS-1$
	
	protected IStructuredSelection selection;
	
	protected String preSuperClassName;
	
	protected Text entityText;
	
	protected TreeView treeView;
	
	protected ITable entity;
	
	protected TreeLeaf[] leafs;
	
	protected IEntityMappingFactory mapping;
	
	protected String accessModifiers = "public"; // 初期値 public //$NON-NLS-1$
	
	protected Button button;
	
	public NewEntityWizardPage(IStructuredSelection selection) {
		super(selection, Messages.getString("NewEntityWizardPage.0")); //$NON-NLS-1$
	}
	
	@Override
	public void addPreTypeNameControl(Composite composite, int columns) {
		Label label = new Label(composite, SWT.NONE);
		label.setText(Messages.getString("NewEntityWizardPage.1")); //$NON-NLS-1$
		entityText = new Text(composite, SWT.BORDER);
		entityText.setEnabled(false);
		entityText.addModifyListener(new ModifyListener() {
			
			public void modifyText(ModifyEvent e) {
				modified();
			}
		});
		
		GridData gd = new GridData(GridData.FILL_HORIZONTAL);
		gd.horizontalSpan = 2;
		entityText.setLayoutData(gd);
		button = new Button(composite, SWT.NONE);
		button.setText(Messages.getString("NewEntityWizardPage.2")); //$NON-NLS-1$
		button.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		button.addSelectionListener(new SelectionAdapter() {
			
			public void widgetSelected(SelectionEvent e) {
				pressButton();
			}
			
		});
	}
	
	
	private void pressButton() {
		IJavaProject iprj = getPackageFragmentRoot().getJavaProject();
		IProject prj = iprj.getProject();
		IDBConfig config = ResourceUtil.getDBConfig(prj);
		if (config != null) {
			try {
				treeView = (TreeView) DbPlugin.showView(DbPluginConstant.VIEW_ID_TreeView);
				TreeContentProvider tcp = treeView.getContentProvider();
				DataBase db = tcp.findDataBase(config);
				if (!db.isConnected()) {
					ConnectDBJob job = new ConnectDBJob(treeView.getTreeViewer(), db);
					job.setPriority(ConnectDBJob.SHORT);
					job.setUser(false);
					job.setSystem(false);
					job.schedule();
					// ConnectDBJobが終了するまで待機
					try {
						job.join();
					} catch (InterruptedException ex) {
						DbPlugin.log(ex);
					}
				}
				SelectTableDialog dialog = new SelectTableDialog(DbPlugin.getDefault().getShell(), db);
				int ret = dialog.open();
				if (ret == SelectTableDialog.OK) {
					entity = dialog.table;
					
					if (entity != null) {
						mapping = DefaultEntityMappingFactory.getFactory(entity.getDbConfig());
						entityText.setText(dialog.table.getName());
						setTypeName(createTypeName(dialog.table), true);
						
					} else {
						mapping = null;
						entityText.setText(""); //$NON-NLS-1$
						
					}
				}
				
			} catch (PartInitException e1) {
				Activator.getDefault().showErrorDialog(e1);
			}
			

		} else {
			Activator.getDefault().showInformationMessage(Messages.getString("NewEntityWizardPage.3")); //$NON-NLS-1$
		}
	}
	
	@Override
	public void addAfterTypeNameControl(Composite parent, int columns) {}
	
	private String createTypeName(ITable table) {
		return PropertyNameUtil.getEntityName(table.getName());
	}
	
	private IStatus modified() {
		StatusInfo status = new StatusInfo();
		if (entityText != null) {
			String str = entityText.getText().trim();
			if ("".equals(str)) { //$NON-NLS-1$
				status.setError(Messages.getString("NewEntityWizardPage.4")); //$NON-NLS-1$
			}
		}
		return status;
	}
	
	
	protected String constructCUContent(ICompilationUnit cu, String typeContent, String lineDelimiter) throws CoreException {
		// String fileComment = getFileComment(cu, lineDelimiter);
		// String typeComment = getTypeComment(cu, lineDelimiter);
		StringBuffer sb = new StringBuffer();
		sb.append("/**").append("\r\n");
		sb.append(" * ").append(entity.getLabel()).append("\r\n");
		sb.append(" *").append("\r\n");
		sb.append(" */");
		
		String typeComment = sb.toString();
		
		IPackageFragment pack = (IPackageFragment) cu.getParent();
		StringBuffer buf = new StringBuffer();
		if (!pack.isDefaultPackage())
			buf.append("package ").append(pack.getElementName()).append(';'); //$NON-NLS-1$
		buf.append(lineDelimiter).append(lineDelimiter);
		if (typeComment != null)
			buf.append(typeComment).append(lineDelimiter);
		
		// Entityアノテーションを追加
		// テーブル名は小文字にすること
		String anotation = "@Entity(name = \"" + entity.getName().toLowerCase() + "\")"; //$NON-NLS-1$ //$NON-NLS-2$
		buf.append(anotation).append(LS);
		buf.append(typeContent);
		return buf.toString();
	}
	
	protected void createTypeMembers(IType type, ImportsManager imports, IProgressMonitor monitor) throws CoreException {
		
		monitor.setTaskName(Messages.getString("NewEntityWizardPage.5")); //$NON-NLS-1$
		loadColumnInfo(entity);
		leafs = entity.getChildrens();
		monitor.setTaskName(Messages.getString("NewEntityWizardPage.6")); //$NON-NLS-1$
		
		imports.addImport("javax.persistence.Entity"); //$NON-NLS-1$
		
		// プロパティフィールドの作成
		createField(type, imports, monitor);
		
		super.createTypeMembers(type, imports, monitor);
		
		StringBuffer buf = new StringBuffer();
		do_toString(buf);
		type.createMethod(buf.toString(), null, false, null);
		
		if (monitor != null)
			monitor.done();
		
	}
	
	private void createField(IType type, ImportsManager imports, IProgressMonitor monitor) throws JavaModelException {
		StringBuffer buf = new StringBuffer();
		List superClassFields = new ArrayList();
		if (getSuperClass() != null) {
			IType sType = getPackageFragmentRoot().getJavaProject().findType(getSuperClass());
			IField[] fields = sType.getFields();
			for (int i = 0; i < fields.length; i++) {
				IField field = fields[i];
				superClassFields.add(field.getElementName());
			}
			
			String[] ifs = sType.getSuperInterfaceNames();
			for (int i = 0; i < ifs.length; i++) {
				if (ifs[i].contains("java.io.Serializable")) { //$NON-NLS-1$
					buf.append("\tprivate static final long serialVersionUID = 1L;").append(LS); //$NON-NLS-1$
					buf.append(LS);
					break;
				}
			}
		} else {
			if (getSuperInterfaces().contains("java.io.Serializable")) { //$NON-NLS-1$
				buf.append("\tprivate static final long serialVersionUID = 1L;").append(LS); //$NON-NLS-1$
				buf.append(LS);
			}
		}
		for (int i = 0; i < leafs.length; i++) {
			Column col = (Column) leafs[i];
			
			String propertyName = PropertyNameUtil.getProperty(col, true);
			if (!superClassFields.contains(propertyName)) {
				
				buf.append("\t/**").append(LS); //$NON-NLS-1$
				buf.append("\t * " + CodeCreatorUtil.getLogicalColumnLabel(col)).append(LS); //$NON-NLS-1$
				buf.append("\t */").append(LS); //$NON-NLS-1$
				
				if ("version".equalsIgnoreCase(col.getName())) { //$NON-NLS-1$
				
					buf.append("\t@").append(imports.addImport("javax.persistence.Version")).append(LS); //$NON-NLS-1$ //$NON-NLS-2$
					buf.append("\tpublic Long version = 0L;").append(LS); //$NON-NLS-1$
					
				} else {
					
					if (col.hasPrimaryKey()) {
						buf.append("\t@").append(imports.addImport("javax.persistence.Id")).append(LS); //$NON-NLS-1$ //$NON-NLS-2$
					}
					buf.append("\t@").append(imports.addImport("javax.persistence.Column")).append("(name = \"" + col.getName() + "\")").append(LS); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
					
					if (mapping.isLargeObject(col.getDataType())) {
						buf.append("\t@").append(imports.addImport("javax.persistence.Lob")).append(LS); //$NON-NLS-1$ //$NON-NLS-2$
					}
					
					String temporalType = mapping.getTemporalType(col.getDataType());
					if (temporalType != null) {
						imports.addImport("javax.persistence.TemporalType"); //$NON-NLS-1$
						buf.append("\t@").append(imports.addImport("javax.persistence.Temporal")).append("(" + temporalType + ")").append(LS);; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
					}
					
					String javaType = getJavaType(col);
					if (javaType.startsWith("java.math.") || javaType.startsWith("java.sql.")) { //$NON-NLS-1$ //$NON-NLS-2$
						javaType = imports.addImport(javaType);
					}
					buf.append("\t").append(propertyString(accessModifiers, javaType, propertyName)); //$NON-NLS-1$
					buf.append(LS);
					
				}
				buf.append(LS);
				
				// コメントに記載されているアノテーションから出力
				String remarks = col.getRemarks();
				if (remarks != null && !"".equals(remarks.trim())) { //$NON-NLS-1$
					String ano = CommentUtil.getAnnotation(remarks);
					if (ano != null) {
						if ("@ManyToOne".equalsIgnoreCase(ano)) { //$NON-NLS-1$
							String entityName = PropertyNameUtil.getEntityFromIdName(col.getName());
							
							buf.append("\t@").append(imports.addImport("javax.persistence.ManyToOne")); //$NON-NLS-1$ //$NON-NLS-2$
							buf.append(LS);
							// buf.append("\t@").append(imports.addImport("javax.persistence.JoinColumn")).append("(name = \"" + PropertyNameUtil.getPropertyName(col.getName()) + "\")");
							buf.append("\t@").append(imports.addImport("javax.persistence.JoinColumn")).append("(name = \"" + col.getName() + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
							buf.append(LS);
							buf.append("\t").append("public ").append(entityName + " " + PropertyNameUtil.getPropertyName(entityName)).append(";"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
							buf.append(LS);
							
						} else if ("@OneToOne".equalsIgnoreCase(ano)) { //$NON-NLS-1$
							String entityName = PropertyNameUtil.getEntityFromIdName(col.getName());
							
							buf.append("\t@").append(imports.addImport("javax.persistence.OneToOne")); //$NON-NLS-1$ //$NON-NLS-2$
							buf.append(LS);
							// buf.append("\t@").append(imports.addImport("javax.persistence.JoinColumn")).append("(name = \"" + PropertyNameUtil.getPropertyName(col.getName()) + "\")");
							buf.append("\t@").append(imports.addImport("javax.persistence.JoinColumn")).append("(name = \"" + col.getName() + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
							buf.append(LS);
							buf.append("\t").append("public ").append(entityName + " " + PropertyNameUtil.getPropertyName(entityName)).append(";"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
							buf.append(LS);
						} else {
							boolean b = false;
							if (ano.indexOf("@OneToOne") >= 0) {
								imports.addImport("javax.persistence.OneToOne");
								b = true;
							}
							if (ano.indexOf("@OneToMany") >= 0) {
								imports.addImport("javax.persistence.OneToMany");
								b = true;
							}
							if (ano.indexOf("@ManyToOne") >= 0) {
								imports.addImport("javax.persistence.ManyToOne");
								b = true;
							}
							if (ano.indexOf("@JoinColumn") >= 0) {
								imports.addImport("javax.persistence.JoinColumn");
								b = true;
							}
							if (b) {
								buf.append("	/**").append(LS);
								buf.append("	 * <関連アノテーション>").append(LS);
								buf.append("	 */");
								buf.append(LS).append(ano).append(LS);
							}
						}
					}
					
				}
				
				buf.append(LS);
				
			}
		}
		
		try {
			IDBConfig config = entity.getDbConfig();
			String schema = config.getSchema();
			String columnName = entity.getName().toUpperCase() + "_ID"; //$NON-NLS-1$
			Transaction trans = Transaction.getInstance(config);
			List<String> oneToManys = OneToManySearcher.execute(trans.getConnection(), schema, columnName);
			for (String tableName : oneToManys) {
				
				String myEntityName = PropertyNameUtil.getPropertyName(entity.getName()); // mKuni
				String targetEntityName = PropertyNameUtil.getEntityName(tableName); // MBu
				
				buf.append("\t@").append(imports.addImport("javax.persistence.OneToMany")); //$NON-NLS-1$ //$NON-NLS-2$
				buf.append("(mappedBy = \"" + myEntityName + "\")"); //$NON-NLS-1$ //$NON-NLS-2$
				buf.append(LS);
				
				// buf.append("\t@").append(imports.addImport("javax.persistence.JoinColumn")).append("(name = \"" + col.getName() + "\")");
				// buf.append(LS);
				buf.append("\t").append("public "); //$NON-NLS-1$ //$NON-NLS-2$
				buf.append(imports.addImport("java.util.List")); //$NON-NLS-1$
				buf.append("<"); //$NON-NLS-1$
				buf.append(targetEntityName);
				buf.append("> "); //$NON-NLS-1$
				buf.append(PropertyNameUtil.getPropertyName(tableName)).append("List;"); //$NON-NLS-1$
				buf.append(LS);
			}
			buf.append(LS);
			
			// for OneToOne
			List<String> oneToOnes = OneToOneSearcher.execute(trans.getConnection(), schema, columnName);
			for (String tableName : oneToOnes) {
				
				String myEntityName = PropertyNameUtil.getPropertyName(entity.getName()); // mKuni
				String targetEntityName = PropertyNameUtil.getEntityName(tableName); // MBu
				
				buf.append("\t@").append(imports.addImport("javax.persistence.OneToOne")); //$NON-NLS-1$ //$NON-NLS-2$
				buf.append("(mappedBy = \"" + myEntityName + "\")"); //$NON-NLS-1$ //$NON-NLS-2$
				buf.append(LS);
				buf.append("\t").append("public "); //$NON-NLS-1$ //$NON-NLS-2$
				buf.append(targetEntityName);
				buf.append(" "); //$NON-NLS-1$
				buf.append(PropertyNameUtil.getPropertyName(tableName));
				buf.append(";"); //$NON-NLS-1$
				buf.append(LS);
			}
			buf.append(LS);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		

		type.createField(buf.toString(), null, false, null);
	}
	
	protected IStatus typeNameChanged() {
		IStatus status = super.typeNameChanged();
		String typeName = getTypeName();
		if (typeName != null && !"".equals(typeName)) { //$NON-NLS-1$
			if (status.getCode() == IStatus.OK) {
				return modified();
			} else {
				return status;
			}
		}
		return status;
	}
	
	
	protected void do_toString(StringBuffer buf) {
		if (leafs == null)
			return;
		
		String[] properties = new String[leafs.length];
		for (int i = 0; i < leafs.length; i++) {
			Column col = (Column) leafs[i];
			properties[i] = PropertyNameUtil.getProperty(col, true);
		}
		buf.append(toStringString(getTypeName(), properties)).append(LS);
	}
	
	// JavaTypeの取得
	protected String getJavaType(Column col) {
		TableColumn tCol = col.getColumn();
		return mapping.getJavaType(tCol);
	}
	
	
	// カラム情報を取得
	protected void loadColumnInfo(ITable table) {
		if (!table.isExpanded()) {
			table.setExpanded(true);
			Display display = Display.getDefault();
			// カラム検索は非同期処理にしない(変更しないこと)
			display.syncExec((Runnable) new ColumnSearchAction(treeView.getTreeViewer(), table));
			
		}
	}
	
	// 主キーを持っているか
	protected boolean hasPrimaryKey() {
		if (leafs == null)
			return false;
		
		for (int i = 0; i < leafs.length; i++) {
			Column col = (Column) leafs[i];
			TablePKColumn pkColumn = col.getPkColumn();
			if (pkColumn != null) {
				return true;
			}
		}
		return false;
	}
	
	protected void setFocus() {
		// Entityが選択されていない場合は、Entity選択ダイアログを表示する
		if (entityText != null && "".equals(entityText.getText()))
			pressButton();
	}
}