package org.mtransit.android.task;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.mtransit.android.commons.CollectionUtils;
import org.mtransit.android.commons.MTLog;
import org.mtransit.android.commons.RuntimeUtils;
import org.mtransit.android.commons.data.News;
import org.mtransit.android.commons.provider.NewsProviderContract;
import org.mtransit.android.commons.provider.NewsProviderContract.Filter;
import org.mtransit.android.commons.task.MTCallable;
import org.mtransit.android.data.DataSourceManager;
import org.mtransit.android.data.DataSourceProvider;
import org.mtransit.android.data.NewsProviderProperties;

import android.content.Context;

public class NewsLoader extends MTAsyncTaskLoaderV4<ArrayList<News>> {

	private static final String TAG = NewsLoader.class.getSimpleName();

	@Override
	public String getLogTag() {
		return TAG;
	}

	private ArrayList<News> news;
	private ArrayList<String> targetAuthorities;
	private ArrayList<String> filterUUIDs;
	private ArrayList<String> filterTargets;

	public NewsLoader(Context context, ArrayList<String> optTargetAuthorities, ArrayList<String> optFilterUUIDs, ArrayList<String> optFilterTargets) {
		super(context);
		this.targetAuthorities = optTargetAuthorities;
		this.filterUUIDs = optFilterUUIDs;
		this.filterTargets = optFilterTargets;
	}

	@Override
	public ArrayList<News> loadInBackgroundMT() {
		if (this.news != null) {
			return this.news;
		}
		this.news = new ArrayList<News>();
		ArrayList<NewsProviderProperties> newsProviders;
		if (CollectionUtils.getSize(this.targetAuthorities) == 0) {
			newsProviders = DataSourceProvider.get(getContext()).getAllNewsProvider();
		} else {
			newsProviders = new ArrayList<NewsProviderProperties>();
			for (String targetAuthority : this.targetAuthorities) {
				newsProviders.addAll(DataSourceProvider.get(getContext()).getTargetAuthorityNewsProviders(targetAuthority));
			}
		}
		if (CollectionUtils.getSize(newsProviders) == 0) {
			MTLog.w(this, "loadInBackground() > no News provider found");
			return this.news;
		}
		ThreadPoolExecutor executor = new ThreadPoolExecutor(RuntimeUtils.NUMBER_OF_CORES, RuntimeUtils.NUMBER_OF_CORES, 1, TimeUnit.SECONDS,
				new LinkedBlockingDeque<Runnable>(newsProviders.size()));
		ArrayList<Future<ArrayList<News>>> taskList = new ArrayList<Future<ArrayList<News>>>();
		for (NewsProviderProperties newsProvider : newsProviders) {
			taskList.add(executor.submit(new FindNewsTask(getContext(), newsProvider.getAuthority(), this.filterUUIDs, this.filterTargets)));
		}
		HashSet<String> newsUUIDs = new HashSet<String>();
		for (Future<ArrayList<News>> future : taskList) {
			try {
				ArrayList<News> agencyNews = future.get();
				for (News aNews : agencyNews) {
					if (newsUUIDs.contains(aNews.getUUID())) {
						continue;
					}
					this.news.add(aNews);
					newsUUIDs.add(aNews.getUUID());
				}
			} catch (Exception e) {
				MTLog.w(this, e, "Error while loading in background!");
			}
		}
		executor.shutdown();
		CollectionUtils.sort(this.news, News.NEWS_COMPARATOR);
		return this.news;
	}

	@Override
	protected void onStartLoading() {
		super.onStartLoading();
		if (this.news != null) {
			deliverResult(this.news);
		}
		if (this.news == null) {
			forceLoad();
		}
	}

	@Override
	protected void onStopLoading() {
		super.onStopLoading();
		cancelLoad();
	}

	@Override
	public void deliverResult(ArrayList<News> data) {
		this.news = data;
		if (isStarted()) {
			super.deliverResult(data);
		}
	}

	private static class FindNewsTask extends MTCallable<ArrayList<News>> {

		private static final String TAG = NewsLoader.class.getSimpleName() + ">" + FindNewsTask.class.getSimpleName();

		@Override
		public String getLogTag() {
			return TAG;
		}

		private Context context;
		private String authority;
		private ArrayList<String> filterUUIDs;
		private ArrayList<String> filterTargets;

		public FindNewsTask(Context context, String authority, ArrayList<String> optFilterUUIDs, ArrayList<String> optFilterTargets) {
			this.context = context;
			this.authority = authority;
			this.filterUUIDs = optFilterUUIDs;
			this.filterTargets = optFilterTargets;
		}

		@Override
		public ArrayList<News> callMT() throws Exception {
			Filter newsFilter;
			if (CollectionUtils.getSize(this.filterUUIDs) > 0) {
				newsFilter = NewsProviderContract.Filter.getNewUUIDsFilter(this.filterUUIDs);
			} else if (CollectionUtils.getSize(this.filterTargets) > 0) {
				newsFilter = NewsProviderContract.Filter.getNewTargetsFilter(this.filterTargets);
			} else {
				newsFilter = NewsProviderContract.Filter.getNewEmptyFilter();
			}
			return DataSourceManager.findNews(this.context, this.authority, newsFilter);
		}
	}
}

@@ -2876,16 +2876,47 @@
 .get();%0A
+%09%09%09%09if (agencyNews != null) %7B%0A%09
 %09%09%09%09for 
@@ -2939,24 +2939,25 @@
 encyNews) %7B%0A
+%09
 %09%09%09%09%09if (new
@@ -2988,24 +2988,25 @@
 tUUID())) %7B%0A
+%09
 %09%09%09%09%09%09contin
@@ -3014,18 +3014,20 @@
 e;%0A%09%09%09%09%09
+%09
 %7D%0A
+%09
 %09%09%09%09%09thi
@@ -3045,16 +3045,17 @@
 aNews);%0A
+%09
 %09%09%09%09%09new
@@ -3083,16 +3083,23 @@
 UID());%0A
+%09%09%09%09%09%7D%0A
 %09%09%09%09%7D%0A%09%09

Fix NPE in production.