package com.datarecovery.master.utils;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;

import androidx.annotation.IntDef;
import androidx.annotation.RequiresApi;

import com.atmob.common.runtime.ContextUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * 用于保存文件到公共存储空间
 */
public class MediaStoreHelper {

    public static final int TYPE_IMAGE = 1;

    public static final int TYPE_VIDEO = 2;

    public static final int TYPE_AUDIO = 3;

    public static final int TYPE_FILES = 4;

    public static final int TYPE_DOWNLOADS = 5;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({TYPE_IMAGE, TYPE_VIDEO, TYPE_AUDIO, TYPE_FILES, TYPE_DOWNLOADS})
    @interface MediaType {
    }

    public static void saveToSharedStorage(@MediaType int mediaType, File file, String fileName) throws Exception {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            saveToSharedStorage(mediaType, java.nio.file.Files.newInputStream(file.toPath()), fileName);
        } else {
            saveToSharedStorage(mediaType, new FileInputStream(file), fileName);
        }
    }

    public static void saveToSharedStorage(@MediaType int mediaType, InputStream inputStream, String fileName) throws Exception {
        // Add a media item that other apps don't see until the item is
        // fully written to the media store.
        Context applicationContext = ContextUtil.getContext().getApplicationContext();

        ContentResolver resolver = applicationContext.getContentResolver();

        Media targetMedia = getTargetMedia(mediaType);

        // Find all media files on the primary external storage device.
        Uri mediaCollection = targetMedia.getMediaCollection();

        ContentValues mediaDetails = new ContentValues();
        mediaDetails.put(targetMedia.displayName(), fileName);

        // lock media file until it's fully written.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            mediaDetails.put(targetMedia.isPending(), 1);
        }

        Uri mediaContentUri = resolver.insert(mediaCollection, mediaDetails);

        if (mediaContentUri == null) {
            throw new IllegalStateException("Failed to create new media item.");
        }

        // "w" for write.
        try (ParcelFileDescriptor pfd =
                     resolver.openFileDescriptor(mediaContentUri, "w", null);
             InputStream is = inputStream;
             FileOutputStream fos = pfd == null ? null : new FileOutputStream(pfd.getFileDescriptor())
        ) {
            if (fos == null) {
                throw new IllegalStateException("Failed to open new media item.");
            }
            // Write data into the pending media file.
            byte[] buf = new byte[8192];
            int len;
            while ((len = is.read(buf)) > 0) {
                fos.write(buf, 0, len);
            }
            fos.flush();
        } finally {
            // finished, release the "pending" status.
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                mediaDetails.clear();
                mediaDetails.put(targetMedia.isPending(), 0);
                resolver.update(mediaContentUri, mediaDetails, null, null);
            }
        }
    }

    private static Media getTargetMedia(int mediaType) {
        switch (mediaType) {
            case TYPE_IMAGE:
                return new Image();
            case TYPE_VIDEO:
                return new Video();
            case TYPE_AUDIO:
                return new Audio();
            case TYPE_FILES:
                return new Files();
            case TYPE_DOWNLOADS:
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    return new Downloads();
                }
                return new Files();
            default:
                throw new IllegalArgumentException("Unknown media type: " + mediaType);
        }
    }

    private static class Image implements Media {
        @Override
        public Uri getMediaCollection() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                return MediaStore.Images.Media
                        .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
            } else {
                return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            }
        }

        @Override
        public String displayName() {
            return MediaStore.Images.Media.DISPLAY_NAME;
        }

        @Override
        public String isPending() {
            return MediaStore.Images.Media.IS_PENDING;
        }
    }

    private static class Video implements Media {

        @Override
        public Uri getMediaCollection() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                return MediaStore.Video.Media
                        .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
            } else {
                return MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            }
        }

        @Override
        public String displayName() {
            return MediaStore.Video.Media.DISPLAY_NAME;
        }

        @Override
        public String isPending() {
            return MediaStore.Video.Media.IS_PENDING;
        }
    }

    private static class Audio implements Media {

        @Override
        public Uri getMediaCollection() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                return MediaStore.Audio.Media
                        .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
            } else {
                return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }
        }

        @Override
        public String displayName() {
            return MediaStore.Audio.Media.DISPLAY_NAME;
        }

        @Override
        public String isPending() {
            return MediaStore.Audio.Media.IS_PENDING;
        }
    }

    private static class Files implements Media {

        @Override
        public Uri getMediaCollection() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                return MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
            } else {
                return MediaStore.Files.getContentUri("external");
            }
        }

        @Override
        public String displayName() {
            return MediaStore.Files.FileColumns.DISPLAY_NAME;
        }

        @Override
        public String isPending() {
            return MediaStore.Files.FileColumns.IS_PENDING;
        }
    }

    private static class Downloads implements Media {

        @RequiresApi(api = Build.VERSION_CODES.Q)
        @Override
        public Uri getMediaCollection() {
            return MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
        }

        @Override
        public String displayName() {
            return MediaStore.Downloads.DISPLAY_NAME;
        }

        @Override
        public String isPending() {
            return MediaStore.Downloads.IS_PENDING;
        }
    }

    private interface Media {
        Uri getMediaCollection();

        String displayName();

        String isPending();
    }
}