/*
 *
 *  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.flex.swf.tags;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;

/**
 * Factory class to create an {@code Iterable} object from potentially several
 * iterable instances
 */
public class CharacterIterableFactory
{
    /**
     * Create an iterable instance by combining two iterable objects. The
     * iteration order is {@code i1} then {@code i2}. Each character will only
     * be visited once. All duplicated characters will be ignored.
     * 
     * @param i1 first iterable object
     * @param i2 second iterable object
     * @return combined iterable object
     */
    public static Iterable<ICharacterTag> collect(
            final Iterable<ICharacterTag> i1,
            final Iterable<ICharacterTag> i2)
    {
        // Possible optimization: hand-build an iterable instead of using a list.
        final Collection<ICharacterTag> merged = new LinkedHashSet<ICharacterTag>();
        for (final ICharacterTag character : i1)
        {
            merged.add(character);
        }

        for (final ICharacterTag character : i2)
        {
            merged.add(character);
        }
        return merged;
    }

    /**
     * Create an iterable instance by combining two iterable objects. The
     * iteration order is {@code i1} then {@code i2}.Each character will only be
     * visited once. All duplicated characters will be ignored.
     * 
     * @param i1 first iterable object
     * @param i2 second iterable object
     * @param i3 third iterable object
     * @return combined iterable object
     */
    public static Iterable<ICharacterTag> collect(
            final Iterable<ICharacterTag> i1,
            final Iterable<ICharacterTag> i2,
            final Iterable<ICharacterTag> i3)
    {
        // Possible optimization: hand-build an iterable instead of using a list.
        final Collection<ICharacterTag> merged = new LinkedHashSet<ICharacterTag>();
        for (final ICharacterTag character : i1)
        {
            merged.add(character);
        }

        for (final ICharacterTag character : i2)
        {
            merged.add(character);
        }

        for (final ICharacterTag character : i3)
        {
            merged.add(character);
        }
        return merged;
    }

    /**
     * Create an iterable object from multiple {@link ICharacterReferrer}
     * objects. The result consists of all the referred characters by them. Each
     * character will only be visited once. All duplicated characters will be
     * ignored.
     * 
     * @param referrers multiple {@link ICharacterReferrer} objects.
     * @return all the referred character tags
     */
    public static Iterable<ICharacterTag> collect(final Iterable<? extends ICharacterReferrer> referrers)
    {
        final Collection<ICharacterTag> merged = new LinkedHashSet<ICharacterTag>();
        for (final Object referrer : referrers)
        {
            for (final ICharacterTag character : ((ICharacterReferrer)referrer).getReferences())
            {
                merged.add(character);
            }
        }
        return merged;
    }

    /**
     * Find all the ICharacterReferrer instances from a generic collection.
     * Then, create an iterable object from these {@link ICharacterReferrer}
     * objects. The result consists of all the referred characters by them. Each
     * character will only be visited once. All duplicated characters will be
     * ignored.
     * 
     * @param iterable an iterable object that contains {@code ICharacterReferrer}
     * members
     * @return all the referred character tags
     */
    public static Iterable<ICharacterTag> filterAndCollect(final Iterable<?> iterable)
    {
        final Collection<ICharacterTag> merged = new LinkedHashSet<ICharacterTag>();
        for (final Object object : iterable)
        {
            if (object instanceof ICharacterReferrer)
            {
                for (final ICharacterTag character : (((ICharacterReferrer)object).getReferences()))
                {
                    assert character != null;
                    merged.add(character);
                }
            }
        }
        return merged;
    }

    /**
     * An empty iterable object.
     */
    private static final Iterable<ICharacterTag> EMPTY = new Iterable<ICharacterTag>()
    {
        @Override
        public Iterator<ICharacterTag> iterator()
        {
            return new Iterator<ICharacterTag>()
            {
                @Override
                public boolean hasNext()
                {
                    return false;
                }

                @Override
                public ICharacterTag next()
                {
                    return null;
                }

                @Override
                public void remove()
                {
                }
            };
        }
    };

    /**
     * Always use this empty iterable instead of creating an empty
     * {@code Collection}.
     * 
     * @return an empty iterable
     */
    public static Iterable<ICharacterTag> empty()
    {
        return EMPTY;
    }

    /**
     * Create an iterable object from one member.
     * 
     * @param character the only member in the iterable
     * @return one-item iterable
     */
    public static Iterable<ICharacterTag> from(final ICharacterTag character)
    {
        return new Iterable<ICharacterTag>()
        {
            @Override
            public Iterator<ICharacterTag> iterator()
            {
                return new Iterator<ICharacterTag>()
                {
                    private boolean visited = false;

                    @Override
                    public boolean hasNext()
                    {
                        if (visited)
                        {
                            return false;
                        }
                        else
                        {
                            return true;
                        }
                    }

                    @Override
                    public ICharacterTag next()
                    {
                        if (visited)
                        {
                            return null;
                        }
                        else
                        {
                            visited = true;
                            return character;
                        }
                    }

                    @Override
                    public void remove()
                    {
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }
}
