/*
 * Copyright 2010-2015 Institut Pasteur.
 * 
 * This file is part of Icy.
 * 
 * Icy is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Icy is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
 */
package icy.search;

import icy.network.URLUtil;
import icy.system.thread.ThreadUtil;
import icy.util.XMLUtil;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import org.w3c.dom.Document;

/**
 * The OnlineSearchResultProducer is the basic class for {@link SearchResult} producer from online
 * results.<br>
 * It does use a single static instance to do the online search then dispatch XML result the
 * overriding class.
 * 
 * @author Stephane Dallongeville
 * @see SearchResultProducer
 */
public abstract class OnlineSearchResultProducer extends SearchResultProducer
{
    protected static final String SEARCH_URL = "http://bioimageanalysis.org/icy/search/search.php?search=";
    protected static final long REQUEST_INTERVAL = 400;
    protected static final long MAXIMUM_SEARCH_TIME = 10000;

    // we want to have only one online search shared between all online providers
    protected static volatile boolean searchingOnline = false;
    protected static Document document = null;

    @Override
    public void doSearch(String[] words, SearchResultConsumer consumer)
    {
        if (searchingOnline)
        {
            // just wait while searching end
            while (searchingOnline)
            {
                // abort
                if (hasWaitingSearch())
                    return;
                ThreadUtil.sleep(10);
            }
        }
        else
        {
            // do the online search
            searchingOnline = true;
            try
            {
                document = null;
                String request = SEARCH_URL;

                try
                {
                    if (words.length > 0)
                        request += URLEncoder.encode(words[0], "UTF-8");
                    for (int i = 1; i < words.length; i++)
                        request += "%20" + URLEncoder.encode(words[i], "UTF-8");
                }
                catch (UnsupportedEncodingException e)
                {
                    // can't encode
                    return;
                }

                final long startTime = System.currentTimeMillis();

                // wait interval elapsed before sending request (avoid website request spam)
                while ((System.currentTimeMillis() - startTime) < REQUEST_INTERVAL)
                {
                    ThreadUtil.sleep(10);
                    // abort
                    if (hasWaitingSearch())
                        return;
                }

                int retry = 0;

                // let's 5 tries to get the result
                while (((System.currentTimeMillis() - startTime) < MAXIMUM_SEARCH_TIME) && (document == null)
                        && (retry < 5))
                {
                    // we use an online request as website can search in plugin documentation
                    document = XMLUtil.loadDocument(URLUtil.getURL(request), false);

                    // abort
                    if (hasWaitingSearch())
                        return;

                    // error ? --> wait a bit before retry
                    if (document == null)
                        ThreadUtil.sleep(100);

                    retry++;
                }

                // can't get result from website --> exit
                if (document == null)
                    return;

                // already have other pending search --> exit
                if (hasWaitingSearch())
                    return;
            }
            finally
            {
                searchingOnline = false;
            }
        }

        final Document doc = document;

        // another search waiting or error --> exit
        if (hasWaitingSearch() || (doc == null))
            return;

        doSearch(doc, words, consumer);
    }

    public abstract void doSearch(Document doc, String[] words, SearchResultConsumer consumer);
}