package com.ReactNativeBlobUtil;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.util.Base64;

import com.ReactNativeBlobUtil.Utils.FileDescription;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableArray;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class ReactNativeBlobUtilMediaCollection {

    public enum MediaType {
        Audio,
        Image,
        Video,
        Download
    }

    private static Uri getMediaUri(MediaType mt) {
        Uri res = null;
        if (mt == MediaType.Audio) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                res = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
            } else {
                res = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }
        } else if (mt == MediaType.Video) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                res = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
            } else {
                res = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            }
        } else if (mt == MediaType.Image) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                res = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
            } else {
                res = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            }
        } else if (mt == MediaType.Download) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                res = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
            }
        }

        return res;
    }

    private static String getRelativePath(MediaType mt, ReactApplicationContext ctx) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            if (mt == MediaType.Audio) return Environment.DIRECTORY_MUSIC;
            if (mt == MediaType.Video) return Environment.DIRECTORY_MOVIES;
            if (mt == MediaType.Image) return Environment.DIRECTORY_PICTURES;
            if (mt == MediaType.Download) return Environment.DIRECTORY_DOWNLOADS;
            return Environment.DIRECTORY_DOWNLOADS;
        } else {
            if (mt == MediaType.Audio) return ReactNativeBlobUtilFS.getLegacySystemfolders(ctx).get("LegacyMusicDir").toString();
            if (mt == MediaType.Video) return ReactNativeBlobUtilFS.getLegacySystemfolders(ctx).get("LegacyMovieDir").toString();
            if (mt == MediaType.Image) return ReactNativeBlobUtilFS.getLegacySystemfolders(ctx).get("LegacyPictureDir").toString();
            if (mt == MediaType.Download) return ReactNativeBlobUtilFS.getLegacySystemfolders(ctx).get("LegacyDownloadDir").toString();
            return ReactNativeBlobUtilFS.getLegacySystemfolders(ctx).get("LegacyDownloadDir").toString();
        }
    }

    public static Uri createNewMediaFile(FileDescription file, MediaType mt, ReactApplicationContext ctx) {
        // Add a specific media item.
        Context appCtx = ReactNativeBlobUtilImpl.RCTContext.getApplicationContext();
        ContentResolver resolver = appCtx.getContentResolver();

        ContentValues fileDetails = new ContentValues();
        String relativePath = getRelativePath(mt, ctx);
        String mimeType = file.mimeType;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            fileDetails.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
            fileDetails.put(MediaStore.MediaColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000);
            fileDetails.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
            fileDetails.put(MediaStore.MediaColumns.DISPLAY_NAME, file.name);
            fileDetails.put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath + '/' + file.partentFolder);

            Uri mediauri = getMediaUri(mt);

            try {
                // Keeps a handle to the new file's URI in case we need to modify it later.
                return resolver.insert(mediauri, fileDetails);
            } catch (Exception e) {
                return null;
            }
        } else {
            File f = new File(relativePath + file.getFullPath());
            if (true) {
                if (!f.exists()) {
                    File parent = f.getParentFile();
                    if (parent != null && !parent.exists() && !parent.mkdirs()) {
                        return null;
                    }
                    try {
                        if (f.createNewFile()) ;
                        {
                            return Uri.fromFile(f);
                        }
                    } catch (IOException ioException) {
                        return null;
                    }

                } else {
                    return Uri.fromFile(f);
                }

            }
        }

        return null;
    }

    public static boolean writeToMediaFile(Uri fileUri, String data, boolean transformFile, Promise promise, ReactApplicationContext ctx) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            try {
                Context appCtx = ctx.getApplicationContext();
                ContentResolver resolver = appCtx.getContentResolver();

                // set pending doesn't work right now. We would have to requery for the item
                //ContentValues contentValues = new ContentValues();
                //contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);
                //resolver.update(fileUri, contentValues, null, null);

                // write data
                OutputStream stream = null;
                Uri uri = null;

                try {
                    ParcelFileDescriptor descr;
                    try {
                        assert fileUri != null;
                        descr = appCtx.getContentResolver().openFileDescriptor(fileUri, "w");
                        assert descr != null;
                        String normalizedData = ReactNativeBlobUtilUtils.normalizePath(data);
                        File src = new File(normalizedData);
                        if (!src.exists()) {
                            promise.reject("ENOENT", "No such file ('" + normalizedData + "')");
                            return false;
                        }


                        FileInputStream fin = new FileInputStream(src);
                        FileOutputStream out = new FileOutputStream(descr.getFileDescriptor());

                        if (transformFile) {
                            // in order to transform file, we must load the entire file onto memory
                            int length = (int) src.length();
                            byte[] bytes = new byte[length];
                            fin.read(bytes);
                            if (ReactNativeBlobUtilFileTransformer.sharedFileTransformer == null) {
                                throw new IllegalStateException("Write to media file with transform was specified but the shared file transformer is not set");
                            }
                            byte[] transformedBytes = ReactNativeBlobUtilFileTransformer.sharedFileTransformer.onWriteFile(bytes);
                            out.write(transformedBytes);
                        } else  {
                            byte[] buf = new byte[10240];
                            int read;

                            while ((read = fin.read(buf)) > 0) {
                                out.write(buf, 0, read);
                            }
                        }


                        fin.close();
                        out.close();
                        descr.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                        promise.reject(new IOException("Failed to get output stream."));
                        return false;
                    }

                    //contentValues.clear();
                    //contentValues.put(MediaStore.Video.Media.IS_PENDING, 0);
                    //appCtx.getContentResolver().update(fileUri, contentValues, null, null);
                    stream = resolver.openOutputStream(fileUri);
                    if (stream == null) {
                        promise.reject(new IOException("Failed to get output stream."));
                        return false;
                    }
                } catch (IOException e) {
                    // Don't leave an orphan entry in the MediaStore
                    resolver.delete(uri, null, null);
                    promise.reject(e);
                    return false;
                } finally {
                    if (stream != null) {
                        stream.close();
                    }
                }

                // remove pending
                //contentValues = new ContentValues();
                //contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
                //resolver.update(fileUri, contentValues, null, null);

            } catch (IOException e) {
                promise.reject("ReactNativeBlobUtil.createMediaFile", "Cannot write to file, file might not exist");
                return false;
            }
            return true;
        } else {
            return ReactNativeBlobUtilFS.writeFile(ReactNativeBlobUtilUtils.normalizePath(fileUri.toString()), ReactNativeBlobUtilConst.DATA_ENCODE_URI, data, false);
        }
    }

    public static void copyToInternal(Uri contenturi, String destpath, Promise promise) {
        Context appCtx = ReactNativeBlobUtilImpl.RCTContext.getApplicationContext();
        ContentResolver resolver = appCtx.getContentResolver();

        InputStream in = null;
        OutputStream out = null;
        File f = new File((destpath));

        if (!f.exists()) {
            try {
                File parent = f.getParentFile();
                if (parent != null && !parent.exists() && !parent.mkdirs()) {
                    promise.reject("ReactNativeBlobUtil.copyToInternal: Cannot create parent folders<'" + destpath);
                    return;
                }
                boolean result = f.createNewFile();
                if (!result) {
                    promise.reject("ReactNativeBlobUtil.copyToInternal: Destination file at '" + destpath + "' already exists");
                    return;
                }
            } catch (IOException ioException) {
                promise.reject("ReactNativeBlobUtil.copyToInternal: Could not create file: " + ioException.getLocalizedMessage());
            }
        }

        try {
            in = resolver.openInputStream(contenturi);
            out = new FileOutputStream(destpath);

            byte[] buf = new byte[10240];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }

        } catch (IOException e) {
            promise.reject("ReactNativeBlobUtil.copyToInternal:  Could not write data: " + e.getLocalizedMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }

        promise.resolve("");
    }

    public static void getBlob(Uri contentUri, String encoding, Promise promise) {
        Context appCtx = ReactNativeBlobUtilImpl.RCTContext.getApplicationContext();
        ContentResolver resolver = appCtx.getContentResolver();
        try {
            InputStream in = resolver.openInputStream(contentUri);
            int length = in.available();

            byte[] bytes = new byte[length];
            int bytesRead = in.read(bytes);
            in.close();

            if (bytesRead < length) {
                promise.reject("EUNSPECIFIED", "Read only " + bytesRead + " bytes of " + length);
                return;
            }

            switch (encoding.toLowerCase()) {
                case "base64":
                    promise.resolve(Base64.encodeToString(bytes, Base64.NO_WRAP));
                    break;
                case "ascii":
                    WritableArray asciiResult = Arguments.createArray();
                    for (byte b : bytes) {
                        asciiResult.pushInt(b);
                    }
                    promise.resolve(asciiResult);
                    break;
                default: // covers utf-8
                    promise.resolve(new String(bytes));
                    break;
            }
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }
}
