/**
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 *
 * Copyright (C)
 * <a href="http://www.inab.org">Spanish National Institute of Bioinformatics (INB)</a>
 * <a href="http://www.bsc.es">Barcelona Supercomputing Center (BSC)</a>
 * <a href="http://inb.bsc.es">Computational Node 6</a>
 */

package org.inb.biomoby.central.gui;

import java.awt.Color;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.event.ChangeEvent;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.TableColumn;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.inb.biomoby.central.gui.model.MobyObjectPropertiesTableModel;
import org.inb.biomoby.central.gui.renderer.PropertyNameTableCellEditor;
import org.inb.biomoby.central.model.AbstractModel;
import org.inb.biomoby.shared.registry.ObjectType;
import org.inb.biomoby.shared.registry.Relationship;

/**
 * Abstract BioMoby properties table implementation based on a model with concrete BioMoby ObjectType.
 * 
 * @author Dmitry Repchevsky
 */

public abstract class AbstractMobyObjectPropertyTable extends AbstractMobyPropertyTable
{
    public AbstractMobyObjectPropertyTable()
    {
        TableColumn propertyNameColumn = columnModel.getColumn(1);
        PropertyNameTableCellEditor editor = (PropertyNameTableCellEditor)propertyNameColumn.getCellEditor();

        editor.getComponent().getDocument().addDocumentListener(new DefaultPropertyNameValidator(editor));
    }

    @Override
    protected abstract MobyObjectPropertiesTableModel createDefaultDataModel();

    @Override
    public MobyObjectPropertiesTableModel getModel()
    {
        return (MobyObjectPropertiesTableModel)super.getModel();
    }

    @Override
    public void editingStopped(ChangeEvent e)
    {
        if (editingColumn == 1)
        {
            PropertyNameTableCellEditor editor = (PropertyNameTableCellEditor)getCellEditor();

            if (editor != null)
            {
                String newPropertyName = editor.getComponent().getText();

                if (newPropertyName.isEmpty())
                {
                    editor.cancelCellEditing();
                }
                else if (isValid(newPropertyName))
                {
                    ObjectType oldObjectType = (ObjectType)editor.getCellEditorValue();
                    ObjectType newObjectType = oldObjectType.clone();
                    newObjectType.setArticleName(newPropertyName);

                    MobyObjectPropertiesTableModel tModel = getModel();
                    tModel.setValueAt(newObjectType, editingRow, 1);
                    removeEditor();
                }
                else
                {
                    editor.cancelCellEditing();
                }
            }
        }
        else
        {
            super.editingStopped(e);
        }
    }

    private boolean isValid(String propertyName)
    {
        MobyObjectPropertiesTableModel tModel = getModel();
        ObjectType clazz = tModel.getClazz();

        // check for duplication names (including parents' properties)
        for (int i = 0, n = tModel.getRowCount(); i < n; i++)
        {
            ObjectType objectType = (ObjectType)tModel.getValueAt(i, 1);
            if (i != editingRow &&
                propertyName.equals(objectType.getArticleName()))
            {
                return false;
            }
        }

        if (clazz != null)
        { // search for property name duplications in children
            AbstractModel<ObjectType> model = tModel.getModel();

            Set<ObjectType> descendants = new TreeSet<ObjectType>();
            findAllDescendants(model, descendants, clazz);

            for (ObjectType child : descendants)
            {
                if (!clazz.equals(child))
                {
                    List<Relationship<ObjectType>> relationships = child.getRelationships();
                    for (Relationship<ObjectType> relationship : relationships)
                    {
                        Relationship.RELATIONSHIP_TYPE relationshipType = relationship.getRelationshipType();
                        if (relationshipType == Relationship.RELATIONSHIP_TYPE.HAS ||
                            relationshipType == Relationship.RELATIONSHIP_TYPE.HASA)
                        {
                            List<ObjectType> properties = relationship.getEntityTypeList();

                            for (ObjectType property : properties)
                            {
                                if (propertyName.equals(property.getArticleName()))
                                {
                                    return false;
                                }
                            }
                        }
                    }
                }
            }
        }

        return true;
    }

    private void findAllDescendants(AbstractModel<ObjectType> model, Set<ObjectType> descendants, ObjectType objectType)
    {
        descendants.add(objectType);
        
        Collection<ObjectType> datatypes = model.getElements();

        for (ObjectType datatype : datatypes)
        {
            Relationship<ObjectType> relationship = datatype.getRelationship(Relationship.RELATIONSHIP_TYPE.ISA);

            if (relationship != null)
            {
                List<ObjectType> parents = relationship.getEntityTypeList();

                for (ObjectType parent : parents)
                {
                    if (parent.equals(objectType))

                    findAllDescendants(model, descendants, datatype);
                }
            }
        }
    }

    public class DefaultPropertyNameValidator implements DocumentListener
    {
        private Color ERROR_COLOR = new Color(0xFFAAAA);

        private PropertyNameTableCellEditor editor;
        private Color background;

        public DefaultPropertyNameValidator(PropertyNameTableCellEditor editor)
        {
            this.editor = editor;
            this.background = editor.getComponent().getBackground();
        }

        @Override
        public void insertUpdate(DocumentEvent e)
        {
            validate(e.getDocument());
        }

        @Override
        public void removeUpdate(DocumentEvent e)
        {
            validate(e.getDocument());
        }

        @Override
        public void changedUpdate(DocumentEvent e)
        {
            validate(e.getDocument());
        }

        private void validate(Document document)
        {
            try
            {
                String articleName = document.getText(0, document.getLength());

                ObjectType objectType = editor.getCellEditorValue();

                editor.getComponent().setBackground(articleName.equals(objectType.getArticleName()) || isValid(articleName) ? background : ERROR_COLOR);
            }
            catch(BadLocationException ex) {}
        }
    }
}
