/*
 *   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.openldap.config.acl.widgets;


import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.directory.studio.ldapbrowser.core.model.IBrowserConnection;
import org.apache.directory.studio.ldapbrowser.core.model.schema.SchemaUtils;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalProvider;


/**
 * 
 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
 */
public class AttributesWidgetContentProposalProvider implements IContentProposalProvider
{
    /** The content proposal adapter */
    private ContentProposalAdapter proposalAdapter;

    /** The browser connection */
    private IBrowserConnection browserConnection;

    /** The proposals */
    private List<AttributesWidgetContentProposal> proposals;


    public AttributesWidgetContentProposalProvider()
    {
        // Initializing the proposals list
        proposals = new ArrayList<AttributesWidgetContentProposal>();

        // Building the proposals list
        buildProposals();
    }


    public IContentProposal[] getProposals( String contents, int position )
    {
        String value = getCurrentValue( contents, position );

        List<AttributesWidgetContentProposal> matchingProposals = new ArrayList<AttributesWidgetContentProposal>();
        for ( AttributesWidgetContentProposal proposal : proposals )
        {
            if ( proposal.getLabel().toUpperCase().startsWith( value.toUpperCase() ) )
            {
                matchingProposals.add( proposal );
                proposal.setStartPosition( value.length() );
            }
        }

        return matchingProposals.toArray( new AttributesWidgetContentProposal[0] );
    }


    /**
     * {@inheritDoc}
     */
    public char[] getAutoActivationCharacters()
    {
        return new char[0];
    }


    /**
     * Gets the current value.
     *
     * @param contents the contents
     * @param position the position
     * @return the current value
     */
    private String getCurrentValue( String contents, int position )
    {
        int start = 0;

        for ( int i = position - 1; i >= 0; i-- )
        {
            char c = contents.charAt( i );
            if ( c == ',' || Character.isWhitespace( c ) )
            {
                start = i + 1;
                break;
            }
        }

        return contents.substring( start, position );
    }


    /**
     * Builds the proposals list
     */
    private void buildProposals()
    {
        // Reseting previous proposals
        proposals.clear();

        // Adding proposals
        addKeywordProposals();
        addConnectionProposals();

        // Sorting the proposals
        sortProposals();

        // Setting auto-activation characters
        setAutoActivationChars();
    }


    /**
     * Sets the auto-activation characters
     */
    private void setAutoActivationChars()
    {
        if ( proposalAdapter != null )
        {
            Set<Character> characterSet = new HashSet<Character>();
            for ( IContentProposal proposal : proposals )
            {
                String string = proposal.getLabel();
                for ( int k = 0; k < string.length(); k++ )
                {
                    char ch = string.charAt( k );
                    characterSet.add( Character.toLowerCase( ch ) );
                    characterSet.add( Character.toUpperCase( ch ) );
                }
            }

            char[] autoActivationCharacters = new char[characterSet.size() + 1];
            autoActivationCharacters[0] = ',';
            int i = 1;
            for ( Iterator<Character> it = characterSet.iterator(); it.hasNext(); )
            {
                Character ch = it.next();
                autoActivationCharacters[i] = ch.charValue();
                i++;
            }

            proposalAdapter.setAutoActivationCharacters( autoActivationCharacters );
        }
    }


    /**
     * Adding the keyword proposals
     */
    private void addKeywordProposals()
    {
        proposals.add( new KeywordContentProposal( "entry" ) );
        proposals.add( new KeywordContentProposal( "children" ) );
    }


    /**
     * Adding the connection proposals (attribute types and object classes).
     */
    private void addConnectionProposals()
    {
        if ( browserConnection != null )
        {
            // Attribute types
            Collection<String> atNames = SchemaUtils.getNames( browserConnection.getSchema().getAttributeTypeDescriptions() );
            for ( String atName : atNames )
            {
                proposals.add( new AttributeTypeContentProposal( atName ) );
            }

            // Object classes
            Collection<String> ocNames = SchemaUtils.getNames( browserConnection.getSchema().getObjectClassDescriptions() );
            for ( String ocName : ocNames )
            {
                proposals.add( new ObjectClassContentProposal( "@" + ocName ) );
                proposals.add( new ObjectClassContentProposal( "!" + ocName ) );
            }
        }
    }


    /**
     * Sorts the proposals.
     */
    private void sortProposals()
    {
        Comparator<? super AttributesWidgetContentProposal> comparator = new Comparator<AttributesWidgetContentProposal>()
        {
            public int compare( AttributesWidgetContentProposal o1, AttributesWidgetContentProposal o2 )
            {
                if ( ( o1 instanceof KeywordContentProposal ) && !( o2 instanceof KeywordContentProposal ) )
                {
                    return -2;
                }
                else if ( !( o1 instanceof KeywordContentProposal ) && ( o2 instanceof KeywordContentProposal ) )
                {
                    return 2;
                }

                else if ( ( o1 instanceof AttributeTypeContentProposal )
                    && !( o2 instanceof AttributeTypeContentProposal ) )
                {
                    return -3;
                }
                else if ( !( o1 instanceof AttributeTypeContentProposal )
                    && ( o2 instanceof AttributeTypeContentProposal ) )
                {
                    return 3;
                }

                else if ( ( o1 instanceof ObjectClassContentProposal )
                    && !( o2 instanceof ObjectClassContentProposal ) )
                {
                    return -3;
                }
                else if ( !( o1 instanceof ObjectClassContentProposal )
                    && ( o2 instanceof ObjectClassContentProposal ) )
                {
                    return 3;
                }

                return o1.getLabel().compareToIgnoreCase( o2.getLabel() );
            }
        };
        Collections.sort( proposals, comparator );
    }


    /**
     * @param browserConnection the browser connection to set
     */
    public void setBrowserConnection( IBrowserConnection browserConnection )
    {
        this.browserConnection = browserConnection;

        // Re-building proposals
        buildProposals();
    }


    /**
     * @param proposalAdapter the proposalAdapter to set
     */
    public void setProposalAdapter( ContentProposalAdapter proposalAdapter )
    {
        this.proposalAdapter = proposalAdapter;
    }
}
