package com.stanfy.enroscar.images;

import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.NinePatch;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;

import com.stanfy.enroscar.beans.BeansContainer;
import com.stanfy.enroscar.beans.EnroscarBean;
import com.stanfy.enroscar.beans.InitializingBean;
import com.stanfy.enroscar.images.cache.ImageMemoryCache;
import com.stanfy.enroscar.io.BuffersPool;
import com.stanfy.enroscar.net.cache.EnhancedResponseCache;

import java.io.IOException;
import java.net.ResponseCache;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

/**
 * A manager that encapsulates the images downloading and caching logic.
 * @author Roman Mazur - Stanfy (http://www.stanfy.com)
 */
@EnroscarBean(value = ImagesManager.BEAN_NAME, contextDependent = true)
public class ImagesManager implements InitializingBean {

  /** Bean name. */
  public static final String BEAN_NAME = "enroscar.ImagesManager";

  /** Images cache bean name. */
  public static final String CACHE_BEAN_NAME = "enroscar.ImagesCache";

  /** Image consumer factory bean name. */
  public static final String IMAGE_CONSUMER_FACTORY_NAME = "enroscar.ViewImageConsumerFactory";

  /** UrlConnectionBuilderFactory bean name. */
  public static final String CONNECTION_BUILDER_FACTORY_NAME = "enroscar.ImagesUrlConnectionBuilderFactory";

  /** Logging tag. */
  static final String TAG = BEAN_NAME;

  /** Max distance between sample factor and its nearest power of 2 to use the latter. */
  static final int MAX_POWER_OF_2_DISTANCE = 3;

  /** Current loads. */
  // TODO make private
  final ConcurrentHashMap<String, ImageLoader> currentLoads = new ConcurrentHashMap<String, ImageLoader>(Threading.imagesWorkersCount);

  /** Application context. */
  private final Context context;

  /** Buffers pool. */
  private BuffersPool buffersPool;
  /** Memory cache. */
  private ImageMemoryCache memCache;
  /** Images response cache. */
  private EnhancedResponseCache imagesResponseCache;
  /** Consumer factory. */
  private ViewImageConsumerFactory consumerFactory;

  /** Paused state. */
  private boolean paused = false;

  /** Debug flag. */
  boolean debug = false;

  public ImagesManager(final Context context) {
    this.context = context.getApplicationContext();
  }

  /**
   * Set count of working threads used to load images.
   * @param count count of threads
   */
  public static void configureImageTaskExecutorsCount(final int count) {
    Threading.configureImageTasksExecutor(count);
  }

  /** @return application context */
  public Context getContext() {
    return context;
  }

  /** @return application resources */
  public Resources getResources() {
    return context.getResources();
  }

  /**
   * Set verbose logging flag.
   * @param value debug flag value
   */
  public void setDebug(final boolean value) {
    this.debug = value;
  }

  BuffersPool getBuffersPool() { return buffersPool; }

  @Override
  public void onInitializationFinished(final BeansContainer beansContainer) {
    this.buffersPool = beansContainer.getBean(BuffersPool.class.getName(), BuffersPool.class);
    this.memCache = beansContainer.getBean(ImageMemoryCache.BEAN_NAME, ImageMemoryCache.class);
    if (buffersPool == null || memCache == null) {
      throw new IllegalStateException("Buffers pool and images memory cache must be initialized before images manager.");
    }

    this.imagesResponseCache = (EnhancedResponseCache) beansContainer.getBean(CACHE_BEAN_NAME, ResponseCache.class);
    if (imagesResponseCache == null) {
      Log.w(TAG, "Response cache for images is not defined");
    }

    this.consumerFactory = beansContainer.getBean(IMAGE_CONSUMER_FACTORY_NAME, ViewImageConsumerFactory.class);

    if (!beansContainer.containsBean(CONNECTION_BUILDER_FACTORY_NAME)) {
      throw new IllegalStateException("Images connection builder factory is not defined: bean ImagesManager.CONNECTION_BUILDER_FACTORY_NAME ("
          + CONNECTION_BUILDER_FACTORY_NAME + ")");
    }
  }

  /** @return images response cache instance */
  public ResponseCache getImagesResponseCache() { return (ResponseCache)imagesResponseCache; }

  public boolean isPresentOnDisk(final String url) {
    return url.startsWith(ContentResolver.SCHEME_FILE)
        || url.startsWith(ContentResolver.SCHEME_CONTENT)
        || url.startsWith(ContentResolver.SCHEME_ANDROID_RESOURCE)
        || imagesResponseCache.contains(url);
  }

  /**
   * @param images list of requests to load
   * @param executor executor to run the task (if null task is solved in the current thread)
   */
  public void ensureImages(final List<ImageRequest> images, final Executor executor) {
    final Runnable task = new Runnable() {
      @Override
      public void run() {
        for (final ImageRequest request : images) {
          try {
            request.storeToDisk();
          } catch (final IOException e) {
            if (debug) { Log.e(TAG, "IO error for " + request.url + ": " + e.getMessage()); }
          } catch (final Exception e) {
            Log.e(TAG, "Ignored error for ensureImages", e);
          }
        }
      }
    };

    if (executor == null) {
      task.run();
    } else {
      executor.execute(task);
    }
  }

  /**
   * Pause all future loading tasks.
   */
  public final synchronized void pauseLoading() {
    this.paused = true;
  }
  /**
   * Resume all the loading tasks.
   */
  public final synchronized void resumeLoading() {
    this.paused = false;
    notifyAll();
  }

  /**
   * Start building image requests.
   * @return image requests builder
   */
  public ImageRequestsBuilder load() {
    return new ImageRequestsBuilder(this);
  }

  /**
   * Clear the cached entities.
   * @param url image URL
   * @return true if entry was deleted
   */
  public boolean clearCache(final String url) {
    memCache.remove(url);
    try {
      return imagesResponseCache.deleteGetEntry(url);
    } catch (final IOException e) {
      Log.w(TAG, "Cannot clear disk cache for " + url, e);
      return false;
    }
  }

  /**
   * Populate the requested image to the specified view. Called from the GUI thread.
   * @param view view instance
   * @param url image URL
   */
  public void populateImage(final View view, final String url) {
    final Object tag = view.getTag();
    ImageConsumer consumer = null;
    if (tag == null) {
      consumer = createImageConsumer(view);
      view.setTag(consumer);
    } else {
      if (!(tag instanceof ImageConsumer)) {
        throw new IllegalStateException("View already has a tag " + tag + ". Cannot store consumer");
      }
      consumer = (ImageConsumer)tag;
    }
    populateImage(consumer, url);
  }

  /**
   * Cancel image loading for a view.
   * @param view view that hold an image
   */
  public void cancelImageLoading(final View view) {
    checkThread();
    final Object tag = view.getTag();
    if (tag != null && tag instanceof ImageConsumer) {
      cancelImageLoading((ImageConsumer)tag);
    }
  }
  /**
   * Cancel image loading for a holder.
   * @param holder image holder instance
   */
  public void cancelImageLoading(final ImageConsumer holder) {
    checkThread();
    holder.cancelCurrentLoading();
  }

  /**
   * Create an image holder instance for the defined view.
   * @param view view instance
   * @return image holder instance
   */
  protected ImageConsumer createImageConsumer(final View view) {
    if (this.consumerFactory == null) {
      throw new IllegalStateException("Image consumers factory bean not found in container. Take a look at DefaultBeansManager.edit().images() method in assist package.");
    }
    return consumerFactory.createConsumer(view);
  }

  /**
   * @param url image URL
   * @return true if image is cached in memory
   */
  public boolean isMemCached(final String url) { return memCache.contains(url); }

  /**
   * @param url image URL
   * @param view that contains an image holder
   * @return image bitmap from memory cache
   */
  public Bitmap getMemCached(final String url, final View view) {
    checkThread();
    Object tag = view.getTag();
    if (tag == null) {
      tag = createImageConsumer(view);
    }
    if (!(tag instanceof ImageConsumer)) { throw new IllegalStateException("View already has a tag"); }
    return getMemCached(url, (ImageConsumer) tag);
  }

  public Bitmap getMemCached(final String url, final ImageConsumer consumer) {
    ImageRequest request = createImageRequest(url, consumer);
    final ImageResult res = getFromMemCache(request, consumer);
    if (res == null) { return null; }
    decorateResult(consumer, res);
    return res.getBitmap();
  }

  private ImageRequest createImageRequest(String url, ImageConsumer consumer) {
    ImageRequest request = new ImageRequest(this, url, -1);
    consumer.prepareImageRequest(request);
    return request;
  }

  @SuppressWarnings("ConstantConditions")
  public void populateImage(final ImageConsumer consumer, final String url) {
    if (consumer.isMatchingParentButNotMeasured()) {

      if (consumer instanceof ViewImageConsumer) {
        final View view = ((ViewImageConsumer) consumer).getView();
        view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
          @SuppressWarnings("deprecation")
          @Override
          public void onGlobalLayout() {
            view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            populateImageNow(consumer, url);
          }
        });
        return;
      }

      consumer.post(new Runnable() {
        @Override
        public void run() {
          populateImageNow(consumer, url);
        }
      });
    } else {
      populateImageNow(consumer, url);
    }
  }

  private void checkThread() {
    if (Looper.myLooper() != Looper.getMainLooper()) {
      throw new IllegalStateException("populateImageNow must be called from the main thread");
    }
  }

  public void populateImageNow(final ImageConsumer consumer, final String url) {
    checkThread();
    if (debug) { Log.d(TAG, "Process url " + url); }
    if (TextUtils.isEmpty(url)) {
      setLoadingImage(consumer);
      return;
    }

    consumer.cancelCurrentLoading();

    ImageRequest request = createImageRequest(url, consumer);

    final ImageResult result = getFromMemCache(request, consumer);
    if (result != null) {
      consumer.onStart(null, url);
      setResultImage(consumer, result, false);
      consumer.onFinish(url, result);
      return;
    }

    if (debug) { Log.d(TAG, "Set loading for " + request.getKey()); }
    setLoadingImage(consumer);
    startImageLoaderTask(consumer, request);
  }

  /**
   * @return an executor for image tasks
   */
  protected Executor getImageTaskExecutor() { return Threading.getImageTasksExecutor(); }

  /**
   * @param holder image consumer
   * @return drawable to display while image is being loaded
   */
  protected Drawable getLoadingDrawable(final ImageConsumer holder) {
    final Drawable d = holder.getLoadingImage();
    final int color = 0xffeeeeee;
    return d != null ? d : new ColorDrawable(color);
  }

  /**
   * @param consumer image consumer
   * @param result result instance
   */
  protected void decorateResult(final ImageConsumer consumer, final ImageResult result) {
    // nothing
  }

  private Drawable createDrawable(final Bitmap bitmap) {
    // same logic as private Drawable.drawableFromBitmap(...)
    final byte[] npc = bitmap.getNinePatchChunk();
    if (npc != null && NinePatch.isNinePatchChunk(npc)) {
      return new NinePatchDrawable(getResources(), new NinePatch(bitmap, npc, null));
    }
    return new BitmapDrawable(getResources(), bitmap);
  }

  /**
   * It must be executed in the main thread.
   * @param consumer image consumer
   * @param result result instance
   * @param animate whether to animate image change
   */
  protected final void setResultImage(final ImageConsumer consumer, final ImageResult result, final boolean animate) {
    decorateResult(consumer, result);
    consumer.setImage(createDrawable(result.getBitmap()), animate);
    consumer.reset();
  }

  /**
   * Set preloader.
   * @param consumer image consumer instance
   */
  private void setLoadingImage(final ImageConsumer consumer) {
    if (!consumer.skipLoadingImage()) {
      Drawable d = getLoadingDrawable(consumer);
      if (!consumer.hasUndefinedSize() || (d.getIntrinsicWidth() != 0 && d.getIntrinsicHeight() != 0)) {
        consumer.setLoadingImage(d);
      }
    }
  }

  /**
   * @param request image request
   * @param consumer image consumer (to get target size)
   * @return cached drawable
   */
  protected ImageResult getFromMemCache(final ImageRequest request, final ImageConsumer consumer) {
    ImageLoader loader = consumer.currentLoader;
    String cacheKey = request.getCacheKey();
    if (loader != null && !loader.request.getCacheKey().equals(cacheKey)) {
      return null;
    }

    final Bitmap map = memCache.getElement(cacheKey);
    if (map == null) {
      if (debug) { Log.v(TAG, "Not in mem " + cacheKey); }
      return null;
    }

    // check bitmap size
    final boolean suits = consumer.allowSmallImagesFromCache() || consumer.checkBitmapSize(map);
    if (debug) { Log.v(TAG, "Use mem cache " + suits + " for " + cacheKey); }
    return suits ? new ImageResult(map, ImageSourceType.MEMORY) : null;
  }

  /**
   * Executed in the main thread.
   * @param request image request
   * @param consumer image holder to process
   */
  private void startImageLoaderTask(final ImageConsumer consumer, final ImageRequest request) {
    final String key = request.getKey();
    if (debug) { Log.d(TAG, "Key " + key); }

    ImageLoader loader = currentLoads.get(key);
    if (loader != null) {
      final boolean added = loader.addTarget(consumer);
      if (!added) { loader = null; }
    }

    if (loader == null) {

      if (debug) { Log.d(TAG, "Start a new task"); }
      loader = new ImageLoader(request, this);
      if (!loader.addTarget(consumer)) {
        throw new IllegalStateException("Cannot add target to the new loader");
      }

      currentLoads.put(key, loader);
      if (debug) { Log.d(TAG, "Current loaders count: " + currentLoads.size()); }
      final Executor executor = getImageTaskExecutor();
      executor.execute(loader.future);

    } else if (debug) {
      Log.d(TAG, "Joined to the existing task " + key);
    }
  }

  /**
   * Add image to memory cache.
   * @param url image URL
   * @param bitmap bitmap
   */
  protected void memCacheImage(final String url, final Bitmap bitmap) {
    if (debug) { Log.d(TAG, "Memcache for " + url); }
    memCache.putElement(url, bitmap);
  }

  static int calculateSampleFactor(final int inW, final int inH, final int width, final int height) {
    if (inW <= width && inH <= height) {
      return 1;
    }
    if (width == 0 && height == 0) {
      return 1;
    }

    final int factor;
    if (width == 0) {
      factor = inH / height;
    } else if (height == 0) {
      factor = inW / width;
    } else {
      factor = inW > inH ? inW / width : inH / height;
    }

    return factor;
  }

  /**
   * This our barrier for images loading tasks.
   * @return true if task can continue it's work and false if it's interrupted
   */
  final synchronized boolean waitForPause() {
    try {
      while (paused) { wait(); }
      return true;
    } catch (final InterruptedException e) {
      return false;
    }
  }

}


package com.stanfy.enroscar.rest.response.handler;

import android.content.Context;
import android.util.Log;

import com.stanfy.enroscar.beans.BeansContainer;
import com.stanfy.enroscar.beans.InitializingBean;
import com.stanfy.enroscar.io.BuffersPool;
import com.stanfy.enroscar.io.IoUtils;
import com.stanfy.enroscar.net.ContentControlUrlConnection;
import com.stanfy.enroscar.net.UrlConnectionWrapper;
import com.stanfy.enroscar.rest.EntityTypeToken;
import com.stanfy.enroscar.rest.Utils;
import com.stanfy.enroscar.net.operation.RequestDescription;
import com.stanfy.enroscar.rest.response.Model;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.ContentHandler;
import java.net.HttpURLConnection;
import java.net.URLConnection;
import java.nio.charset.Charset;

/**
 * Base content handler. Takes care about buffers and gzip.
 * @author Roman Mazur (Stanfy - http://stanfy.com)
 */
public abstract class BaseContentHandler extends ContentHandler implements InitializingBean {

  /** Logging tag. */
  private static final String TAG = RequestDescription.TAG;

  /** Default date format. */
  public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss Z";

  /** Buffers pool. */
  private BuffersPool buffersPool;

  /** Content characters set. */
  private Charset charset = IoUtils.UTF_8;

  /** Context instance. */
  private final Context context;

  public BaseContentHandler(final Context context) {
    this.context = context;
  }

  @Override
  public final Object getContent(final URLConnection uConn) throws IOException {
    final ContentControlUrlConnection connection = ContentControlUrlConnection.from(uConn);
    if (connection == null) {
      throw new IllegalArgumentException("Connection is not wrapped with " + ContentControlUrlConnection.class);
    }

    // try to get input stream
    InputStream responseStream = null;
    try {
      responseStream = connection.getInputStream();
    } catch (final IOException responseStreamException) {
      if (Utils.isDebugRest(context)) {
        Log.v(TAG, "Cannot get input stream, message: " + responseStreamException.getMessage() + ", try to use error stream");
      }

      final URLConnection orig = UrlConnectionWrapper.unwrap(connection);
      if (orig instanceof HttpURLConnection) {
        responseStream = ((HttpURLConnection) orig).getErrorStream();
      }

      // no error stream?
      if (responseStream == null) {
        throw responseStreamException;
      }
    }

    // we have input => wrap it for reading

    InputStream source = IoUtils.getUncompressedInputStream(
        connection.getContentEncoding(),
        buffersPool.bufferize(responseStream)
    );

    if (Utils.isDebugRestResponse(context)) {
      final String responseString = IoUtils.streamToString(source, buffersPool); // source is now closed, don't worry
      Log.d(TAG, responseString);
      source = new ByteArrayInputStream(responseString.getBytes(IoUtils.UTF_8_NAME));
    }

    try {
      return getContent(connection, source, connection.getEntityType());
    } finally {
      // do not forget to close the source
      IoUtils.closeQuietly(source);
    }
  }

  /**
   * Implementation must read the input stream and return an object of type specified by the token.
   * In order to get a Java type from the model type token, method {@link #getModelType(com.stanfy.enroscar.rest.EntityTypeToken)} may be used.
   * @param connection connection instance
   * @param source input stream for the connection
   * @param modelType model type token
   * @return object of type described by the model type token
   * @throws IOException if an I/O error happpens
   */
  protected abstract Object getContent(final URLConnection connection, final InputStream source, final EntityTypeToken modelType) throws IOException;

  /**
   * Checks the raw type provided by the token an presence of {@link Model} annotation on it.
   * @param modelType model type token
   * @return Java type
   */
  protected Type getModelType(final EntityTypeToken modelType) {
    final Class<?> modelClass = modelType.getRawClass();

    // check for wrappers
    final Model modelAnnotation = modelClass.getAnnotation(Model.class);
    if (modelAnnotation == null) { return modelType.getType(); }

    final Class<?> wrapper = modelAnnotation.wrapper();
    return wrapper != null && wrapper != Model.class ? wrapper : modelType.getType();
  }

  public void setCharset(final Charset charset) { this.charset = charset; }
  public Charset getCharset() { return charset; }

  protected BuffersPool getBuffersPool() {
    return buffersPool;
  }

  @Override
  public void onInitializationFinished(final BeansContainer beansContainer) {
    this.buffersPool = beansContainer.getBean(BuffersPool.class);
  }

}


package com.stanfy.enroscar.io;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * A pool of arrays that might be used to decode images or perform other IO operations.
 * @author Roman Mazur - Stanfy (http://www.stanfy.com)
 */
public class BuffersPool {

  /** Default pool configuration. */
  private static final int[][] DESCRIPTION_DEFAULT = {
      {4, IoUtils.BUFFER_SIZE_16K}, {2, IoUtils.BUFFER_SIZE_8K}
  };

  /** Buffers store. */
  private final TreeMap<Integer, List<Object>> buffers = new TreeMap<Integer, List<Object>>();

  /** Protects {@link #buffers}. */
  private final Object lock = new Object();

  /** Stats counter. */
  private int usedBuffersCount, allocatedBuffersCount;

  /** Strict mode flag. */
  boolean strictMode = true;

  public BuffersPool() {
    this(DESCRIPTION_DEFAULT);
  }

  public BuffersPool(final int[][] initDescription) {
    for (int i = initDescription.length - 1; i >= 0; i--) {
      int count = initDescription[i][0];
      int amount = initDescription[i][1];

      for (int k = count - 1; k >= 0; k--) {
        allocatedBuffersCount++;
        release(allocate(amount));
      }

    }
    usedBuffersCount = 0;
  }

  private static byte[] allocate(final int size) {
    return new byte[size];
  }

  /**
   * @return buffer with default width
   */
  public byte[] get() {
    return get(IoUtils.BUFFER_SIZE_8K);
  }

  /**
   * @param minCapacity minimal capacity of the buffer
   * @return buffer with length greater on equal than minCapacity
   */
  public byte[] get(final int minCapacity) {
    synchronized (lock) {
      usedBuffersCount++;

      final SortedMap<Integer, List<Object>> map = buffers.tailMap(minCapacity);
      if (map.isEmpty()) {
        allocatedBuffersCount++;
        return allocate(minCapacity);
      }

      final List<Object> bList = map.get(map.firstKey());
      if (bList == null || bList.isEmpty()) {
        allocatedBuffersCount++;
        return allocate(minCapacity);
      }

      return (byte[])bList.remove(0);
    }
  }

  /**
   * Recycle the buffer.
   * @param buffer unused buffer
   */
  public void release(final byte[] buffer) {
    if (buffer == null) {
      return;
    }

    final int capacity = buffer.length;
    if (capacity == 0) {
      return;
    }

    synchronized (lock) {
      List<Object> bList = buffers.get(capacity);
      if (bList == null) {
        bList = new LinkedList<Object>();
        buffers.put(capacity, bList);
      }
      bList.add(buffer);

      usedBuffersCount--;
    }

  }

  /**
   * In strict mode streams obtained with {@code bufferize} write warnings to {@link System#err}
   * about unreleased buffers.
   * @param strictMode strict mode enabled flag
   */
  public void setStrictMode(boolean strictMode) {
    this.strictMode = strictMode;
  }

  public int getAllocatedBuffersCount() {
    synchronized (lock) {
      return allocatedBuffersCount;
    }
  }

  public int getUsedBuffersCount() {
    synchronized (lock) {
      return usedBuffersCount;
    }
  }

  int getBuffersMapSize() {
    synchronized (lock) {
      return buffers.size();
    }
  }

  /**
   * Clear all the retained buffers to free resources.
   * May be used in low memory conditions.
   */
  public void flush() {
    synchronized (lock) {
      buffers.clear();
    }
  }

  /**
   * Wrap input stream into a buffered implementation using this buffers pool.
   * @param input input stream to wrap
   * @return buffered input stream
   */
  public InputStream bufferize(final InputStream input) {
    return bufferize(input, IoUtils.BUFFER_SIZE_8K);
  }

  /**
   * Wrap output stream into a buffered implementation using this buffers pool.
   * @param output output stream to wrap
   * @return buffered output stream
   */
  public OutputStream bufferize(final OutputStream output) {
    return bufferize(output, IoUtils.BUFFER_SIZE_8K);
  }

  /**
   * Wrap input stream into a buffered implementation using this buffers pool.
   * @param input input stream to wrap
   * @param bufferSize buffer size
   * @return buffered input stream
   */
  public InputStream bufferize(final InputStream input, final int bufferSize) {
    return new PoolableBufferedInputStream(input, bufferSize, this);
  }

  /**
   * Wrap output stream into a buffered implementation using this buffers pool.
   * @param output output stream to wrap
   * @param bufferSize buffer size
   * @return buffered output stream
   */
  public OutputStream bufferize(final OutputStream output, final int bufferSize) {
    return new PoolableBufferedOutputStream(output, bufferSize, this);
  }

}


package com.stanfy.enroscar.net.operation;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;

import com.stanfy.enroscar.beans.BeansManager;
import com.stanfy.enroscar.io.IoUtils;
import com.stanfy.enroscar.rest.EntityTypeToken;
import com.stanfy.enroscar.rest.request.binary.BinaryData;
import com.stanfy.enroscar.rest.request.net.BaseRequestDescriptionConverter;
import com.stanfy.enroscar.rest.request.net.BaseRequestDescriptionConverter.ConverterFactory;
import com.stanfy.enroscar.rest.request.net.PayloadPostConverter;
import com.stanfy.enroscar.rest.request.net.SimpleGetConverter;
import com.stanfy.enroscar.rest.request.net.SimplePostConverter;
import com.stanfy.enroscar.rest.request.net.UploadPostConverter;
import com.stanfy.enroscar.rest.response.Model;

import java.io.IOException;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.LinkedList;

/**
 * Request method description. This object is passed to the service describing the request.
 * @author Roman Mazur - Stanfy (http://www.stanfy.com)
 */
public class RequestDescription implements Parcelable {

  /** Default name for binary content. */
  public static final String BINARY_NAME_DEFAULT = "content";

  /** ID counter. */
  private static int idCounter = 0;

  /** Logging tag. */
  public static final String TAG = "ReqDesc";

  /** Creator. */
  public static final Creator<RequestDescription> CREATOR = new Creator<RequestDescription>() {
    @Override
    public RequestDescription createFromParcel(final Parcel source) { return new RequestDescription(source); }
    @Override
    public RequestDescription[] newArray(final int size) { return new RequestDescription[size]; }
  };

  /** Converters to {@link URLConnection}. */
  private static final SparseArray<ConverterFactory> CONVERTER_FACTORIES = new SparseArray<>(3);
  static {
    registerConverterFactory(OperationType.SIMPLE_GET, SimpleGetConverter.FACTORY);
    registerConverterFactory(OperationType.SIMPLE_POST, SimplePostConverter.FACTORY);
    registerConverterFactory(OperationType.UPLOAD_POST, UploadPostConverter.FACTORY);
    registerConverterFactory(OperationType.PAYLOAD_POST, PayloadPostConverter.FACTORY);
  }

  /** Request ID. */
  final int id;

  /** Operation type. */
  int operationType = OperationType.SIMPLE_GET;

  /** URL part. */
  String url;
  /** Cache instance name. */
  String cacheName;
  /** Simple parameters. */
  ParametersGroup simpleParameters;

  /** Content type. */
  String contentType;
  /** Request data encoding. */
  Charset encoding = IoUtils.UTF_8;
  /** Content language. */
  String contentLanguage;

  /** Binary data array. */
  ArrayList<BinaryData<?>> binaryData;

  /** Whether request should be performed in parallel. */
  boolean parallelMode = false;

  /** Canceled state flag. */
  volatile boolean canceled = false;

  /** Model class. */
  EntityTypeToken modelType;
  /** Content handler name. */
  String contentHandler;
  /** Content analyzer. */
  String contentAnalyzer;

  /** Task queue name. */
  String taskQueueName;

  /** Statistics tag. */
  int statsTag;

  /** Request headers. */
  private Bundle headers;

  /**
   * Create with predefined ID.
   * @param id request ID
   */
  public RequestDescription(final int id) {
    this.id = id;
  }

  /**
   * Create new description with new request ID.
   */
  public RequestDescription() {
    this(nextId());
  }

  /**
   * Create from parcel.
   * @param source source parcel
   */
  protected RequestDescription(final Parcel source) {
    this(source.readInt());
    final ClassLoader cl = getClass().getClassLoader();

    this.operationType = source.readInt();
    this.url = source.readString();
    this.cacheName = source.readString();
    this.simpleParameters = source.readParcelable(cl);
    this.contentType = source.readString();
    this.encoding = Charset.forName(source.readString());
    this.contentLanguage = source.readString();

    this.parallelMode = source.readInt() == 1;
    this.taskQueueName = source.readString();

    this.canceled = source.readInt() == 1;

    this.modelType = source.readParcelable(cl);
    this.contentHandler = source.readString();
    this.contentAnalyzer = source.readString();

    // binary content fields
    final Parcelable[] binary = source.readParcelableArray(cl);
    if (binary != null) {
      this.binaryData = new ArrayList<>(binary.length);
      for (Parcelable b : binary) {
        this.binaryData.add((BinaryData<?>) b);
      }
    }

    this.statsTag = source.readInt();

    this.headers = source.readBundle(cl);
  }

  public static void registerConverterFactory(final int opertationType, final ConverterFactory factory) {
    CONVERTER_FACTORIES.put(opertationType, factory);
  }

  private static synchronized int nextId() {
    ++idCounter;
    if (idCounter < 0) { idCounter = 1; }
    return idCounter;
  }

  public static String getParamValue(final String name, final LinkedList<Parameter> param) {
    for (final Parameter p : param) {
      if (p instanceof ParameterValue && name.equals(p.getName())) {
        return ((ParameterValue)p).getValue();
      }
    }
    return null;
  }

  @Override
  public void writeToParcel(final Parcel dest, final int flags) {
    dest.writeInt(id);
    dest.writeInt(operationType);
    dest.writeString(url);
    dest.writeString(cacheName);
    dest.writeParcelable(simpleParameters, flags);
    dest.writeString(contentType);
    dest.writeString(encoding.name());
    dest.writeString(contentLanguage);

    dest.writeInt(parallelMode ? 1 : 0);
    dest.writeString(taskQueueName);

    dest.writeInt(canceled ? 1 : 0);

    dest.writeParcelable(modelType, 0);
    dest.writeString(contentHandler);
    dest.writeString(contentAnalyzer);

    // binary content fields
    if (binaryData != null) {
      final BinaryData<?>[] binary = new BinaryData<?>[binaryData.size()];
      dest.writeParcelableArray(binaryData.toArray(binary), flags);
    } else {
      dest.writeParcelableArray(null, flags);
    }

    dest.writeInt(statsTag);

    dest.writeBundle(headers);
  }

  @Override
  public boolean equals(final Object o) {
    if (!(o instanceof RequestDescription)) { return false; }
    return id == ((RequestDescription)o).id;
  }

  @Override
  public int hashCode() { return id; }

  @Override
  public final int describeContents() {
    final ArrayList<BinaryData<?>> binaryData = this.binaryData;
    if (binaryData == null) { return 0; }
    final int count = binaryData.size();
    int result = 0;
    for (int i = 0; i < count; i++) {
      result |= binaryData.get(i).describeContents();
    }
    return result;
  }

  /** @return request identifier */
  public final int getId() { return id; }

  /** @param operationType operation type */
  public void setOperationType(final int operationType) { this.operationType = operationType; }
  /** @return the operationType */
  public int getOperationType() { return operationType; }

  /** @return the contentLanguage */
  public String getContentLanguage() { return contentLanguage; }
  /** @param contentLanguage the contentLanguage to set */
  public void setContentLanguage(final String contentLanguage) { this.contentLanguage = contentLanguage; }
  /** @return the contentType */
  public String getContentType() { return contentType; }
  /** @param contentType the contentType to set */
  public void setContentType(final String contentType) { this.contentType = contentType; }
  /** @return URL */
  public String getUrl() { return url; }
  /** @param url URL to set */
  public void setUrl(final String url) { this.url = url; }
  /** @param encoding request encoding */
  public void setEncoding(final Charset encoding) { this.encoding = encoding; }
  /** @return request encoding */
  public Charset getEncoding() { return encoding; }

  /** @param typeToken type token the response model */
  public void setModelType(final EntityTypeToken typeToken) { this.modelType = typeToken; }
  /** @return type token the response model */
  public EntityTypeToken getModelType() { return modelType; }

  private void checkBeanExists(final String name) {
    if (name != null && !BeansManager.get(null).getContainer().containsBean(name)) {
      throw new IllegalArgumentException("Bean " + name + " is not registered");
    }
  }

  /** @param cacheName cache manager name */
  public void setCacheName(final String cacheName) {
    checkBeanExists(cacheName);
    this.cacheName = cacheName;
  }
  /** @return cache manager name */
  public String getCacheName() { return cacheName; }
  /** @param contentHandler content handler name */
  public void setContentHandler(final String contentHandler) {
    checkBeanExists(contentHandler);
    this.contentHandler = contentHandler;
  }
  /** @return content handler name */
  public String getContentHandler() { return contentHandler; }
  /** @param contentAnalyzer content analyzer bean name */
  public void setContentAnalyzer(final String contentAnalyzer) {
    checkBeanExists(contentAnalyzer);
    this.contentAnalyzer = contentAnalyzer;
  }
  /** @return content analyzer bean name */
  public String getContentAnalyzer() {
    synchronized (this) {
      if (contentAnalyzer == null && modelType != null) {
        final Model modelAnnotation = modelType.getRawClass().getAnnotation(Model.class);
        if (modelAnnotation != null) {
          final String analyzer = modelAnnotation.analyzer();
          if (analyzer.length() > 0) { contentAnalyzer = analyzer; }
        }
      }
    }
    return contentAnalyzer;
  }

  public void setCanceled(final boolean canceled) { this.canceled = canceled; }
  public boolean isCanceled() { return canceled; }

  public ArrayList<BinaryData<?>> getBinaryData() { return binaryData; }
  public void addBinaryData(final BinaryData<?> bdata) {
    if (binaryData == null) { binaryData = new ArrayList<BinaryData<?>>(); }
    binaryData.add(bdata);
  }
  public void clearBinaryData() {
    if (binaryData != null) {
      binaryData.clear();
    }
  }

  /** @return whether request is simple. */
  public boolean isSimple() { return operationType == OperationType.SIMPLE_POST || operationType == OperationType.SIMPLE_GET; }

  /** @param parallelMode parallel mode flag */
  public void setParallelMode(final boolean parallelMode) { this.parallelMode = parallelMode; }
  /** @return parallel mode flag */
  public boolean isParallelMode() { return parallelMode; }

  /** @param taskQueueName task queue name */
  public void setTaskQueueName(final String taskQueueName) { this.taskQueueName = taskQueueName; }
  /** @return task queue name */
  public String getTaskQueueName() { return taskQueueName; }

  /** @return the simpleParameters */
  public ParametersGroup getSimpleParameters() { return simpleParameters; }

  /**
   * Set a tag that will be used as a parameter for {@link android.net.TrafficStats}.
   * @param statsTag tag value
   */
  public void setStatsTag(final int statsTag) {
    this.statsTag = statsTag;
  }

  /**
   * @return network traffic statistics tag
   */
  public int getStatsTag() {
    return statsTag;
  }

  /**
   * @param name request header name
   * @return request header value
   */
  public String getHeader(final String name) {
    return headers != null ? headers.getString(name) : null;
  }

  /**
   * @param name request header name
   * @param value request header value
   */
  public void addHeader(final String name, final String value) {
    if (this.headers == null) {
      this.headers = new Bundle();
    }
    this.headers.putString(name, value);
  }

  /**
   * @param name request header name
   */
  public void removeHeader(final String name) {
    if (this.headers != null) {
      this.headers.remove(name);
    }
  }

  /**
   * Remove all request headers.
   */
  public void clearHeaders() {
    this.headers = null;
  }

  // ============================ HTTP REQUESTS ============================

  /**
   * @param context context instance
   * @return user agent string
   */
  public static String buildUserAgent(final Context context) {
    if (context == null) { return null; }
    try {
      final PackageManager manager = context.getPackageManager();
      final StringBuilder agentString = new StringBuilder();
      agentString.append("Android client (").append(Build.VERSION.RELEASE).append(" / api ").append(Build.VERSION.SDK_INT).append("), ");
      if (manager != null) {
        final PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
        agentString.append(info.packageName).append("/").append(info.versionName)
            .append(" (").append(info.versionCode).append("), ").append(IoUtils.ENCODING_GZIP);
      } else {
        // test environment only
        agentString.append(" test");
      }
      return agentString.toString();
    } catch (final NameNotFoundException e) {
      return null;
    }
  }

  /**
   * A good place to set custom request headers.
   * @param context system context
   * @param urlConnection URL connection instance
   */
  protected void onURLConnectionPrepared(final Context context, final URLConnection urlConnection) {
    if (contentType != null) {
      urlConnection.addRequestProperty("Content-Type", contentType);
    }
    if (contentLanguage != null) {
      urlConnection.addRequestProperty("Accept-Language", contentLanguage);
    }
    urlConnection.addRequestProperty("Accept-Encoding", IoUtils.ENCODING_GZIP);
    urlConnection.addRequestProperty("User-Agent", buildUserAgent(context));

    if (headers != null) {
      for (String name : headers.keySet()) {
        urlConnection.addRequestProperty(name, headers.getString(name));
      }
    }
  }

  /**
   * Build {@link URLConnection} instance, connect, write request.
   * @param context context instance
   * @return {@link URLConnection} instance, ready for {@link URLConnection#getInputStream()} call
   * @throws IOException in case of I/O errors
   */
  public URLConnection makeConnection(final Context context) throws IOException {
    ConverterFactory factory = CONVERTER_FACTORIES.get(operationType);
    if (factory == null) {
      throw new IllegalArgumentException("Don't know how to convert operation type " + operationType);
    }
    final BaseRequestDescriptionConverter converter = factory.createConverter(this, context);

    // create instance
    final URLConnection connection = converter.prepareConnectionInstance();
    // setup headers
    onURLConnectionPrepared(context, connection);
    // make a connection
    converter.connect(connection);
    // send data, if required
    converter.sendRequest(connection);

    return connection;
  }

  @Override
  public String toString() {
    return "RequestDescription[id=" + id + ", url=" + url + "]";
  }

}


package com.stanfy.enroscar.net.operation;

import java.util.Locale;
import java.util.TreeMap;

import android.content.Context;

import com.stanfy.enroscar.beans.BeanUtils;
import com.stanfy.enroscar.net.operation.executor.RequestExecutor;
import com.stanfy.enroscar.rest.response.handler.GsonContentHandler;
import com.stanfy.enroscar.rest.response.handler.StringContentHandler;
import com.stanfy.enroscar.rest.response.handler.XmlGsonContentHandler;

/**
 * Simple request builder.
 * @param <MT> model type
 * @author Roman Mazur (Stanfy - http://stanfy.com)
 */
public abstract class SimpleRequestBuilder<MT> extends BaseRequestBuilder<MT> {

  /** Format. */
  public static final String JSON = "json", XML = "xml", STRING = "string";

  /** Content format mappings. */
  private static final TreeMap<String, String> FORMAT_MAPPINGS = new TreeMap<String, String>();
  static {
    FORMAT_MAPPINGS.put(JSON, GsonContentHandler.BEAN_NAME);
    FORMAT_MAPPINGS.put(XML, XmlGsonContentHandler.BEAN_NAME);
    FORMAT_MAPPINGS.put(STRING, StringContentHandler.BEAN_NAME);
  }

  public SimpleRequestBuilder(final Context context) {
    super(context);
  }

  /**
   * Set target URL.
   * @param url URL to set
   * @return this builder for chaining
   */
  public SimpleRequestBuilder<MT> setUrl(final String url) {
    setTargetUrl(url);
    return this;
  }

  /**
   * Set request operation type.
   * @param type request operation type
   * @return this builder for chaining
   */
  public SimpleRequestBuilder<MT> setOperationType(final int type) {
    setRequestOperationType(type);
    return this;
  }

  /**
   * @param name cache manager bean name
   * @return this builder for chaining
   */
  public SimpleRequestBuilder<MT> setCacheName(final String name) {
    setRequestCacheName(name);
    return this;
  }

  public SimpleRequestBuilder<MT> setFormat(final String format) {
    final String name = FORMAT_MAPPINGS.get(format.toLowerCase(Locale.US));
    setRequestContentHandler(name != null ? name : format);
    return this;
  }

  @Override
  public SimpleRequestBuilder<MT> setParallel(final boolean value) {
    super.setParallel(value);
    return this;
  }

  @Override
  public SimpleRequestBuilder<MT> setExecutor(final RequestExecutor executor) {
    super.setExecutor(executor);
    return this;
  }

  @Override
  public SimpleRequestBuilder<MT> setTaskQueueName(final String taskQueue) {
    super.setTaskQueueName(taskQueue);
    return this;
  }

  /**
   * Add string parameter.
   * @param name parameter name
   * @param value parameter value
   * @return this builder for chaining
   */
  public SimpleRequestBuilder<MT> addParam(final String name, final String value) {
    addSimpleParameter(name, value);
    return this;
  }

  /**
   * Add boolean parameter (true <-> 1, false <-> 0).
   * @param name parameter name
   * @param value parameter value
   * @return this builder for chaining
   */
  public SimpleRequestBuilder<MT> addParam(final String name, final boolean value) {
    addSimpleParameter(name, value);
    return this;
  }

  /**
   * Add integer parameter.
   * @param name parameter name
   * @param value parameter value
   * @return this builder for chaining
   */
  public SimpleRequestBuilder<MT> addParam(final String name, final int value) {
    addSimpleParameter(name, value);
    return this;
  }

  /**
   * Add long integer parameter.
   * @param name parameter name
   * @param value parameter value
   * @return this builder for chaining
   */
  public SimpleRequestBuilder<MT> addParam(final String name, final long value) {
    addSimpleParameter(name, value);
    return this;
  }

  @Override
  public SimpleRequestBuilder<MT> addHeader(final String name, final String value) {
    super.addHeader(name, value);
    return this;
  }

  public SimpleRequestBuilder<MT> setContentAnalyzer(final String contentAnalyzer) {
    defineContentAnalyzer(contentAnalyzer);
    return this;
  }

  public SimpleRequestBuilder<MT> setContentAnalyzer(final Class<?> clazz) {
    return setContentAnalyzer(BeanUtils.getBeanInfo(clazz).value());
  }

  public SimpleRequestBuilder<MT> setTafficStatsTag(final int tag) {
    getResult().statsTag = tag;
    return this;
  }

  public SimpleRequestBuilder<MT> setTrafficStatsTag(final String tag) {
    setConvertedTrafficStatsTag(tag);
    return this;
  }

}


package com.stanfy.enroscar.net.cache;

import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLConnection;

import javax.net.ssl.HttpsURLConnection;

import android.os.Build;
import android.util.Log;

import com.stanfy.enroscar.net.UrlConnectionWrapper;

/**
 * {@link URLConnection} that supplies information about required cache manager.
 *
NB! You cannot share this connection between multiple threads.

 * @author Roman Mazur (Stanfy - http://stanfy.com)
 */
public class CacheControlUrlConnection extends UrlConnectionWrapper {

  /** Cache manager name. */
  private final String cacheManagerName;

  /** Flag that indicates that connections stack has been cleared. */
  private boolean done = false;

  public CacheControlUrlConnection(final URLConnection urlConnection, final String cacheManagerName) {
    super(urlConnection);
    this.cacheManagerName = cacheManagerName;
  }

  public String getResponseCacheName() { return cacheManagerName; }

  /*
   * Note: this method is not synchronized since CacheControlUrlConnection must be accessed from one thread only.
   */
  void restoreConnectionsStack() {
    if (!done) {
      ResponseCacheSwitcher.restoreUrlConnection(this);
      done = true;
    } else {
      Log.w("CacheControl", "restoreConnectionsStack() has been called twice, there is something wrong with it");
    }
  }

  @Override
  public void connect() throws IOException {
    if (connected) { return; }
    if (getDoInput() || getDoOutput()) {
      ResponseCacheSwitcher.saveUrlConnection(this);
    }
    connectWithWorkaround();
  }

  // XXX on 2.3 we get NP exception in case of HTTPs connection and cache
  private void connectWithWorkaround() throws IOException {
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
      super.connect();
      return;
    }

    URLConnection coreConnection = UrlConnectionWrapper.unwrap(getCore());
    if (coreConnection instanceof HttpsURLConnection) {

      // CHECKSTYLE:OFF
      try {
        super.connect();
      } catch (NullPointerException e) {
        // ignore this NP
      }
      // CHECKSTYLE:ON

    } else {
      super.connect();
    }
  }

  @Override
  public InputStream getInputStream() throws IOException {
    connect();
    if (getDoInput()) {
      return new CacheControlInputStream();
    }
    return super.getInputStream();
  }

  @Override
  public OutputStream getOutputStream() throws IOException {
    connect();
    if (!getDoInput() && getDoOutput()) {
      return new CacheControlOutputStream();
    }
    return super.getOutputStream();
  }

  /**
   * Stream that restores connections stack when it's closed.
   */
  protected class CacheControlInputStream extends FilterInputStream {

    /**
     * @throws IOException if an I/O error happens
     */
    public CacheControlInputStream() throws IOException {
      super(CacheControlUrlConnection.super.getInputStream());
    }

    @Override
    public void close() throws IOException {
      try {
        super.close();
      } finally {
        restoreConnectionsStack();
      }
    }

  }

  /**
   * Stream that restores connections stack when it's closed.
   */
  protected class CacheControlOutputStream extends FilterOutputStream {

    /**
     * @throws IOException if an I/O error happens
     */
    public CacheControlOutputStream() throws IOException {
      super(CacheControlUrlConnection.super.getOutputStream());
    }

    @Override
    public void close() throws IOException {
      try {
        super.close();
      } finally {
        restoreConnectionsStack();
      }
    }

  }

}


package com.stanfy.enroscar.net.cache;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import static com.stanfy.enroscar.io.IoUtils.UTF_8_NAME;

/**
 * Utility for MD5 calculations.
 */
class Md5 {

  /**
   * Calculate 32 bytes length MD5 digest.
   * @param text input text
   * @return MD5 digest
   */
  public static String getMd5(final String text)  {
    try {

      final MessageDigest md = MessageDigest.getInstance("MD5");
      final byte[] utf8Bytes = text.getBytes(UTF_8_NAME);
      md.update(utf8Bytes, 0, utf8Bytes.length);
      final byte[] md5hash = md.digest();
      final int radix = 16;
      final int length = 32;

      final StringBuilder result = new StringBuilder(length).append(new BigInteger(1, md5hash).toString(radix));

      final int zeroBeginLen = length - result.length();
      if (zeroBeginLen > 0) {
        final char [] zeroBegin = new char[zeroBeginLen];
        Arrays.fill(zeroBegin, Character.forDigit(0, radix));
        result.insert(0, zeroBegin);
      }

      return result.toString();

    } catch (final NoSuchAlgorithmException|UnsupportedEncodingException e) {
      throw new AssertionError(e);
    }
  }

  private Md5() {
    throw new UnsupportedOperationException();
  }

}


package com.stanfy.enroscar.content;

import java.lang.ref.WeakReference;
import java.util.ArrayList;

import android.app.Activity;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.MediaStore.MediaColumns;
import android.support.v4.app.ShareCompat;
import android.text.TextUtils;

/**
 * Helper class for activities that respond to {@link Intent#ACTION_SEND} action.
 * Be sure to call its methods from the main thread.
 * @author Roman Mazur (Stanfy - http://stanfy.com)
 */
public final class SharingHelper {

  /** Sharing extra. */
  public static final String EXTRA_SHARING = "_enroscar_sharing_intent_";

  /** Media query projection. */
  private static final String[] MEDIA_PROJECTION = new String[] {MediaColumns.DISPLAY_NAME, MediaColumns.TITLE, MediaColumns.DATA};

  /** Context instance. */
  private WeakReference<Context> context;
  /** Actor instance. */
  private final SharingActor actor;

  /** Resolver task. */
  private ResolveContentUriTask asyncTask;

  private SharingHelper(final SharingActor actor) {
    this.actor = actor;
    if (actor instanceof Context) {
      this.context = new WeakReference<Context>((Context)actor);
    }
  }

  public static SharingHelper use(final SharingActor actor) {
    return new SharingHelper(actor);
  }

  public static Intent setSharingIntent(final Intent dest, final Intent sharingIntent) {
    return sharingIntent != null ? dest.putExtra(EXTRA_SHARING, sharingIntent) : dest;
  }

  /**
   * @param context context instance
   * @return helper for chaining
   */
  public SharingHelper withContext(final Context context) {
    this.context = new WeakReference<Context>(context);
    return this;
  }

  /**
   * Check whether activity contains a sharing intent previously set with {@link #setSharingIntent(Intent, Intent)}
   * and process it with {@link #processSharingIntent(Intent)} if result is positive.
   * Anyway activity intent will not contain a sharing extra after this method execution.
   * @param activity activity instance
   * @return whether sharing extra has been put to the activity intent
   */
  public boolean checkSharingIntent(final Activity activity) {
    final Intent activityIntent = activity.getIntent();
    if (activityIntent.hasExtra(SharingHelper.EXTRA_SHARING)) {
      processSharingIntent(activityIntent.<Intent>getParcelableExtra(SharingHelper.EXTRA_SHARING));
      activityIntent.removeExtra(SharingHelper.EXTRA_SHARING);
      return true;
    }
    return false;
  }

  /**
   * Process data provided by the sharing intent. Call it from the main thread.
   * @param sharingIntent sharing intent instance
   */
  public void processSharingIntent(final Intent sharingIntent) {
    final SharingData data = createSharingData(sharingIntent);
    deliverSharingData(data);
  }

  /**
   * Stop asynchronous operations. Call it from the main thread.
   */
  public void abort() {
    asyncTask.cancel(false);
  }

  private SharingData createSharingData(final Intent sharingIntent) {
    final SharingData data = TextUtils.isEmpty(sharingIntent.getAction()) ? new PickData() : new SharingData();
    data.intent = sharingIntent;
    final Context context = this.context.get();
    if (context instanceof Activity) {
      data.callerInfo = ((Activity) context).getCallingActivity();
    } else {
      final String pkg = sharingIntent.getStringExtra(ShareCompat.EXTRA_CALLING_PACKAGE);
      final String act = sharingIntent.getStringExtra(ShareCompat.EXTRA_CALLING_ACTIVITY);
      if (!TextUtils.isEmpty(pkg) && !TextUtils.isEmpty(act)) {
        data.callerInfo = new ComponentName(pkg, act);
      }
    }
    return data;
  }

  private void deliverSharingData(final SharingData data) {
    final SharingActor actor = this.actor;
    final Context context = this.context.get();
    if (actor == null || context == null) { return; }
    if (!data.containsStream() || data.isStreamResolved()) {
      asyncTask = null;
      actor.dispatchSharedData(data);
      return;
    }
    asyncTask = new ResolveContentUriTask();
    asyncTask.execute(data);
  }

  private void deliverError() {
    final SharingActor actor = this.actor;
    final Context context = this.context.get();
    if (actor == null || context == null) { return; }
    actor.dispatchResolverError();
  }

  /**
   * Interface of an object that can react on sharing intent.
   * @author Roman Mazur (Stanfy - http://stanfy.com)
   */
  public interface SharingActor {
    void dispatchSharedData(final SharingData data);
    void dispatchResolverError();
  }

  /**
   *
   * @author Roman Mazur (Stanfy - http://stanfy.com)
   */
  public static class SharingData implements Parcelable {

    /** Creator. */
    public static final Creator<SharingData> CREATOR = new Creator<SharingHelper.SharingData>() {
      @Override
      public SharingData createFromParcel(final Parcel source) { return new SharingData(source); }
      @Override
      public SharingData[] newArray(final int size) { return new SharingData[size]; }
    };

    /** Intent that contains all the information. */
    Intent intent;

    /** Content name. */
    String[] contentNames;

    /** Caller info. */
    ComponentName callerInfo;

    /** Streams. */
    private ArrayList<Uri> cachedStreams;

    SharingData() {
      // nothing
    }

    SharingData(final Parcel in) {
      this.intent = in.readParcelable(getClass().getClassLoader());
      this.contentNames = in.createStringArray();
    }

    @Override
    public int describeContents() { return 0; }
    @Override
    public void writeToParcel(final Parcel dest, final int flags) {
      dest.writeParcelable(intent, flags);
      dest.writeStringArray(contentNames);
    }

    private void cacheStreams() {
      this.cachedStreams = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
    }

    /** @return whether this intent has one of send actions */
    public boolean isShareIntent() {
      return isSingleShare() || isMultipleShare();
    }
    /** @return whether this intent has action {@link Intent#ACTION_SEND} */
    public boolean isSingleShare() {
      return Intent.ACTION_SEND.equals(intent.getAction());
    }
    /** @return whether this intent has action {@link Intent#ACTION_SEND_MULTIPLE} */
    public boolean isMultipleShare() {
      return Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction());
    }

    /** @return subject */
    public String getSubject() { return intent.getStringExtra(Intent.EXTRA_SUBJECT); }
    /** @return data MIME-type */
    public String getType() { return intent.getType(); }
    /** @return recipient address (email) */
    public String[] getRecipient() { return intent.getStringArrayExtra(Intent.EXTRA_EMAIL); }
    /** @return CC field value */
    public String[] getCopyTo() { return intent.getStringArrayExtra(Intent.EXTRA_CC); }
    /** @return BCC field value */
    public String[] getBlindCopyTo() { return intent.getStringArrayExtra(Intent.EXTRA_BCC); }
    /** @return message text (can be styled!) */
    public CharSequence getMessage() { return intent.getCharSequenceExtra(Intent.EXTRA_TEXT); }

    /** @return stream URI (use content resolver to get content details and stream object) */
    public Uri getStreamUri() { return intent.getParcelableExtra(Intent.EXTRA_STREAM); }
    /** @return count of shared streams */
    public int getStreamUriCount() {
      if (isMultipleShare()) {
        if (cachedStreams == null) { cacheStreams(); }
        return cachedStreams == null ? 0 : cachedStreams.size();
      }
      return getStreamUri() == null ? 0 : 1;
    }

    public Uri getStreamUri(final int index) {
      if (cachedStreams == null && isMultipleShare()) { cacheStreams(); }
      if (cachedStreams != null) { return cachedStreams.get(index); }
      if (index == 0) { return getStreamUri(); }
      throw new IndexOutOfBoundsException("Trying to get stream[" + index + "] from single share intent. It can have not more than one stream.");
    }

    /** @return whether sharing data describes a content stream */
    public boolean containsStream() { return getStreamUriCount() > 0; }

    /** @return caller information */
    public ComponentName getCallerInfo() { return callerInfo; }

    /** @return whether content stream is resolved */
    public boolean isStreamResolved() { return contentNames != null; }

    /** @return content name */
    public String[] getContentNames() { return contentNames; }

  }

  /**
   * @author Roman Mazur (Stanfy - http://stanfy.com)
   */
  public static class PickData extends SharingData {

    /** Creator. */
    public static final Creator<PickData> CREATOR = new Creator<PickData>() {
      @Override
      public PickData createFromParcel(final Parcel source) { return new PickData(source); }
      @Override
      public PickData[] newArray(final int size) { return new PickData[size]; }
    };

    PickData() {
      // nothing
    }

    PickData(final Parcel in) {
      super(in);
    }

    @Override
    public boolean isShareIntent() { return false; }

    @Override
    public Uri getStreamUri() { return intent.getData(); }

  }

  /**
   * @author Roman Mazur (Stanfy - http://stanfy.com)
   */
  private class ResolveContentUriTask extends AsyncTask<SharingData, Void, SharingData> {

    /** Error state. */
    private boolean resolverErrorState = false;

    /** Resolver instance. */
    private ContentResolver resolver;

    private void resolveContent(final ContentResolver resolver, final SharingData data, final int index) {
      final Uri contentUri = data.getStreamUri(index);

      // resolve content name
      String contentName = null;
      final String scheme = contentUri.getScheme();
      if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {

        // query content resolver for additional info
        final Cursor cursor = resolver.query(contentUri, MEDIA_PROJECTION, null, null, null);
        if (cursor != null) {
          try {
            cursor.moveToFirst();
            // display name
            contentName = cursor.getString(0);
            if (TextUtils.isEmpty(contentName)) {
              // title
              contentName = cursor.getString(1);
            }
            if (TextUtils.isEmpty(contentName)) {
              // file name
              contentName = Uri.parse(cursor.getString(2)).getLastPathSegment();
            }
          } finally {
            cursor.close();
          }
        }

      } else if (ContentResolver.SCHEME_FILE.equals(scheme)) {

        // use file name
        contentName = contentUri.getLastPathSegment();

      }

      data.contentNames[index] = contentName;
    }

    @Override
    protected void onPreExecute() {
      final Context ctx = context.get();
      if (ctx == null) { return; }
      this.resolver = ctx.getApplicationContext().getContentResolver();
    }

    @Override
    protected SharingData doInBackground(final SharingData... params) {
      if (resolver == null) { return null; }

      final SharingData data = params[0];
      final int count = data.getStreamUriCount();
      if (count == 0) { throw new IllegalStateException("Cannot run resolver task with a null content URI"); }

      data.contentNames = new String[count];
      for (int i = 0; i < count; i++) {
        resolveContent(resolver, data, i);
      }

      return data;
    }

    @Override
    protected void onPostExecute(final SharingData result) {
      if (result == null) { return; }
      if (!resolverErrorState) {
        deliverSharingData(result);
      } else {
        deliverError();
      }
    }
  }

}


package com.stanfy.enroscar.rest.request.net;

import android.content.Context;
import android.util.Log;

import com.stanfy.enroscar.beans.BeansManager;
import com.stanfy.enroscar.io.BuffersPool;
import com.stanfy.enroscar.io.IoUtils;
import com.stanfy.enroscar.rest.Utils;
import com.stanfy.enroscar.net.operation.Parameter;
import com.stanfy.enroscar.net.operation.ParameterValue;
import com.stanfy.enroscar.net.operation.RequestDescription;
import com.stanfy.enroscar.rest.request.binary.BinaryData;
import com.stanfy.enroscar.rest.request.net.multipart.Part;
import com.stanfy.enroscar.rest.request.net.multipart.StringPart;

import org.apache.http.util.EncodingUtils;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * Multipart upload converter.
 */
public class UploadPostConverter extends PostConverter {

  /** Multipart POST converter factory. */
  public static final ConverterFactory FACTORY = new ConverterFactory() {
    @Override
    public BaseRequestDescriptionConverter createConverter(final RequestDescription requestDescription, final Context context) {
      return new UploadPostConverter(requestDescription, context);
    }
  };

  /**
   * The pool of ASCII chars to be used for generating a multipart boundary.
   */
  private static byte[] multipartChars = null;

  /** Boundary. */
  private final byte[] boundary;

  /** Composed parts. */
  private Part[] parts;

  public UploadPostConverter(final RequestDescription requestDescription, final Context context) {
    super(requestDescription, context, null);
    this.boundary = generateMultipartBoundary();
  }

  private static byte[] getMultipartChars() {
    if (multipartChars == null) {
      multipartChars = EncodingUtils.getAsciiBytes("-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
    }
    return multipartChars;
  }

  @Override
  protected String getRequestUrl() {
    return getRequestDescription().getUrl();
  }

  /**
   * Generates a random multipart boundary string.
   */
  /* From MultipartEntity */
  private static byte[] generateMultipartBoundary() {
    final Random rand = new Random();
    final int c11 = 11, c30 = 30;
    final byte[] bytes = new byte[rand.nextInt(c11) + c30]; // a random size from 30 to 40
    final byte[] chars = getMultipartChars();
    for (int i = 0; i < bytes.length; i++) {
      bytes[i] = chars[rand.nextInt(chars.length)];
    }
    return bytes;
  }

  @Override
  public URLConnection prepareConnectionInstance() throws IOException {
    final URLConnection connection = super.prepareConnectionInstance();
    connection.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + EncodingUtils.getAsciiString(boundary));

    this.parts = composeParts(getContext(), getRequestDescription());

    asHttp(connection).setFixedLengthStreamingMode((int)Part.getLengthOfParts(parts, boundary));

    return connection;
  }

  @Override
  public void sendRequest(final URLConnection connection) throws IOException {
    final BuffersPool buffersPool = BeansManager.get(getContext()).getContainer().getBean(BuffersPool.class);
    final OutputStream out = buffersPool.bufferize(connection.getOutputStream());

    if (Utils.isDebugRest(getContext())) {
      Log.d(TAG, "(" + getRequestDescription().getId() + ") Parts: " + Arrays.toString(parts));
    }

    try {
      Part.sendParts(out, parts, boundary);
    } finally {
      IoUtils.closeQuietly(out);
    }

  }

  /**
   * @param context application context
   * @param requestDescription request description
   * @return upload request parts obtained from the request description
   * @throws IOException if an I/O error happens
   */
  protected static Part[] composeParts(final Context context, final RequestDescription requestDescription) throws IOException {
    final ArrayList<BinaryData<?>> binaryData = requestDescription.getBinaryData();
    final List<Parameter> params = requestDescription.getSimpleParameters().getChildren();
    int realCount = 0;
    final int binaryCount = binaryData != null ? binaryData.size() : 0;
    Part[] parts = new Part[params.size() + binaryCount];
    for (final Parameter p : params) {
      if (p instanceof ParameterValue) {
        final ParameterValue pv = (ParameterValue)p;
        if (pv.getValue() == null) { continue; }
        parts[realCount++] = new StringPart(pv.getName(), pv.getValue(), requestDescription.getEncoding().name());
      }
    }
    for (int i = 0; i < binaryCount; i++) {
      final Part part = binaryData.get(i).createHttpPart(context);
      if (part != null) {
        parts[realCount++] = part;
      }
    }
    if (realCount < parts.length) {
      final Part[] trim = new Part[realCount];
      System.arraycopy(parts, 0, trim, 0, realCount);
      parts = trim;
    }
    return parts;
  }

}


/*
 * JCublas - Java bindings for CUBLAS, the NVIDIA CUDA BLAS library,
 * to be used with JCuda
 *
 * Copyright (c) 2010-2016 Marco Hutter - http://www.jcuda.org
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
package jcuda.jcublas;

/**
 * Different GEMM algorithms
 */
public class cublasGemmAlgo
{
    public static final int CUBLAS_GEMM_DFALT = -1;
    public static final int CUBLAS_GEMM_DEFAULT = -1;
    public static final int CUBLAS_GEMM_ALGO0 = 0;
    public static final int CUBLAS_GEMM_ALGO1 = 1;
    public static final int CUBLAS_GEMM_ALGO2 = 2;
    public static final int CUBLAS_GEMM_ALGO3 = 3;
    public static final int CUBLAS_GEMM_ALGO4 = 4;
    public static final int CUBLAS_GEMM_ALGO5 = 5;
    public static final int CUBLAS_GEMM_ALGO6 = 6;
    public static final int CUBLAS_GEMM_ALGO7 = 7;
    public static final int CUBLAS_GEMM_ALGO8 = 8;
    public static final int CUBLAS_GEMM_ALGO9 = 9;
    public static final int CUBLAS_GEMM_ALGO10 = 10;
    public static final int CUBLAS_GEMM_ALGO11 = 11;
    public static final int CUBLAS_GEMM_ALGO12 = 12;
    public static final int CUBLAS_GEMM_ALGO13 = 13;
    public static final int CUBLAS_GEMM_ALGO14 = 14;
    public static final int CUBLAS_GEMM_ALGO15 = 15;
    public static final int CUBLAS_GEMM_ALGO16 = 16;
    public static final int CUBLAS_GEMM_ALGO17 = 17;
    // sliced 32x32
    public static final int CUBLAS_GEMM_ALGO18 = 18;
    // sliced 64x32
    public static final int CUBLAS_GEMM_ALGO19 = 19;
    // sliced 128x32
    public static final int CUBLAS_GEMM_ALGO20 = 20;
    // sliced 32x32 -splitK
    public static final int CUBLAS_GEMM_ALGO21 = 21;
    // sliced 64x32 -splitK
    public static final int CUBLAS_GEMM_ALGO22 = 22;
    // sliced 128x32 -splitK
    public static final int CUBLAS_GEMM_ALGO23 = 23;

    public static final int CUBLAS_GEMM_DEFAULT_TENSOR_OP = 99;
    public static final int CUBLAS_GEMM_DFALT_TENSOR_OP = 99;
    public static final int CUBLAS_GEMM_ALGO0_TENSOR_OP = 100;
    public static final int CUBLAS_GEMM_ALGO1_TENSOR_OP = 101;
    public static final int CUBLAS_GEMM_ALGO2_TENSOR_OP = 102;
    public static final int CUBLAS_GEMM_ALGO3_TENSOR_OP = 103;
    public static final int CUBLAS_GEMM_ALGO4_TENSOR_OP = 104;
    public static final int CUBLAS_GEMM_ALGO5_TENSOR_OP = 105;
    public static final int CUBLAS_GEMM_ALGO6_TENSOR_OP = 106;
    public static final int CUBLAS_GEMM_ALGO7_TENSOR_OP = 107;
    public static final int CUBLAS_GEMM_ALGO8_TENSOR_OP = 108;
    public static final int CUBLAS_GEMM_ALGO9_TENSOR_OP = 109;
    public static final int CUBLAS_GEMM_ALGO10_TENSOR_OP = 110;
    public static final int CUBLAS_GEMM_ALGO11_TENSOR_OP = 111;
    public static final int CUBLAS_GEMM_ALGO12_TENSOR_OP = 112;
    public static final int CUBLAS_GEMM_ALGO13_TENSOR_OP = 113;
    public static final int CUBLAS_GEMM_ALGO14_TENSOR_OP = 114;
    public static final int CUBLAS_GEMM_ALGO15_TENSOR_OP = 115;

    /**
     * Private constructor to prevent instantiation
     */
    private cublasGemmAlgo()
    {
        // Private constructor to prevent instantiation
    }

    /**
     * Returns a string representation of the given constant
     *
     * @return A string representation of the given constant
     */
    public static String stringFor(int n)
    {
        switch (n)
        {
            case CUBLAS_GEMM_DEFAULT: return "CUBLAS_GEMM_DEFAULT";
            case CUBLAS_GEMM_ALGO0: return "CUBLAS_GEMM_ALGO0";
            case CUBLAS_GEMM_ALGO1: return "CUBLAS_GEMM_ALGO1";
            case CUBLAS_GEMM_ALGO2: return "CUBLAS_GEMM_ALGO2";
            case CUBLAS_GEMM_ALGO3: return "CUBLAS_GEMM_ALGO3";
            case CUBLAS_GEMM_ALGO4: return "CUBLAS_GEMM_ALGO4";
            case CUBLAS_GEMM_ALGO5: return "CUBLAS_GEMM_ALGO5";
            case CUBLAS_GEMM_ALGO6: return "CUBLAS_GEMM_ALGO6";
            case CUBLAS_GEMM_ALGO7: return "CUBLAS_GEMM_ALGO7";
            case CUBLAS_GEMM_ALGO8: return "CUBLAS_GEMM_ALGO8";
            case CUBLAS_GEMM_ALGO9: return "CUBLAS_GEMM_ALGO9";
            case CUBLAS_GEMM_ALGO10: return "CUBLAS_GEMM_ALGO10";
            case CUBLAS_GEMM_ALGO11: return "CUBLAS_GEMM_ALGO11";
            case CUBLAS_GEMM_ALGO12: return "CUBLAS_GEMM_ALGO12";
            case CUBLAS_GEMM_ALGO13: return "CUBLAS_GEMM_ALGO13";
            case CUBLAS_GEMM_ALGO14: return "CUBLAS_GEMM_ALGO14";
            case CUBLAS_GEMM_ALGO15: return "CUBLAS_GEMM_ALGO15";
            case CUBLAS_GEMM_ALGO16: return "CUBLAS_GEMM_ALGO16";
            case CUBLAS_GEMM_ALGO17: return "CUBLAS_GEMM_ALGO17";
            case CUBLAS_GEMM_ALGO18: return "CUBLAS_GEMM_ALGO18";
            case CUBLAS_GEMM_ALGO19: return "CUBLAS_GEMM_ALGO19";
            case CUBLAS_GEMM_ALGO20: return "CUBLAS_GEMM_ALGO20";
            case CUBLAS_GEMM_ALGO21: return "CUBLAS_GEMM_ALGO21";
            case CUBLAS_GEMM_ALGO22: return "CUBLAS_GEMM_ALGO22";
            case CUBLAS_GEMM_ALGO23: return "CUBLAS_GEMM_ALGO23";
            case CUBLAS_GEMM_DEFAULT_TENSOR_OP: return "CUBLAS_GEMM_DEFAULT_TENSOR_OP";
            case CUBLAS_GEMM_ALGO0_TENSOR_OP: return "CUBLAS_GEMM_ALGO0_TENSOR_OP";
            case CUBLAS_GEMM_ALGO1_TENSOR_OP: return "CUBLAS_GEMM_ALGO1_TENSOR_OP";
            case CUBLAS_GEMM_ALGO2_TENSOR_OP: return "CUBLAS_GEMM_ALGO2_TENSOR_OP";
            case CUBLAS_GEMM_ALGO3_TENSOR_OP: return "CUBLAS_GEMM_ALGO3_TENSOR_OP";
            case CUBLAS_GEMM_ALGO4_TENSOR_OP: return "CUBLAS_GEMM_ALGO4_TENSOR_OP";
            case CUBLAS_GEMM_ALGO5_TENSOR_OP: return "CUBLAS_GEMM_ALGO5_TENSOR_OP";
            case CUBLAS_GEMM_ALGO6_TENSOR_OP: return "CUBLAS_GEMM_ALGO6_TENSOR_OP";
            case CUBLAS_GEMM_ALGO7_TENSOR_OP: return "CUBLAS_GEMM_ALGO7_TENSOR_OP";
            case CUBLAS_GEMM_ALGO8_TENSOR_OP: return "CUBLAS_GEMM_ALGO8_TENSOR_OP";
            case CUBLAS_GEMM_ALGO9_TENSOR_OP: return "CUBLAS_GEMM_ALGO9_TENSOR_OP";
            case CUBLAS_GEMM_ALGO10_TENSOR_OP: return "CUBLAS_GEMM_ALGO10_TENSOR_OP";
            case CUBLAS_GEMM_ALGO11_TENSOR_OP: return "CUBLAS_GEMM_ALGO11_TENSOR_OP";
            case CUBLAS_GEMM_ALGO12_TENSOR_OP: return "CUBLAS_GEMM_ALGO12_TENSOR_OP";
            case CUBLAS_GEMM_ALGO13_TENSOR_OP: return "CUBLAS_GEMM_ALGO13_TENSOR_OP";
            case CUBLAS_GEMM_ALGO14_TENSOR_OP: return "CUBLAS_GEMM_ALGO14_TENSOR_OP";
        }
        return "INVALID cublasGemmAlgo: "+n;
    }
}


/*
 * JCublas - Java bindings for CUBLAS, the NVIDIA CUDA BLAS library,
 * to be used with JCuda
 *
 * Copyright (c) 2008-2015 Marco Hutter - http://www.jcuda.org
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

package jcuda.jcublas;

/**
 * JCublas status return values.
 */
public class cublasStatus
{
    /** Operation completed successfully */
    public static final int CUBLAS_STATUS_SUCCESS          = 0x00000000;

    /** Library not initialized */
    public static final int CUBLAS_STATUS_NOT_INITIALIZED  = 0x00000001;

    /** Resource allocation failed */
    public static final int CUBLAS_STATUS_ALLOC_FAILED     = 0x00000003;

    /** Unsupported numerical value was passed to function */
    public static final int CUBLAS_STATUS_INVALID_VALUE    = 0x00000007;

    /**
     * Function requires an architectural feature absent from the
     * architecture of the device
     */
    public static final int CUBLAS_STATUS_ARCH_MISMATCH    = 0x00000008;

    /** Access to GPU memory space failed */
    public static final int CUBLAS_STATUS_MAPPING_ERROR    = 0x0000000B;

    /** GPU program failed to execute */
    public static final int CUBLAS_STATUS_EXECUTION_FAILED = 0x0000000D;

    /** An internal CUBLAS operation failed */
    public static final int CUBLAS_STATUS_INTERNAL_ERROR   = 0x0000000E;

    /** The functionality requested is not supported. */
    public static final int CUBLAS_STATUS_NOT_SUPPORTED    = 0x0000000F;

    /** JCublas status returns */
    public static final int JCUBLAS_STATUS_INTERNAL_ERROR      = 0x10000001;

    /**
     * Returns the String identifying the given cublasStatus
     *
     * @param n The cublasStatus
     * @return The String identifying the given cublasStatus
     */
    public static String stringFor(int n)
    {
        switch (n)
        {
            case CUBLAS_STATUS_SUCCESS          : return "CUBLAS_STATUS_SUCCESS";
            case CUBLAS_STATUS_NOT_INITIALIZED  : return "CUBLAS_STATUS_NOT_INITIALIZED";
            case CUBLAS_STATUS_ALLOC_FAILED     : return "CUBLAS_STATUS_ALLOC_FAILED";
            case CUBLAS_STATUS_INVALID_VALUE    : return "CUBLAS_STATUS_INVALID_VALUE";
            case CUBLAS_STATUS_ARCH_MISMATCH    : return "CUBLAS_STATUS_ARCH_MISMATCH";
            case CUBLAS_STATUS_MAPPING_ERROR    : return "CUBLAS_STATUS_MAPPING_ERROR";
            case CUBLAS_STATUS_EXECUTION_FAILED : return "CUBLAS_STATUS_EXECUTION_FAILED";
            case CUBLAS_STATUS_INTERNAL_ERROR   : return "CUBLAS_STATUS_INTERNAL_ERROR";
            case CUBLAS_STATUS_NOT_SUPPORTED   : return "CUBLAS_STATUS_NOT_SUPPORTED";
            case JCUBLAS_STATUS_INTERNAL_ERROR  : return "JCUBLAS_STATUS_INTERNAL_ERROR";
        }
        return "INVALID cublasStatus: "+n;
    }

    /**
     * Private constructor to prevent instantiation.
     */
    private cublasStatus()
    {
    }

}