package lb.listviewvariants.utils;

import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.provider.ContactsContract;

import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;

import lb.listviewvariants.BuildConfig;

public class ContactImageUtil
  {
  @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
  public static Bitmap loadContactPhoto(Context context,Uri contactUri,int imageSize)
    {
    // Instantiates a ContentResolver for retrieving the Uri of the image
    final ContentResolver contentResolver=context.getContentResolver();

    // Instantiates an AssetFileDescriptor. Given a content Uri pointing to an image file, the
    // ContentResolver can return an AssetFileDescriptor for the file.
    AssetFileDescriptor afd=null;
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.ICE_CREAM_SANDWICH)
      {
      // On platforms running Android 4.0 (API version 14) and later, a high resolution image
      // is available from Photo.DISPLAY_PHOTO.
      try
        {
        // Constructs the content Uri for the image
        Uri displayImageUri=Uri.withAppendedPath(contactUri,ContactsContract.Contacts.Photo.DISPLAY_PHOTO);

        // Retrieves an AssetFileDescriptor from the Contacts Provider, using the
        // constructed Uri
        afd=contentResolver.openAssetFileDescriptor(displayImageUri,"r");
        // If the file exists
        if(afd!=null)
          {
          // Reads and decodes the file to a Bitmap and scales it to the desired size
          return decodeSampledBitmapFromDescriptor(
              afd.getFileDescriptor(),imageSize,imageSize);
          }
        }
      catch(FileNotFoundException e)
        {
        // Catches file not found exceptions
        if(BuildConfig.DEBUG)
          {
          // Log debug message, this is not an error message as this exception is thrown
          // when a contact is legitimately missing a contact photo (which will be quite
          // frequently in a long contacts list).
//        Log.d(TAG,"Contact photo not found for contact "+contactUri.toString()
//            +": "+e.toString());
          }
        }
      finally
        {
        // Once the decode is complete, this closes the file. You must do this each time
        // you access an AssetFileDescriptor; otherwise, every image load you do will open
        // a new descriptor.
        if(afd!=null)
          {
          try
            {
            afd.close();
            }
          catch(IOException e)
            {
            // Closing a file descriptor might cause an IOException if the file is
            // already closed. Nothing extra is needed to handle this.
            }
          }
        }
      }

    // If the platform version is less than Android 4.0 (API Level 14), use the only available
    // image URI, which points to a normal-sized image.
    try
      {
      // Constructs the image Uri from the contact Uri and the directory twig from the
      // Contacts.Photo table
      Uri imageUri=Uri.withAppendedPath(contactUri,ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);

      // Retrieves an AssetFileDescriptor from the Contacts Provider, using the constructed
      // Uri
      afd=contentResolver.openAssetFileDescriptor(imageUri,"r");

      // If the file exists
      if(afd!=null)
        {
        // Reads the image from the file, decodes it, and scales it to the available screen
        // area
        return decodeSampledBitmapFromDescriptor(
            afd.getFileDescriptor(),imageSize,imageSize);
        }
      }
    catch(FileNotFoundException e)
      {
      // Catches file not found exceptions
      if(BuildConfig.DEBUG)
        {
//        Log.d(TAG,"Contact photo not found for contact "+contactUri.toString()
//            +": "+e.toString());
        }
      }
    finally
      {
      // Once the decode is complete, this closes the file. You must do this each time you
      // access an AssetFileDescriptor; otherwise, every image load you do will open a new
      // descriptor.
      if(afd!=null)
        {
        try
          {
          afd.close();
          }
        catch(IOException e)
          {
          // Closing a file descriptor might cause an IOException if the file is
          // already closed. Ignore this.
          }
        }
      }

    // If none of the case selectors match, returns null.
    return null;
    }

  /**
   * Decodes and scales a contact's image from a file pointed to by a Uri in the contact's data,
   * and returns the result as a Bitmap. The column that contains the Uri varies according to the
   * platform version.
   *
   * @param photoData For platforms prior to Android 3.0, provide the Contact._ID column value.
   *                  For Android 3.0 and later, provide the Contact.PHOTO_THUMBNAIL_URI value.
   * @param imageSize The desired target width and height of the output image in pixels.
   * @return A Bitmap containing the contact's image, resized to fit the provided image size. If
   * no thumbnail exists, returns null.
   */
  public static Bitmap loadContactPhotoThumbnail(Context context,String photoData,int imageSize)
    {
    // Instantiates an AssetFileDescriptor. Given a content Uri pointing to an image file, the
    // ContentResolver can return an AssetFileDescriptor for the file.
    AssetFileDescriptor afd=null;
    // This "try" block catches an Exception if the file descriptor returned from the Contacts
    // Provider doesn't point to an existing file.
    try
      {
      Uri thumbUri;
      // If Android 3.0 or later, converts the Uri passed as a string to a Uri object.
      if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
        thumbUri=Uri.parse(photoData);
      else
        {
        // For versions prior to Android 3.0, appends the string argument to the content
        // Uri for the Contacts table.
        final Uri contactUri=Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI,photoData);
        // Appends the content Uri for the Contacts.Photo table to the previously
        // constructed contact Uri to yield a content URI for the thumbnail image
        thumbUri=Uri.withAppendedPath(contactUri,ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
        }
      // Retrieves a file descriptor from the Contacts Provider. To learn more about this
      // feature, read the reference documentation for
      // ContentResolver#openAssetFileDescriptor.
      afd=context.getContentResolver().openAssetFileDescriptor(thumbUri,"r");

      // Gets a FileDescriptor from the AssetFileDescriptor. A BitmapFactory object can
      // decode the contents of a file pointed to by a FileDescriptor into a Bitmap.
      FileDescriptor fileDescriptor=afd.getFileDescriptor();

      if(fileDescriptor!=null)
        {
        // Decodes a Bitmap from the image pointed to by the FileDescriptor, and scales it
        // to the specified width and height
        return decodeSampledBitmapFromDescriptor(
            fileDescriptor,imageSize,imageSize);
        }
      }
    catch(FileNotFoundException e)
      {
      // If the file pointed to by the thumbnail URI doesn't exist, or the file can't be
      // opened in "read" mode, ContentResolver.openAssetFileDescriptor throws a
      // FileNotFoundException.
//      if(BuildConfig.DEBUG)
//        {
//        Log.d(TAG,"Contact photo thumbnail not found for contact "+photoData
//            +": "+e.toString());
//        }
      }
    finally
      {
      // If an AssetFileDescriptor was returned, try to close it
      if(afd!=null)
        {
        try
          {
          afd.close();
          }
        catch(IOException e)
          {
          // Closing a file descriptor might cause an IOException if the file is
          // already closed. Nothing extra is needed to handle this.
          }
        }
      }

    // If the decoding failed, returns null
    return null;
    }


  public static Bitmap decodeSampledBitmapFromDescriptor(
      FileDescriptor fileDescriptor,int reqWidth,int reqHeight)
    {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options=new BitmapFactory.Options();
    options.inJustDecodeBounds=true;
    BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);

    // Calculate inSampleSize
    options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds=false;
    return BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
    }

  /**
   * Calculate an inSampleSize for use in a {@link BitmapFactory.Options} object when decoding
   * bitmaps using the decode* methods from {@link BitmapFactory}. This implementation calculates
   * the closest inSampleSize that will result in the final decoded bitmap having a width and
   * height equal to or larger than the requested width and height. This implementation does not
   * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
   * results in a larger bitmap which isn't as useful for caching purposes.
   *
   * @param options   An options object with out* params already populated (run through a decode*
   *                  method with inJustDecodeBounds==true
   * @param reqWidth  The requested width of the resulting bitmap
   * @param reqHeight The requested height of the resulting bitmap
   * @return The value to be used for inSampleSize
   */
  public static int calculateInSampleSize(BitmapFactory.Options options,
                                          int reqWidth,int reqHeight)
    {
    // Raw height and width of image
    final int height=options.outHeight;
    final int width=options.outWidth;
    int inSampleSize=1;

    if(height>reqHeight||width>reqWidth)
      {

      // Calculate ratios of height and width to requested height and width
      final int heightRatio=Math.round((float)height/(float)reqHeight);
      final int widthRatio=Math.round((float)width/(float)reqWidth);

      // Choose the smallest ratio as inSampleSize value, this will guarantee a final image
      // with both dimensions larger than or equal to the requested height and width.
      inSampleSize=heightRatio<widthRatio?heightRatio:widthRatio;

      // This offers some additional logic in case the image has a strange
      // aspect ratio. For example, a panorama may have a much larger
      // width than height. In these cases the total pixels might still
      // end up being too large to fit comfortably in memory, so we should
      // be more aggressive with sample down the image (=larger inSampleSize).

      final float totalPixels=width*height;

      // Anything more than 2x the requested pixels we'll sample down further
      final float totalReqPixelsCap=reqWidth*reqHeight*2;

      while(totalPixels/(inSampleSize*inSampleSize)>totalReqPixelsCap)
        {
        inSampleSize++;
        }
      }
    return inSampleSize;
    }
  }