/*
 * 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.workspace;

import icy.network.NetworkUtil;
import icy.network.URLUtil;
import icy.preferences.RepositoryPreferences;
import icy.preferences.RepositoryPreferences.RepositoryInfo;
import icy.system.IcyExceptionHandler;
import icy.util.StringUtil;
import icy.util.XMLUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EventListener;

import javax.swing.event.EventListenerList;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

/**
 * @author Stephane
 */
public class WorkspaceRepositoryLoader
{
    public static interface WorkspaceRepositoryLoaderListener extends EventListener
    {
        public void workspaceRepositeryLoaderChanged();
    }

    private class LoadRunner extends Thread
    {
        public LoadRunner()
        {
            super("Online workspace loader");
        }

        @Override
        public void run()
        {
            // no Internet connection ?
            if (!NetworkUtil.hasInternetAccess())
            {
                failed = true;
                return;
            }

            final ArrayList<Workspace> newWorkspaces = new ArrayList<Workspace>();

            failed = false;
            try
            {
                final ArrayList<RepositoryInfo> repositories = RepositoryPreferences.getRepositeries();

                // load online workspace from all active repositories
                for (RepositoryInfo repoInfo : repositories)
                {
                    if (interrupted())
                        return;

                    if (repoInfo.isEnabled())
                    {
                        final ArrayList<Workspace> workspacesRepos = loadInternal(repoInfo);

                        if (workspacesRepos == null)
                        {
                            failed = true;
                            return;
                        }

                        newWorkspaces.addAll(workspacesRepos);
                    }
                }

                // sort list
                Collections.sort(newWorkspaces);

                synchronized (workspaces)
                {
                    workspaces = newWorkspaces;
                }
            }
            catch (Exception e)
            {
                IcyExceptionHandler.showErrorMessage(e, true);
                failed = true;
                return;
            }

            changed();
        }
    }

    private static final String ID_ROOT = "workspaces";
    private static final String ID_WORKSPACE = "workspace";
    private static final String ID_PATH = "path";

    /**
     * static class
     */
    private static final WorkspaceRepositoryLoader instance = new WorkspaceRepositoryLoader();

    /**
     * Online workspace list
     */
    ArrayList<Workspace> workspaces;

    /**
     * internal
     */
    boolean failed;

    private LoadRunner loadRunner;

    /**
     * listeners
     */
    private final EventListenerList listeners;

    /**
     * static class
     */
    private WorkspaceRepositoryLoader()
    {
        super();

        workspaces = new ArrayList<Workspace>();
        listeners = new EventListenerList();
        failed = false;

        // initial loading
        startLoad();
    }

    /**
     * Return the workspaces file list from a repository URL
     */
    public static ArrayList<String> getWorkspaceFiles(RepositoryInfo repos)
    {
        final ArrayList<String> result = new ArrayList<String>();
        final Document document = XMLUtil.loadDocument(repos.getLocation(), repos.getAuthenticationInfo(), true);

        if (document != null)
        {
            // get workspaces node
            final Node workspacesNode = XMLUtil.getElement(document.getDocumentElement(), ID_ROOT);

            // workspaces node found
            if (workspacesNode != null)
            {
                final ArrayList<Node> nodes = XMLUtil.getChildren(workspacesNode, ID_WORKSPACE);

                for (Node node : nodes)
                {
                    final String path = XMLUtil.getElementValue(node, ID_PATH, "");

                    if (!StringUtil.isEmpty(path))
                        result.add(path);
                }
            }
        }
        else
        {
            if (!NetworkUtil.hasInternetAccess())
                System.out.println("You are not connected to internet.");
            else
                System.out.println("Can't access repository '" + repos.getName() + "'");
        }

        return result;
    }

    /**
     * Start loading process
     */
    private void startLoad()
    {
        loadRunner = new LoadRunner();
        loadRunner.start();
    }

    /**
     * Reload all online workspaces from all active repositories (old list is cleared)
     */
    public static void reload()
    {
        // request interrupt
        instance.loadRunner.interrupt();

        // We don't need to wait... can make the preferences panel to lag on validation
//        // wait for end processing
//        try
//        {
//            instance.loadRunner.join();
//        }
//        catch (InterruptedException e)
//        {
//            // ignore
//        }

        // start it again
        instance.startLoad();
    }

    /**
     * Load and return the list of online workspaces located at specified repository url
     */
    ArrayList<Workspace> loadInternal(RepositoryInfo repos)
    {
        final ArrayList<String> paths = getWorkspaceFiles(repos);

        // error while retrieving paths ?
        if (paths == null)
        {
            if (!NetworkUtil.hasInternetAccess())
                System.out.println("You are not connected to internet.");
            else
                System.out.println("Can't access repository '" + repos.getName() + "'");

            return null;
        }

        final ArrayList<Workspace> result = new ArrayList<Workspace>();

        for (String path : paths)
        {
            try
            {
                final Workspace workspace = new Workspace(URLUtil.getURL(path), repos);

                if (!workspace.isEmpty())
                    result.add(workspace);
            }
            catch (Exception e)
            {
                System.err.println("WorkspaceRepositoryLoader.load('" + repos.getLocation() + "') error :");
                IcyExceptionHandler.showErrorMessage(e, false);
            }
        }

        return result;
    }

    /**
     * @return the workspaceList
     */
    public static ArrayList<Workspace> getWorkspaces()
    {
        synchronized (instance.workspaces)
        {
            return new ArrayList<Workspace>(instance.workspaces);
        }
    }

    public static Workspace getWorkspace(String className)
    {
        synchronized (instance.workspaces)
        {
            return Workspace.getWorkspace(instance.workspaces, className);
        }
    }

    /**
     * Return the workspace list from the specified repository
     */
    public static ArrayList<Workspace> getWorkspaces(RepositoryInfo repos)
    {
        final ArrayList<Workspace> result = new ArrayList<Workspace>();

        synchronized (instance.workspaces)
        {
            for (Workspace workspace : instance.workspaces)
                if (workspace.getRepository().equals(repos))
                    result.add(workspace);
        }

        return result;
    }

    /**
     * @return the loaded
     */
    public static boolean isLoading()
    {
        return instance.loadRunner.isAlive();
    }

    /**
     * wait until loading completed
     */
    public static void waitWhileLoading()
    {
        try
        {
            instance.loadRunner.join();
        }
        catch (InterruptedException e)
        {
            // ignore
        }
    }

    /**
     * return true if an error occurred during the workspace loading process
     */
    public static boolean failed()
    {
        return instance.failed;
    }

    /**
     * workspace list has changed
     */
    void changed()
    {
        fireEvent();
    }

    /**
     * Add a listener
     * 
     * @param listener
     */
    public static void addListener(WorkspaceRepositoryLoaderListener listener)
    {
        synchronized (instance.listeners)
        {
            instance.listeners.add(WorkspaceRepositoryLoaderListener.class, listener);
        }
    }

    /**
     * Remove a listener
     * 
     * @param listener
     */
    public static void removeListener(WorkspaceRepositoryLoaderListener listener)
    {
        synchronized (instance.listeners)
        {
            instance.listeners.remove(WorkspaceRepositoryLoaderListener.class, listener);
        }
    }

    /**
     * fire event
     */
    private void fireEvent()
    {
        for (WorkspaceRepositoryLoaderListener listener : listeners
                .getListeners(WorkspaceRepositoryLoaderListener.class))
            listener.workspaceRepositeryLoaderChanged();
    }

}