content: /*******************************************************************************
   * Copyright 2011-2013 Sergey Tarasevich
   *
   * Licensed 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 com.nostra13.universalimageloader.core;
  
  import android.view.View;
  import com.nostra13.universalimageloader.core.assist.FailReason;
  import com.nostra13.universalimageloader.core.assist.FlushedInputStream;
  import com.nostra13.universalimageloader.core.imageaware.ImageAware;
  import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
  
  import java.io.File;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.WeakHashMap;
  import java.util.concurrent.Executor;
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.atomic.AtomicBoolean;
  import java.util.concurrent.locks.ReentrantLock;
  
  /**
   * {@link ImageLoader} engine which responsible for {@linkplain LoadAndDisplayImageTask display task} execution.
   *
   * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
   * @since 1.7.1
   */
  class ImageLoaderEngine {
  
  	final ImageLoaderConfiguration configuration;
  
  	private Executor taskExecutor;
  	private Executor taskExecutorForCachedImages;
  	private Executor taskDistributor;
  
  	private final Map<Integer, String> cacheKeysForImageAwares = Collections
  			.synchronizedMap(new HashMap<Integer, String>());
  	private final Map<String, ReentrantLock> uriLocks = new WeakHashMap<String, ReentrantLock>();
  
  	private final AtomicBoolean paused = new AtomicBoolean(false);
  	private final AtomicBoolean networkDenied = new AtomicBoolean(false);
  	private final AtomicBoolean slowNetwork = new AtomicBoolean(false);
  
  	private final Object pauseLock = new Object();
  
  	ImageLoaderEngine(ImageLoaderConfiguration configuration) {
  		this.configuration = configuration;
  
  		taskExecutor = configuration.taskExecutor;
  		taskExecutorForCachedImages = configuration.taskExecutorForCachedImages;
  
  		taskDistributor = DefaultConfigurationFactory.createTaskDistributor();
  	}
  
  	/** Submits task to execution pool */
  	void submit(final LoadAndDisplayImageTask task) {
  		taskDistributor.execute(new Runnable() {
  			@Override
  			public void run() {
  				File image = configuration.diskCache.get(task.getLoadingUri());
  				boolean isImageCachedOnDisk = image != null && image.exists();
  				initExecutorsIfNeed();
  				if (isImageCachedOnDisk) {
  					taskExecutorForCachedImages.execute(task);
  				} else {
  					taskExecutor.execute(task);
  				}
  			}
  		});
  	}
  
  	/** Submits task to execution pool */
  	void submit(ProcessAndDisplayImageTask task) {
  		initExecutorsIfNeed();
  		taskExecutorForCachedImages.execute(task);
  	}
  
  	private void initExecutorsIfNeed() {
  		if (!configuration.customExecutor && ((ExecutorService) taskExecutor).isShutdown()) {
  			taskExecutor = createTaskExecutor();
  		}
  		if (!configuration.customExecutorForCachedImages && ((ExecutorService) taskExecutorForCachedImages)
  				.isShutdown()) {
  			taskExecutorForCachedImages = createTaskExecutor();
  		}
  	}
  
  	private Executor createTaskExecutor() {
  		return DefaultConfigurationFactory
  				.createExecutor(configuration.threadPoolSize, configuration.threadPriority,
  				configuration.tasksProcessingType);
  	}
  
  	/**
  	 * Returns URI of image which is loading at this moment into passed {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}
  	 */
  	String getLoadingUriForView(ImageAware imageAware) {
  		return cacheKeysForImageAwares.get(imageAware.getId());
  	}
  
  	/**
  	 * Associates <b>memoryCacheKey</b> with <b>imageAware</b>. Then it helps to define image URI is loaded into View at
  	 * exact moment.
  	 */
  	void prepareDisplayTaskFor(ImageAware imageAware, String memoryCacheKey) {
  		cacheKeysForImageAwares.put(imageAware.getId(), memoryCacheKey);
  	}
  
  	/**
  	 * Cancels the task of loading and displaying image for incoming <b>imageAware</b>.
  	 *
  	 * @param imageAware {@link com.nostra13.universalimageloader.core.imageaware.ImageAware} for which display task
  	 *                   will be cancelled
  	 */
  	void cancelDisplayTaskFor(ImageAware imageAware) {
  		cacheKeysForImageAwares.remove(imageAware.getId());
  	}
  
  	/**
  	 * Denies or allows engine to download images from the network.<br /> <br /> If downloads are denied and if image
  	 * isn't cached then {@link ImageLoadingListener#onLoadingFailed(String, View, FailReason)} callback will be fired
  	 * with {@link FailReason.FailType#NETWORK_DENIED}
  	 *
  	 * @param denyNetworkDownloads pass <b>true</b> - to deny engine to download images from the network; <b>false</b> -
  	 *                             to allow engine to download images from network.
  	 */
  	void denyNetworkDownloads(boolean denyNetworkDownloads) {
  		networkDenied.set(denyNetworkDownloads);
  	}
  
  	/**
  	 * Sets option whether ImageLoader will use {@link FlushedInputStream} for network downloads to handle <a
  	 * href="http://code.google.com/p/android/issues/detail?id=6066">this known problem</a> or not.
  	 *
  	 * @param handleSlowNetwork pass <b>true</b> - to use {@link FlushedInputStream} for network downloads; <b>false</b>
  	 *                          - otherwise.
  	 */
  	void handleSlowNetwork(boolean handleSlowNetwork) {
  		slowNetwork.set(handleSlowNetwork);
  	}
  
  	/**
  	 * Pauses engine. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.<br
  	 * /> Already running tasks are not paused.
  	 */
  	void pause() {
  		paused.set(true);
  	}
  
  	/** Resumes engine work. Paused "load&display" tasks will continue its work. */
  	void resume() {
  		paused.set(false);
  		synchronized (pauseLock) {
  			pauseLock.notifyAll();
  		}
  	}
  
  	/**
  	 * Stops engine, cancels all running and scheduled display image tasks. Clears internal data.
  	 * <br />
  	 * <b>NOTE:</b> This method doesn't shutdown
  	 * {@linkplain com.nostra13.universalimageloader.core.ImageLoaderConfiguration.Builder#taskExecutor(java.util.concurrent.Executor)
  	 * custom task executors} if you set them.
  	 */
  	void stop() {
  		if (!configuration.customExecutor) {
  			((ExecutorService) taskExecutor).shutdownNow();
  		}
  		if (!configuration.customExecutorForCachedImages) {
  			((ExecutorService) taskExecutorForCachedImages).shutdownNow();
  		}
  
  		cacheKeysForImageAwares.clear();
  		uriLocks.clear();
  	}
  
  	void fireCallback(Runnable r) {
  		taskDistributor.execute(r);
  	}
  
  	ReentrantLock getLockForUri(String uri) {
  		ReentrantLock lock = uriLocks.get(uri);
  		if (lock == null) {
  			lock = new ReentrantLock();
  			uriLocks.put(uri, lock);
  		}
  		return lock;
  	}
  
  	AtomicBoolean getPause() {
  		return paused;
  	}
  
  	Object getPauseLock() {
  		return pauseLock;
  	}
  
  	boolean isNetworkDenied() {
  		return networkDenied.get();
  	}
  
  	boolean isSlowNetwork() {
  		return slowNetwork.get();
  	}
  }
  