/**
 * 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.model;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.swing.table.AbstractTableModel;
import org.inb.biomoby.shared.registry.ObjectType;
import org.inb.biomoby.shared.registry.Relationship;
import org.inb.biomoby.shared.registry.Relationship.RELATIONSHIP_TYPE;

/**
 * @author Dmitry Repchevsky
 */

public class MobyPropertiesTableModel extends AbstractTableModel
{
    private List<Relationship<ObjectType>> properties;

    public MobyPropertiesTableModel()
    {
        properties = new ArrayList<Relationship<ObjectType>>();
    }

    @Override
    public final int getColumnCount()
    {
        return 3;
    }

    @Override
    public final synchronized int getRowCount()
    {
        return properties.size();
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex)
    {
        return rowIndex >= 0;
    }

    @Override
    public final java.lang.Class getColumnClass(int column)
    {
        switch(column)
        {
            case 2: return Relationship.RELATIONSHIP_TYPE.class;
        }

        return ObjectType.class;
    }

    @Override
    public final synchronized Object getValueAt(int rowIndex, int columnIndex)
    {
        // here we can have only one relationship per property (has/hasa)
        Relationship<ObjectType> property = properties.get(rowIndex);

        switch(columnIndex)
        {
            case 0:
            case 1: return property.getEntityTypeList().get(0);
            case 2: return property.getRelationshipType();
        }

        return null;
    }

    @Override
    public synchronized void setValueAt(Object value, int rowIndex, int columnIndex)
    {
        Relationship<ObjectType> property = properties.get(rowIndex);
        
        switch(columnIndex)
        {
            case 0:
            {
                ObjectType newObjectType = (ObjectType)value;
                ObjectType oldObjectType = property.getEntityTypeList().get(0);

                oldObjectType.setName(newObjectType.getName());
                oldObjectType.setAuthURI(newObjectType.getAuthURI());
                oldObjectType.setTitle(newObjectType.getTitle());
                oldObjectType.setDescription(newObjectType.getDescription());
                oldObjectType.setContactEmail(newObjectType.getContactEmail());
                oldObjectType.setLsid(newObjectType.getLsid());
                break;
            }
            case 1:
            {
                ObjectType newObjectType = (ObjectType)value;
                ObjectType oldObjectType = property.getEntityTypeList().get(0);

                oldObjectType.setArticleName(newObjectType.getArticleName());
                break;
            }
            case 2:
            {
                property.setRelationshipType((RELATIONSHIP_TYPE)value);
            }
        }

        fireTableDataChanged(); // refresh whole table
    }

    public synchronized Relationship<ObjectType> get(int row)
    {
        return properties.get(row);
    }

    public synchronized void clear()
    {
        properties.clear();
        fireTableDataChanged();
    }

    public synchronized Relationship<ObjectType> addProperty(ObjectType property)
    {
        return addProperty(property, RELATIONSHIP_TYPE.HASA);
    }

    public synchronized void addProperties(ObjectType clazz)
    {
        // use tree to sort properties by its name (articleName) and not a type name (default)
        TreeMap<ObjectType, Relationship.RELATIONSHIP_TYPE> map = new TreeMap<ObjectType, Relationship.RELATIONSHIP_TYPE>(new Comparator<ObjectType>()
        {
            public int compare(ObjectType o1, ObjectType o2)
            {
                return o1.getArticleName().compareTo(o2.getArticleName());
            }
        });

        List<Relationship<ObjectType>> relationships = clazz.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> objectTypes = relationship.getEntityTypeList();

                for (ObjectType property : objectTypes)
                {
                    map.put(property, relationshipType);
                }
            }
        }

        for(Map.Entry<ObjectType, Relationship.RELATIONSHIP_TYPE> entry : map.entrySet())
        {
            addProperty(entry.getKey(), entry.getValue());
        }
    }

    public synchronized Relationship<ObjectType> remove(int row)
    {
        Relationship<ObjectType> relationship = properties.remove(row);

        fireTableRowsDeleted(row, row);

        return relationship;
    }

    public synchronized boolean isValid()
    {
        Set<String> names = new TreeSet<String>();

        for (Relationship<ObjectType> relationship : properties)
        {
            String name = relationship.getEntityTypeList().get(0).getArticleName();

            if (name == null || name.isEmpty() || !names.add(name))
            {
                return false;
            }
        }

        return true;
    }

    private Relationship<ObjectType> addProperty(ObjectType property, Relationship.RELATIONSHIP_TYPE relationshipType)
    {
        Relationship<ObjectType> relationship = new Relationship<ObjectType>();
        relationship.setRelationshipType(relationshipType);
        relationship.addEntityType(property);

        final int row = properties.size();

        properties.add(relationship);

        fireTableRowsInserted(row, row);

        return relationship;
    }
}