/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 ****************************************************************/
package org.apache.cayenne.modeler.editor;

import java.awt.BorderLayout;
import java.util.Collection;
import java.util.Iterator;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JToolBar;

import org.apache.cayenne.configuration.DataChannelDescriptor;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.map.Embeddable;
import org.apache.cayenne.map.ObjAttribute;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.event.AttributeEvent;
import org.apache.cayenne.map.event.EmbeddableEvent;
import org.apache.cayenne.modeler.Application;
import org.apache.cayenne.modeler.ProjectController;
import org.apache.cayenne.modeler.action.ActionManager;
import org.apache.cayenne.modeler.action.CreateAttributeAction;
import org.apache.cayenne.modeler.event.EmbeddableDisplayEvent;
import org.apache.cayenne.modeler.event.EmbeddableDisplayListener;
import org.apache.cayenne.modeler.util.Comparators;
import org.apache.cayenne.modeler.util.TextAdapter;
import org.apache.cayenne.project.extension.info.ObjectInfo;
import org.apache.cayenne.util.Util;
import org.apache.cayenne.validation.ValidationException;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.FormLayout;

public class EmbeddableTab extends JPanel implements EmbeddableDisplayListener {

    protected ProjectController mediator;
    protected TextAdapter className;
    protected TextAdapter comment;

    public EmbeddableTab(ProjectController mediator) {
        this.mediator = mediator;
        initView();
        initController();
    }

    private void initController() {
        mediator.addEmbeddableDisplayListener(this);
    }

    private void initView() {
        this.setLayout(new BorderLayout());

        JToolBar toolBar = new JToolBar();
        toolBar.setBorder(BorderFactory.createEmptyBorder());
        toolBar.setFloatable(false);
        ActionManager actionManager = Application.getInstance().getActionManager();
        toolBar.add(actionManager.getAction(CreateAttributeAction.class).buildButton());

        add(toolBar, BorderLayout.NORTH);

        className = new TextAdapter(new JTextField()) {
            @Override
            protected void updateModel(String text) {
                setClassName(text);
            }
        };

        comment = new TextAdapter(new JTextField()) {
            @Override
            protected void updateModel(String text) {
                setComment(text);
            }
        };

        FormLayout layout = new FormLayout(
                "right:50dlu, 3dlu, fill:150dlu, 3dlu, fill:100",
                "");
        DefaultFormBuilder builder = new DefaultFormBuilder(layout);
        builder.setDefaultDialogBorder();
        builder.append("Class Name:", className.getComponent(), 3);
        builder.append("Comment:", comment.getComponent(), 3);

        add(builder.getPanel(), BorderLayout.CENTER);
    }

    void setClassName(String newClassName) {
        if (newClassName != null && newClassName.trim().length() == 0) {
            newClassName = null;
        }

        Embeddable embeddable = mediator.getCurrentEmbeddable();

        if (embeddable == null) {
            return;
        }

        if (Util.nullSafeEquals(newClassName, embeddable.getClassName())) {
            return;
        }

        if (newClassName == null) {
            throw new ValidationException("Embeddable name is required.");
        }
        else if (embeddable.getDataMap().getEmbeddable(newClassName) == null) {
            
            // if newClassName dupliucates in other DataMaps 
            DataChannelDescriptor domain = (DataChannelDescriptor) mediator.getProject().getRootNode();
            if (domain != null) {
                for (DataMap nextMap : domain.getDataMaps()) {
                    if (nextMap == embeddable.getDataMap()) {
                        continue;
                    }

                    Embeddable conflictingEmbeddable = nextMap.getEmbeddable(newClassName);
                    if (conflictingEmbeddable != null) {
                        throw new ValidationException(
                                    "Duplicate Embeddable name in another DataMap: "
                                            + newClassName
                                            + ".");
                    }
                }
            }
            
            // completely new name, set new name for embeddable
            EmbeddableEvent e = new EmbeddableEvent(this, embeddable, embeddable
                    .getClassName());
            String oldName = embeddable.getClassName();
            embeddable.setClassName(newClassName);

            mediator.fireEmbeddableEvent(e, mediator.getCurrentDataMap());

            Iterator it = ((DataChannelDescriptor) mediator.getProject().getRootNode()).getDataMaps().iterator();
            while (it.hasNext()) {
                DataMap dataMap = (DataMap) it.next();
                Iterator<ObjEntity> ent = dataMap.getObjEntities().stream()
                        .sorted(Comparators.getDataMapChildrenComparator())
                        .iterator();

                while (ent.hasNext()) {
                    
                    Collection<ObjAttribute> attr = ent.next().getAttributes();
                    Iterator<ObjAttribute> attrIt = attr.iterator();
                    
                    while (attrIt.hasNext()) {
                        ObjAttribute atribute = attrIt.next();
                        if (atribute.getType()==null || atribute.getType().equals(oldName)) {
                            atribute.setType(newClassName);
                            AttributeEvent ev = new AttributeEvent(this, atribute, atribute
                                    .getEntity());
                            mediator.fireObjAttributeEvent(ev);
                        }
                    }
                    
                }
            }

        }
        else {
            // there is an embeddable with the same name
            throw new ValidationException("There is another embeddable with name '"
                    + newClassName
                    + "'.");
        }

    }

    public void currentEmbeddableChanged(EmbeddableDisplayEvent e) {
        Embeddable embeddable = e.getEmbeddable();
        if (embeddable == null || !e.isEmbeddableChanged()) {
            return;
        }
        initFromModel(embeddable);
    }

    private void initFromModel(Embeddable embeddable) {
        className.setText(embeddable.getClassName());
        comment.setText(getComment(embeddable));
    }

    void setComment(String comment) {
        Embeddable embeddable = mediator.getCurrentEmbeddable();

        if (embeddable == null) {
            return;
        }

        ObjectInfo.putToMetaData(mediator.getApplication().getMetaData(), embeddable, ObjectInfo.COMMENT, comment);
        mediator.fireEmbeddableEvent(new EmbeddableEvent(this, embeddable), mediator.getCurrentDataMap());
    }

    String getComment(Embeddable embeddable) {
        return ObjectInfo.getFromMetaData(mediator.getApplication().getMetaData(), embeddable, ObjectInfo.COMMENT);
    }
}
