/*
 *  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
 *  
 *    http://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.directory.studio.ldapbrowser.ui.editors.searchresult;


import java.util.Arrays;
import java.util.Comparator;

import org.apache.directory.studio.ldapbrowser.common.BrowserCommonActivator;
import org.apache.directory.studio.ldapbrowser.common.BrowserCommonConstants;
import org.apache.directory.studio.ldapbrowser.core.BrowserCoreConstants;
import org.apache.directory.studio.ldapbrowser.core.model.AttributeHierarchy;
import org.apache.directory.studio.ldapbrowser.core.model.IAttribute;
import org.apache.directory.studio.ldapbrowser.core.model.IEntry;
import org.apache.directory.studio.ldapbrowser.core.model.ISearch;
import org.apache.directory.studio.ldapbrowser.core.model.ISearchResult;
import org.apache.directory.studio.ldapbrowser.ui.BrowserUIConstants;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.TableColumn;


/**
 * The SearchResultEditorSorter implements the Sorter for the search result editor.
 *
 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
 */
public class SearchResultEditorSorter extends ViewerSorter implements SelectionListener
{

    /** The content provider. */
    protected SearchResultEditorContentProvider contentProvider;

    /** The search. */
    private ISearch search;

    /** The columns. */
    private TableColumn[] columns;

    /** The show Dn flag. */
    private boolean showDn;

    /** The sort property. */
    private int sortBy;

    /** The sort order. */
    private int sortOrder;


    /**
     * Connects this sorter to the given content provider.
     * 
     * @param contentProvider the content provider
     */
    public void connect( SearchResultEditorContentProvider contentProvider )
    {
        this.contentProvider = contentProvider;

        sortBy = 0;
        sortOrder = BrowserCoreConstants.SORT_ORDER_NONE;

        columns = contentProvider.getViewer().getTable().getColumns();

        for ( TableColumn column : columns )
        {
            column.addSelectionListener( this );
        }
    }


    /**
     * Called when the input of the viewer has been changed.
     * 
     * @param newSearch the new search
     * @param showDn the show Dn flag
     */
    public void inputChanged( ISearch newSearch, boolean showDn )
    {
        this.search = newSearch;
        this.showDn = showDn;

        if ( columns != null )
        {
            for ( TableColumn column : columns )
            {
                column.removeSelectionListener( this );
            }
        }

        columns = contentProvider.getViewer().getTable().getColumns();

        for ( TableColumn column : columns )
        {
            column.addSelectionListener( this );
        }

        // check sort column
        int visibleColumns = search.getReturningAttributes().length;

        if ( showDn )
        {
            visibleColumns++;
        }

        if ( visibleColumns < sortBy + 1 )
        {
            setSortColumn( 0 );
            setSortColumn( 0 );
            setSortColumn( 0 );
        }
    }


    public void dispose()
    {
        if ( columns != null )
        {
            for ( TableColumn column : columns )
            {
                if ( !column.isDisposed() )
                {
                    column.removeSelectionListener( this );
                }
            }
        }

        columns = null;
        search = null;
        contentProvider = null;
    }


    /**
     * {@inheritDoc}
     */
    public void widgetDefaultSelected( SelectionEvent e )
    {
    }


    /**
     * {@inheritDoc}
     */
    public void widgetSelected( SelectionEvent e )
    {
        if ( e.widget instanceof TableColumn )
        {
            int index = contentProvider.getViewer().getTable().indexOf( ( ( TableColumn ) e.widget ) );
            setSortColumn( index );
        }
    }


    /**
     * Sets the sort column.
     * 
     * @param index the new sort column
     */
    private void setSortColumn( int index )
    {
        if ( sortBy == index )
        {
            // toggle sort order
            sortOrder = sortOrder == BrowserCoreConstants.SORT_ORDER_ASCENDING ? BrowserCoreConstants.SORT_ORDER_DESCENDING
                : sortOrder == BrowserCoreConstants.SORT_ORDER_DESCENDING ? BrowserCoreConstants.SORT_ORDER_NONE
                    : BrowserCoreConstants.SORT_ORDER_ASCENDING;
        }
        else
        {
            // set new sort by
            sortBy = index;
            sortOrder = BrowserCoreConstants.SORT_ORDER_ASCENDING;
        }

        if ( sortOrder == BrowserCoreConstants.SORT_ORDER_NONE )
        {
            sortBy = BrowserCoreConstants.SORT_BY_NONE;
        }

        TableColumn[] columns = contentProvider.getViewer().getTable().getColumns();

        for ( TableColumn column : columns )
        {
            column.setImage( null );
        }

        if ( sortOrder == BrowserCoreConstants.SORT_ORDER_ASCENDING )
        {
            ( columns[index] ).setImage( BrowserCommonActivator.getDefault().getImage(
                BrowserCommonConstants.IMG_SORT_ASCENDING ) );
        }
        else if ( sortOrder == BrowserCoreConstants.SORT_ORDER_DESCENDING )
        {
            ( columns[index] ).setImage( BrowserCommonActivator.getDefault().getImage(
                BrowserCommonConstants.IMG_SORT_DESCENDING ) );
        }
        else
        {
            ( columns[index] ).setImage( null );
        }

        contentProvider.refresh();
    }


    /**
     * Checks if is sorted.
     * 
     * @return true, if is sorted
     */
    public boolean isSorted()
    {
        // return sortOrder != SORT_ORDER_NONE;
        return true;
    }


    /**
     * {@inheritDoc}
     */
    public void sort( final Viewer viewer, Object[] elements )
    {
        if ( isSorted() )
        {
            Arrays.sort( elements, new Comparator<Object>()
            {
                public int compare( Object a, Object b )
                {
                    return SearchResultEditorSorter.this.compare( viewer, a, b );
                }
            } );
        }

    }


    /**
     * {@inheritDoc}
     */
    public int compare( Viewer viewer, Object o1, Object o2 )
    {
        if ( search == null )
        {
            return equal();
        }

        ISearchResult sr1 = ( ISearchResult ) o1;
        ISearchResult sr2 = ( ISearchResult ) o2;

        IEntry entry1 = sr1.getEntry();
        IEntry entry2 = sr2.getEntry();

        if ( entry1 == null )
        {
            if ( entry2 == null )
            {
                return equal();
            }
            else
            {
                return lessThan();
            }
        }
        else if ( entry2 == null )
        {
            return greaterThan();
        }
        else
        {
            String attributeName;

            if ( showDn && ( sortBy == 0 ) )
            {
                attributeName = BrowserUIConstants.DN;
            }
            else if ( showDn && ( sortBy > 0 ) )
            {
                attributeName = search.getReturningAttributes()[sortBy - 1];
            }
            else
            {
                attributeName = search.getReturningAttributes()[sortBy];
            }

            if ( attributeName == BrowserUIConstants.DN )
            {
                // compare normalized names
                return compare( entry1.getDn().getNormName(), entry2.getDn().getNormName() );
            }
            else
            {
                AttributeHierarchy ah1 = entry1.getAttributeWithSubtypes( attributeName );
                AttributeHierarchy ah2 = entry2.getAttributeWithSubtypes( attributeName );

                if ( ah1 == null )
                {
                    if ( ah2 == null )
                    {
                        return equal();
                    }
                    else
                    {
                        return lessThan();
                    }
                }
                else if ( ah2 == null )
                {
                    return greaterThan();
                }
                else
                {
                    IAttribute attribute1 = ah1.getAttribute();
                    IAttribute attribute2 = ah2.getAttribute();

                    String value1 = getValue( attribute1 );
                    String value2 = getValue( attribute2 );
                    return compare( value1, value2 );
                }
            }
        }
    }


    /**
     * Gets the value.
     * 
     * @param attribute the attribute
     * 
     * @return the value
     */
    private String getValue( IAttribute attribute )
    {
        if ( attribute.getValueSize() > 0 )
        {
            return attribute.getStringValue();
        }
        else
        {
            return ""; //$NON-NLS-1$
        }
    }


    /**
     * Gets the less than value.
     * 
     * @return the less than value
     */
    private int lessThan()
    {
        return sortOrder == BrowserCoreConstants.SORT_ORDER_ASCENDING ? -1 : 1;
    }


    /**
     * Gets the equal value.
     * 
     * @return the equal value
     */
    private int equal()
    {
        return 0;
    }


    /**
     * Gets the greater than value.
     * 
     * @return the greater than value
     */
    private int greaterThan()
    {
        return sortOrder == BrowserCoreConstants.SORT_ORDER_ASCENDING ? 1 : -1;
    }


    /**
     * Compare two strings.
     * 
     * @param s1 the 1st string
     * @param s2 the 2nd string
     * 
     * @return the compare result
     */
    private int compare( String s1, String s2 )
    {
        return sortOrder == BrowserCoreConstants.SORT_ORDER_ASCENDING ? s1.compareToIgnoreCase( s2 ) : s2
            .compareToIgnoreCase( s1 );
    }

}
