package com.lemenzo.gallery.viewer;

import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

/** Parser/extractor for Google/Pixel Motion Photo containers. Fully local. */
public final class MotionPhotoUtils {
    private static final int MAX_XMP_SCAN = 1024 * 1024;

    private MotionPhotoUtils() {}

    /** Returns appended MP4 length in bytes, or 0 when the file is not a verified Motion Photo. */
    public static long findVideoLength(Context context, Uri uri) {
        if (context == null || uri == null) return 0L;
        try {
            String xmp = readHeaderText(context, uri);
            long bytes = MotionPhotoXmpParser.candidateVideoLength(xmp);
            return bytes > 0L && verifyTailMp4(context, uri, bytes) ? bytes : 0L;
        } catch (Exception ignored) { }
        return 0L;
    }

    public static File extractVideo(Context context, Uri uri, long videoLength) throws Exception {
        if (videoLength <= 0) throw new IllegalArgumentException("videoLength <= 0");
        File directory = new File(context.getCacheDir(), "motion-photo");
        if (!directory.exists() && !directory.mkdirs()) throw new java.io.IOException("Unable to create motion cache");
        File output = new File(directory, "motion_" + Integer.toHexString(uri.toString().hashCode()) + ".mp4");

        try (ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r")) {
            if (pfd == null) throw new java.io.IOException("Unable to open motion photo");
            try (FileInputStream in = new FileInputStream(pfd.getFileDescriptor()); FileOutputStream out = new FileOutputStream(output)) {
                long size = in.getChannel().size();
                if (videoLength > size) throw new java.io.IOException("Invalid motion video length");
                in.getChannel().position(size - videoLength);
                byte[] buffer = new byte[128 * 1024];
                long remaining = videoLength;
                while (remaining > 0) {
                    int read = in.read(buffer, 0, (int)Math.min(buffer.length, remaining));
                    if (read < 0) throw new java.io.IOException("Unexpected EOF");
                    out.write(buffer, 0, read);
                    remaining -= read;
                }
                out.getFD().sync();
            }
        }
        return output;
    }

    private static String readHeaderText(Context context, Uri uri) throws Exception {
        try (InputStream input = context.getContentResolver().openInputStream(uri)) {
            if (input == null) return "";
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[16384];
            int total = 0;
            while (total < MAX_XMP_SCAN) {
                int read = input.read(buffer, 0, Math.min(buffer.length, MAX_XMP_SCAN - total));
                if (read < 0) break;
                out.write(buffer, 0, read);
                total += read;
            }
            return new String(out.toByteArray(), StandardCharsets.ISO_8859_1);
        }
    }

    private static boolean verifyTailMp4(Context context, Uri uri, long length) {
        if (length < 16) return false;
        try (ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r")) {
            if (pfd == null) return false;
            try (FileInputStream in = new FileInputStream(pfd.getFileDescriptor())) {
                long size = in.getChannel().size();
                if (length > size) return false;
                in.getChannel().position(size - length);
                byte[] header = new byte[12];
                if (in.read(header) != header.length) return false;
                return header[4] == 'f' && header[5] == 't' && header[6] == 'y' && header[7] == 'p';
            }
        } catch (Exception e) { return false; }
    }
}
