package org.unicode.cldr.icu;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * Superclass for mappers that convert CLDR data to ICU text files.
 * @author jchye
 */
public abstract class Mapper {
    // Store list of sources for makefile generation.
    protected Set<String> sources = new HashSet<String>();

    /**
     * Converts the specified locale into one or more IcuData objects.
     */
    public abstract IcuData[] fillFromCldr(String locale); // private/protected?

    /**
     * Returns an iterator that iterates through all of the CLDR data within the
     * scope of this mapper and matching the specified filter and returns the generated IcuData objects.
     */
    public Iterator<IcuData> iterator(final Filter filter) {
        IcuDataIterator iterator = new IcuDataIterator(filter);
        iterator.init();
        return iterator;
    }

    private class IcuDataIterator implements Iterator<IcuData> {
        private Filter filter;
        private Iterator<String> localeIterator = getAvailable().iterator();
        private IcuData[] curArray;
        private int curIndex = -1;

        private IcuDataIterator(Filter filter) {
            this.filter = filter;
        }

        /**
         * Initializes internal variables before starting iteration.
         */
        private void init() {
            curArray = new IcuData[0];
            loadNextPos();
        }

        @Override
        public boolean hasNext() {
            return localeIterator.hasNext() || curIndex < curArray.length;
        }

        @Override
        public IcuData next() {
            IcuData icuData = curArray[curIndex];
            sources.add(icuData.getName());
            // Move to next valid position.
            loadNextPos();
            return icuData;
        }

        /**
         * Prepares the next item in the iterator.
         */
        private void loadNextPos() {
            do {
                curIndex++;
                if (curIndex == curArray.length) {
                    String locale = null;
                    do {
                        if (!localeIterator.hasNext()) {
                            // No more items left in the iterator.
                            return;
                        }
                        locale = localeIterator.next();
                    } while (!filter.includes(locale));
                    curArray = fillFromCldr(locale);
                    curIndex = 0;
                }
            } while (!filter.includes(curArray[curIndex].getName()));
        }

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

    /**
     * @return the set of IcuData objects that were generated by the mapper.
     */
    public Set<String> getGenerated() {
        return sources;
    }

    /**
     * Returns the set of all CLDR locales available for conversion.
     */
    public abstract Collection<String> getAvailable();

    /**
     * Generates a makefile from the list of locales converted by this mapper.
     * @param aliases any aliases that were created apart from this mapper
     * @return the generated makefile
     */
    public abstract Makefile generateMakefile(Collection<String> aliases);
}
