/*
 * 作成日: 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();
                    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);

        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);
    }

    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() {
        if (entityText != null && "".equals(entityText.getText()))
            pressButton();
    }
}