package com.lemenzo.gallery.editor;
import com.lemenzo.gallery.ui.SystemInsets;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageDecoder;
import android.graphics.RectF;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import com.lemenzo.gallery.R;

import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;

import com.lemenzo.gallery.util.BackgroundExecutors;

/** Privacy-first, non-destructive local photo editor. */
public final class EditorActivity extends Activity {
    private static final String TAG = "LemenzoGalleryEditor";
    private static final String EXTRA_URI = "uri";
    private static final String STATE_OPS = "operations";
    private static final String STATE_CROP = "cropMode";
    private static final String STATE_CROP_L = "cropLeft", STATE_CROP_T = "cropTop", STATE_CROP_R = "cropRight", STATE_CROP_B = "cropBottom";
    private static final String STATE_BRIGHTNESS = "brightness", STATE_CONTRAST = "contrast", STATE_SATURATION = "saturation", STATE_WARMTH = "warmth";
    private static final String STATE_EXPOSURE = "exposure", STATE_TINT = "tint", STATE_HIGHLIGHTS = "highlights", STATE_SHADOWS = "shadows", STATE_SHARPNESS = "sharpness";
    private static final String STATE_STRAIGHTEN = "straighten", STATE_PERSPECTIVE_X = "perspectiveX", STATE_PERSPECTIVE_Y = "perspectiveY", STATE_VIGNETTE = "vignette";
    private static final String STATE_FILTER = "filter", STATE_ANNOTATIONS = "annotations";
    private static final int PREVIEW_MAX_EDGE = 1600;
    private static final long PREVIEW_DEBOUNCE_MS = 36L;

    private final ExecutorService executor = BackgroundExecutors.single("LemenzoGalleryEditorIO");
    private final ExecutorService previewExecutor = BackgroundExecutors.single("LemenzoGalleryEditorPreview");
    private final Handler mainHandler = new Handler(Looper.getMainLooper());
    private final AtomicInteger previewGeneration = new AtomicInteger();
    private final ArrayList<Integer> operations = new ArrayList<>();
    private final EditHistory history = new EditHistory();
    private final ArrayList<EditorAnnotation> restoredAnnotations = new ArrayList<>();

    private ImageView preview;
    private TextView status;
    private AnnotationOverlayView annotationOverlay;
    private CropOverlayView cropOverlay;
    private Bitmap basePreviewBitmap;
    private Bitmap renderedPreviewBitmap;
    private Uri sourceUri;
    private String sourceMimeType, sourceDisplayName, sourceRelativePath;
    private boolean sourceHasAlpha;
    private float brightness, contrast, warmth, exposure, tint, highlights, shadows, sharpness, straighten, perspectiveX, perspectiveY, vignette;
    private float saturation = 1f;
    private float cropLeft = 0f, cropTop = 0f, cropRight = 1f, cropBottom = 1f;
    private int cropMode;
    private int filter = ImageAdjustments.FILTER_NONE;
    private EditState dragStartState;
    private EditState annotationStartState;

    public static Intent intent(Context context, Uri uri) {
        return new Intent(context, EditorActivity.class).setData(uri)
                .putExtra(EXTRA_URI, uri == null ? null : uri.toString())
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }

    @Override public void onCreate(Bundle state) {
        super.onCreate(state);
        setContentView(R.layout.activity_editor);
        SystemInsets.apply(this);
        preview = findViewById(R.id.editor_preview);
        status = findViewById(R.id.editor_status);
        annotationOverlay = findViewById(R.id.annotation_overlay);
        cropOverlay = findViewById(R.id.crop_overlay);
        sourceUri = resolveSource(getIntent());
        if (state != null) restoreState(state);

        annotationOverlay.setContentDescription(getString(R.string.markup_canvas));
        cropOverlay.setContentDescription(getString(R.string.free_crop_canvas));
        annotationOverlay.setListener(new AnnotationOverlayView.Listener() {
            @Override public void onAnnotationActionStart() { annotationStartState = snapshot(); }
            @Override public void onAnnotationActionEnd() {
                if (annotationStartState != null) history.checkpoint(annotationStartState);
                annotationStartState = null; updateHistoryButtons();
            }
        });

        findViewById(R.id.close_button).setOnClickListener(v -> finish());
        findViewById(R.id.reset_button).setOnClickListener(v -> reset());
        findViewById(R.id.undo_button).setOnClickListener(v -> undo());
        findViewById(R.id.redo_button).setOnClickListener(v -> redo());
        findViewById(R.id.rotate_left_button).setOnClickListener(v -> addOperation(EditPipeline.ROTATE_LEFT));
        findViewById(R.id.rotate_right_button).setOnClickListener(v -> addOperation(EditPipeline.ROTATE_RIGHT));
        findViewById(R.id.flip_button).setOnClickListener(v -> addOperation(EditPipeline.FLIP_HORIZONTAL));
        findViewById(R.id.crop_button).setOnClickListener(v -> cycleCrop());
        findViewById(R.id.free_crop_button).setOnClickListener(v -> toggleFreeCrop());
        findViewById(R.id.draw_button).setOnClickListener(v -> setMarkupMode(AnnotationOverlayView.MODE_DRAW));
        findViewById(R.id.redact_button).setOnClickListener(v -> setMarkupMode(AnnotationOverlayView.MODE_REDACT));
        findViewById(R.id.text_button).setOnClickListener(v -> addText());
        findViewById(R.id.clear_markup_button).setOnClickListener(v -> clearMarkup());
        findViewById(R.id.auto_enhance_button).setOnClickListener(v -> autoEnhance());
        findViewById(R.id.filter_original_button).setOnClickListener(v -> setFilter(ImageAdjustments.FILTER_NONE));
        findViewById(R.id.filter_vivid_button).setOnClickListener(v -> setFilter(ImageAdjustments.FILTER_VIVID));
        findViewById(R.id.filter_mono_button).setOnClickListener(v -> setFilter(ImageAdjustments.FILTER_MONO));
        findViewById(R.id.filter_cool_button).setOnClickListener(v -> setFilter(ImageAdjustments.FILTER_COOL));
        findViewById(R.id.filter_warm_button).setOnClickListener(v -> setFilter(ImageAdjustments.FILTER_WARM));
        findViewById(R.id.save_copy_button).setOnClickListener(v -> saveCopy(true));
        findViewById(R.id.save_private_copy_button).setOnClickListener(v -> saveCopy(false));
        findViewById(R.id.editor_tab_adjust).setOnClickListener(v -> selectEditorPanel(0));
        findViewById(R.id.editor_tab_crop).setOnClickListener(v -> selectEditorPanel(1));
        findViewById(R.id.editor_tab_filters).setOnClickListener(v -> selectEditorPanel(2));
        findViewById(R.id.editor_tab_markup).setOnClickListener(v -> selectEditorPanel(3));
        selectEditorPanel(0);

        bindAdjustmentSeekBars();
        updateSeekBarsFromState();
        updateFilterButtons();
        updateHistoryButtons();
        annotationOverlay.setAnnotations(restoredAnnotations);

        if (sourceUri == null) {
            Toast.makeText(this, R.string.editor_unable_to_open, Toast.LENGTH_LONG).show(); finish(); return;
        }
        querySourceInfo(); loadPreview();
    }

    @Override protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        EditState s = snapshot();
        outState.putIntegerArrayList(STATE_OPS, new ArrayList<>(s.operations));
        outState.putInt(STATE_CROP, s.cropMode);
        outState.putFloat(STATE_CROP_L,s.cropLeft); outState.putFloat(STATE_CROP_T,s.cropTop); outState.putFloat(STATE_CROP_R,s.cropRight); outState.putFloat(STATE_CROP_B,s.cropBottom);
        outState.putFloat(STATE_BRIGHTNESS,s.brightness); outState.putFloat(STATE_CONTRAST,s.contrast); outState.putFloat(STATE_SATURATION,s.saturation); outState.putFloat(STATE_WARMTH,s.warmth);
        outState.putFloat(STATE_EXPOSURE,s.exposure); outState.putFloat(STATE_TINT,s.tint); outState.putFloat(STATE_HIGHLIGHTS,s.highlights); outState.putFloat(STATE_SHADOWS,s.shadows); outState.putFloat(STATE_SHARPNESS,s.sharpness);
        outState.putFloat(STATE_STRAIGHTEN,s.straighten); outState.putFloat(STATE_PERSPECTIVE_X,s.perspectiveX); outState.putFloat(STATE_PERSPECTIVE_Y,s.perspectiveY); outState.putFloat(STATE_VIGNETTE,s.vignette);
        outState.putInt(STATE_FILTER,s.filter);
        outState.putSerializable(STATE_ANNOTATIONS, new ArrayList<>(s.annotations));
    }

    @SuppressWarnings("unchecked")
    private void restoreState(Bundle state) {
        ArrayList<Integer> savedOps = state.getIntegerArrayList(STATE_OPS); if (savedOps != null) operations.addAll(savedOps);
        cropMode=state.getInt(STATE_CROP,0); cropLeft=state.getFloat(STATE_CROP_L,0f); cropTop=state.getFloat(STATE_CROP_T,0f); cropRight=state.getFloat(STATE_CROP_R,1f); cropBottom=state.getFloat(STATE_CROP_B,1f);
        brightness=state.getFloat(STATE_BRIGHTNESS,0f); contrast=state.getFloat(STATE_CONTRAST,0f); saturation=state.getFloat(STATE_SATURATION,1f); warmth=state.getFloat(STATE_WARMTH,0f);
        exposure=state.getFloat(STATE_EXPOSURE,0f); tint=state.getFloat(STATE_TINT,0f); highlights=state.getFloat(STATE_HIGHLIGHTS,0f); shadows=state.getFloat(STATE_SHADOWS,0f); sharpness=state.getFloat(STATE_SHARPNESS,0f);
        straighten=state.getFloat(STATE_STRAIGHTEN,0f); perspectiveX=state.getFloat(STATE_PERSPECTIVE_X,0f); perspectiveY=state.getFloat(STATE_PERSPECTIVE_Y,0f); vignette=state.getFloat(STATE_VIGNETTE,0f);
        filter=state.getInt(STATE_FILTER,ImageAdjustments.FILTER_NONE);
        Object serial = state.getSerializable(STATE_ANNOTATIONS);
        if (serial instanceof ArrayList<?>) for (Object o : (ArrayList<?>)serial) if (o instanceof EditorAnnotation) restoredAnnotations.add(new EditorAnnotation((EditorAnnotation)o));
    }

    private Uri resolveSource(Intent intent) {
        Uri data=intent==null?null:intent.getData(); if(data!=null)return data;
        String value=intent==null?null:intent.getStringExtra(EXTRA_URI); return value==null?null:Uri.parse(value);
    }

    private void selectEditorPanel(int selected) {
        int[] panels={R.id.editor_panel_adjust,R.id.editor_panel_crop,R.id.editor_panel_filters,R.id.editor_panel_markup};
        int[] tabs={R.id.editor_tab_adjust,R.id.editor_tab_crop,R.id.editor_tab_filters,R.id.editor_tab_markup};
        for(int i=0;i<panels.length;i++){
            View panel=findViewById(panels[i]);
            TextView tab=findViewById(tabs[i]);
            boolean active=i==selected;
            panel.setVisibility(active?View.VISIBLE:View.GONE);
            tab.setBackgroundResource(active?R.drawable.bg_editor_tab_selected:R.drawable.bg_editor_tab_normal);
            tab.setTextColor(getColor(active?R.color.gallery_text:R.color.gallery_text_secondary));
        }
        View scroll=findViewById(R.id.editor_panel_scroll);
        if(scroll!=null)scroll.scrollTo(0,0);
        if(selected!=3)setMarkupMode(AnnotationOverlayView.MODE_NONE);
        if(selected!=1)hideCropOverlay();
    }

    private interface ProgressConsumer { void accept(int progress); }
    private void bindAdjustmentSeekBars() {
        bindSeekBar(R.id.brightness_seek,p->brightness=centered(p));
        bindSeekBar(R.id.contrast_seek,p->contrast=centered(p));
        bindSeekBar(R.id.saturation_seek,p->saturation=p/50f);
        bindSeekBar(R.id.warmth_seek,p->warmth=centered(p));
        bindSeekBar(R.id.exposure_seek,p->exposure=centered(p));
        bindSeekBar(R.id.tint_seek,p->tint=centered(p));
        bindSeekBar(R.id.highlights_seek,p->highlights=centered(p), false);
        bindSeekBar(R.id.shadows_seek,p->shadows=centered(p), false);
        bindSeekBar(R.id.sharpness_seek,p->sharpness=p/100f, false);
        bindSeekBar(R.id.straighten_seek,p->straighten=(p-50)/5f);
        bindSeekBar(R.id.perspective_x_seek,p->perspectiveX=(p-50)/250f);
        bindSeekBar(R.id.perspective_y_seek,p->perspectiveY=(p-50)/250f);
        bindSeekBar(R.id.vignette_seek,p->vignette=p/100f);
    }
    private float centered(int p){return(p-50)/50f;}
    private void bindSeekBar(int id, ProgressConsumer consumer) { bindSeekBar(id, consumer, true); }
    private void bindSeekBar(int id, ProgressConsumer consumer, boolean livePreview) {
        SeekBar bar=findViewById(id); bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){
            @Override public void onProgressChanged(SeekBar s,int p,boolean fromUser){if(fromUser){consumer.accept(p);if(livePreview)renderPreview();}}
            @Override public void onStartTrackingTouch(SeekBar s){dragStartState=snapshot();}
            @Override public void onStopTrackingTouch(SeekBar s){if(dragStartState!=null)history.checkpoint(dragStartState);dragStartState=null;if(!livePreview)renderPreview();updateHistoryButtons();}
        });
    }

    private void updateSeekBarsFromState(){
        setProgress(R.id.brightness_seek,toCentered(brightness)); setProgress(R.id.contrast_seek,toCentered(contrast)); setProgress(R.id.saturation_seek,clamp100(Math.round(saturation*50f)));
        setProgress(R.id.warmth_seek,toCentered(warmth)); setProgress(R.id.exposure_seek,toCentered(exposure)); setProgress(R.id.tint_seek,toCentered(tint));
        setProgress(R.id.highlights_seek,toCentered(highlights)); setProgress(R.id.shadows_seek,toCentered(shadows)); setProgress(R.id.sharpness_seek,clamp100(Math.round(sharpness*100f)));
        setProgress(R.id.straighten_seek,clamp100(Math.round(straighten*5f+50f))); setProgress(R.id.perspective_x_seek,clamp100(Math.round(perspectiveX*250f+50f))); setProgress(R.id.perspective_y_seek,clamp100(Math.round(perspectiveY*250f+50f))); setProgress(R.id.vignette_seek,clamp100(Math.round(vignette*100f)));
    }
    private void setProgress(int id,int p){((SeekBar)findViewById(id)).setProgress(p);} private int toCentered(float v){return clamp100(Math.round(v*50f+50f));} private int clamp100(int p){return Math.max(0,Math.min(100,p));}

    private RectF freeCrop(){return new RectF(cropLeft,cropTop,cropRight,cropBottom);}
    private EditState snapshot(){
        List<EditorAnnotation> annotations=annotationOverlay==null?restoredAnnotations:annotationOverlay.getAnnotations();
        return new EditState(operations,cropMode,cropLeft,cropTop,cropRight,cropBottom,brightness,contrast,saturation,warmth,exposure,tint,highlights,shadows,sharpness,straighten,perspectiveX,perspectiveY,vignette,filter,annotations);
    }
    private void applyState(EditState s){if(s==null)return;operations.clear();operations.addAll(s.operations);cropMode=s.cropMode;cropLeft=s.cropLeft;cropTop=s.cropTop;cropRight=s.cropRight;cropBottom=s.cropBottom;brightness=s.brightness;contrast=s.contrast;saturation=s.saturation;warmth=s.warmth;exposure=s.exposure;tint=s.tint;highlights=s.highlights;shadows=s.shadows;sharpness=s.sharpness;straighten=s.straighten;perspectiveX=s.perspectiveX;perspectiveY=s.perspectiveY;vignette=s.vignette;filter=s.filter;if(annotationOverlay!=null)annotationOverlay.setAnnotations(s.annotations);hideCropOverlay();setMarkupMode(AnnotationOverlayView.MODE_NONE);updateSeekBarsFromState();updateFilterButtons();updateCropStatus();renderPreview();updateHistoryButtons();}
    private void undo(){applyState(history.undo(snapshot()));} private void redo(){applyState(history.redo(snapshot()));} private void checkpoint(){history.checkpoint(snapshot());updateHistoryButtons();}
    private void updateHistoryButtons(){findViewById(R.id.undo_button).setEnabled(history.canUndo());findViewById(R.id.redo_button).setEnabled(history.canRedo());}

    private void querySourceInfo(){sourceMimeType=getContentResolver().getType(sourceUri);String[] projection={MediaStore.MediaColumns.DISPLAY_NAME,MediaStore.MediaColumns.MIME_TYPE,MediaStore.MediaColumns.RELATIVE_PATH};try(Cursor c=getContentResolver().query(sourceUri,projection,null,null,null)){if(c!=null&&c.moveToFirst()){int n=c.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME),m=c.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE),p=c.getColumnIndex(MediaStore.MediaColumns.RELATIVE_PATH);if(n>=0)sourceDisplayName=c.getString(n);if(m>=0&&c.getString(m)!=null)sourceMimeType=c.getString(m);if(p>=0)sourceRelativePath=c.getString(p);}}catch(Exception e){Log.d(TAG,"Source metadata query unavailable",e);}}

    private void loadPreview(){status.setText(R.string.editor_loading);executor.execute(()->{try{Bitmap decoded=decode(sourceUri,PREVIEW_MAX_EDGE);sourceHasAlpha=decoded.hasAlpha();runOnUiThread(()->{if(isFinishing()||isDestroyed()){decoded.recycle();return;}basePreviewBitmap=decoded;updateCropStatus();renderPreview();});}catch(Exception e){Log.e(TAG,"Unable to decode image preview",e);runOnUiThread(()->{Toast.makeText(this,R.string.editor_unable_to_open,Toast.LENGTH_LONG).show();finish();});}});}
    private int[] readSourceDimensions(Uri uri)throws IOException{BitmapFactory.Options o=new BitmapFactory.Options();o.inJustDecodeBounds=true;try(java.io.InputStream in=getContentResolver().openInputStream(uri)){if(in==null)throw new IOException("Unable to open source image");BitmapFactory.decodeStream(in,null,o);}return new int[]{Math.max(0,o.outWidth),Math.max(0,o.outHeight)};}
    private Bitmap decode(Uri uri,int maxEdge)throws IOException{ImageDecoder.Source s=ImageDecoder.createSource(getContentResolver(),uri);return ImageDecoder.decodeBitmap(s,(decoder,info,src)->{int w=info.getSize().getWidth(),h=info.getSize().getHeight(),requested=maxEdge;if(requested<0)requested=BitmapBudget.safeDecodeMaxEdge(w,h);if(requested>0&&Math.max(w,h)>requested){float scale=requested/(float)Math.max(w,h);decoder.setTargetSize(Math.max(1,Math.round(w*scale)),Math.max(1,Math.round(h*scale)));}decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);decoder.setMutableRequired(false);});}

    private void addOperation(int op){if(basePreviewBitmap==null)return;checkpoint();operations.add(op);renderPreview();}
    private void cycleCrop(){if(basePreviewBitmap==null)return;checkpoint();hideCropOverlay();cropLeft=0;cropTop=0;cropRight=1;cropBottom=1;cropMode=(cropMode+1)%4;updateCropStatus();renderPreview();}
    private void toggleFreeCrop(){if(basePreviewBitmap==null)return;if(cropOverlay.getVisibility()!=View.VISIBLE){setMarkupMode(AnnotationOverlayView.MODE_NONE);if(cropMode!=0){checkpoint();cropMode=0;cropLeft=0f;cropTop=0f;cropRight=1f;cropBottom=1f;renderPreview();}cropOverlay.resetSelection();cropOverlay.setVisibility(View.VISIBLE);status.setText(R.string.free_crop_instruction);}else{checkpoint();RectF s=cropOverlay.getSelection();float oldL=cropLeft,oldT=cropTop,w=cropRight-cropLeft,h=cropBottom-cropTop;cropLeft=oldL+w*s.left;cropTop=oldT+h*s.top;cropRight=oldL+w*s.right;cropBottom=oldT+h*s.bottom;cropMode=0;hideCropOverlay();status.setText(R.string.free_crop_applied);renderPreview();}}
    private void hideCropOverlay(){if(cropOverlay!=null)cropOverlay.setVisibility(View.GONE);}
    private void setFilter(int f){if(basePreviewBitmap==null||filter==f)return;checkpoint();filter=f;updateFilterButtons();status.setText(R.string.filter_selected);renderPreview();}
    private void updateFilterButtons(){
        int[] ids={R.id.filter_original_button,R.id.filter_vivid_button,R.id.filter_mono_button,R.id.filter_cool_button,R.id.filter_warm_button};
        int[] values={ImageAdjustments.FILTER_NONE,ImageAdjustments.FILTER_VIVID,ImageAdjustments.FILTER_MONO,ImageAdjustments.FILTER_COOL,ImageAdjustments.FILTER_WARM};
        for(int i=0;i<ids.length;i++){
            TextView button=findViewById(ids[i]);
            boolean selected=filter==values[i];
            button.setBackgroundResource(selected?R.drawable.bg_editor_tab_selected:R.drawable.bg_editor_chip);
            button.setTextColor(getColor(selected?R.color.gallery_text:R.color.gallery_text_secondary));
        }
    }

    private void setMarkupMode(int mode){if(annotationOverlay==null)return;hideCropOverlay();annotationOverlay.setMode(mode);annotationOverlay.setVisibility(mode==AnnotationOverlayView.MODE_NONE&&annotationOverlay.getAnnotations().isEmpty()?View.GONE:View.VISIBLE);if(mode==AnnotationOverlayView.MODE_DRAW)status.setText(R.string.draw_instruction);else if(mode==AnnotationOverlayView.MODE_REDACT)status.setText(R.string.redact_instruction);}
    private void addText(){if(basePreviewBitmap==null)return;setMarkupMode(AnnotationOverlayView.MODE_NONE);EditText input=new EditText(this);input.setSingleLine(false);input.setHint(R.string.text_hint);new AlertDialog.Builder(this).setTitle(R.string.add_text).setView(input).setNegativeButton(android.R.string.cancel,null).setPositiveButton(R.string.add,(d,w)->{String t=input.getText().toString().trim();if(t.isEmpty())return;checkpoint();annotationOverlay.setVisibility(View.VISIBLE);annotationOverlay.addCenteredText(t);status.setText(R.string.text_added);updateHistoryButtons();}).show();}
    private void clearMarkup(){if(annotationOverlay.getAnnotations().isEmpty())return;checkpoint();annotationOverlay.clearAnnotations();annotationOverlay.setMode(AnnotationOverlayView.MODE_NONE);annotationOverlay.setVisibility(View.GONE);status.setText(R.string.markup_cleared);}

    private void autoEnhance(){
        if(basePreviewBitmap==null)return;
        checkpoint();
        int[] histogram=new int[256]; long count=0; double sumR=0,sumG=0,sumB=0,sumChroma=0;
        int w=basePreviewBitmap.getWidth(),h=basePreviewBitmap.getHeight();
        int step=Math.max(1,(int)Math.sqrt(Math.max(1L,(long)w*h/65000L)));
        for(int y=0;y<h;y+=step){for(int x=0;x<w;x+=step){int c=basePreviewBitmap.getPixel(x,y);int r=android.graphics.Color.red(c),g=android.graphics.Color.green(c),b=android.graphics.Color.blue(c);int lum=Math.max(0,Math.min(255,(r*54+g*183+b*19)>>8));histogram[lum]++;count++;sumR+=r;sumG+=g;sumB+=b;sumChroma+=Math.max(r,Math.max(g,b))-Math.min(r,Math.min(g,b));}}
        if(count<=0)return;
        AutoEnhanceAnalyzer.Recommendation rec=AutoEnhanceAnalyzer.recommend(histogram,count,sumR/count,sumG/count,sumB/count,sumChroma/count);
        brightness=rec.brightness;contrast=rec.contrast;saturation=rec.saturation;warmth=rec.warmth;exposure=rec.exposure;tint=rec.tint;highlights=rec.highlights;shadows=rec.shadows;sharpness=rec.sharpness;filter=ImageAdjustments.FILTER_NONE;vignette=0f;
        updateSeekBarsFromState();status.setText(R.string.auto_enhance_applied);renderPreview();updateHistoryButtons();
    }

    private float cropAspectRatio(){return cropAspectRatio(cropMode);} private float cropAspectRatio(int mode){if(mode==1)return 1f;if(mode==2)return 4f/3f;if(mode==3)return 16f/9f;return 0f;}
    private void updateCropStatus(){if(cropMode==1)status.setText(R.string.crop_square_selected);else if(cropMode==2)status.setText(R.string.crop_four_three_selected);else if(cropMode==3)status.setText(R.string.crop_sixteen_nine_selected);else status.setText(R.string.editor_ready);}
    private void reset(){if(basePreviewBitmap==null)return;checkpoint();operations.clear();cropMode=0;cropLeft=0;cropTop=0;cropRight=1;cropBottom=1;brightness=0;contrast=0;saturation=1;warmth=0;exposure=0;tint=0;highlights=0;shadows=0;sharpness=0;straighten=0;perspectiveX=0;perspectiveY=0;vignette=0;filter=ImageAdjustments.FILTER_NONE;annotationOverlay.clearAnnotations();annotationOverlay.setVisibility(View.GONE);hideCropOverlay();updateSeekBarsFromState();updateFilterButtons();updateCropStatus();renderPreview();}

    private void renderPreview(){
        if(basePreviewBitmap==null)return;
        final int generation=previewGeneration.incrementAndGet();
        final EditState state=snapshot();
        mainHandler.removeCallbacksAndMessages(this);
        mainHandler.postAtTime(() -> {
            if(isFinishing()||isDestroyed()||generation!=previewGeneration.get()||basePreviewBitmap==null)return;
            final Bitmap source=basePreviewBitmap;
            previewExecutor.execute(() -> {
                Bitmap rendered=null;
                try{
                    if(Thread.currentThread().isInterrupted()||generation!=previewGeneration.get())return;
                    rendered=EditPipeline.render(source,state.operations,cropAspectRatio(state.cropMode),new RectF(state.cropLeft,state.cropTop,state.cropRight,state.cropBottom),state.straighten,state.perspectiveX,state.perspectiveY,state.brightness,state.contrast,state.saturation,state.warmth,state.exposure,state.tint,state.highlights,state.shadows,state.sharpness,state.vignette,state.filter);
                    final Bitmap result=rendered; rendered=null;
                    runOnUiThread(() -> {
                        if(isFinishing()||isDestroyed()||generation!=previewGeneration.get()){if(result!=source&&!result.isRecycled())result.recycle();return;}
                        Bitmap old=renderedPreviewBitmap;
                        renderedPreviewBitmap=result;
                        preview.setImageBitmap(result);
                        annotationOverlay.setImageDimensions(result.getWidth(),result.getHeight());
                        cropOverlay.setImageDimensions(result.getWidth(),result.getHeight());
                        if(!annotationOverlay.getAnnotations().isEmpty())annotationOverlay.setVisibility(View.VISIBLE);
                        if(old!=null&&old!=source&&old!=result&&!old.isRecycled())old.recycle();
                    });
                }catch(OutOfMemoryError e){
                    if(rendered!=null&&rendered!=source&&!rendered.isRecycled())rendered.recycle();
                    Log.e(TAG,"Preview edit ran out of memory",e);
                    runOnUiThread(() -> {if(!isFinishing()&&!isDestroyed())Toast.makeText(this,R.string.editor_memory_error,Toast.LENGTH_LONG).show();});
                }catch(RuntimeException e){
                    if(rendered!=null&&rendered!=source&&!rendered.isRecycled())rendered.recycle();
                    Log.e(TAG,"Preview render failed",e);
                }
            });
        },this,android.os.SystemClock.uptimeMillis()+PREVIEW_DEBOUNCE_MS);
    }

    private void saveCopy(boolean preserveMetadata){if(basePreviewBitmap==null)return;if(cropOverlay.getVisibility()==View.VISIBLE)toggleFreeCrop();setMarkupMode(AnnotationOverlayView.MODE_NONE);final EditState export=snapshot();final String exportMime=sourceMimeType;final boolean exportAlpha=sourceHasAlpha;status.setText(R.string.editor_saving_full_resolution);setEditingEnabled(false);executor.execute(()->{Uri saved=null;Bitmap original=null,outputBitmap=null;try{int[] sourceSize=readSourceDimensions(sourceUri);int safeEdge=BitmapBudget.safeDecodeMaxEdge(sourceSize[0],sourceSize[1]);original=decode(sourceUri,safeEdge>0?safeEdge:-1);outputBitmap=EditPipeline.render(original,export.operations,cropAspectRatio(export.cropMode),new RectF(export.cropLeft,export.cropTop,export.cropRight,export.cropBottom),export.straighten,export.perspectiveX,export.perspectiveY,export.brightness,export.contrast,export.saturation,export.warmth,export.exposure,export.tint,export.highlights,export.shadows,export.sharpness,export.vignette,export.filter);if(!export.annotations.isEmpty()){try{if(outputBitmap.hasGainmap())outputBitmap.setGainmap(null);}catch(Exception ignored){}EditOverlayRenderer.renderInPlace(outputBitmap,export.annotations);}EditorOutputFormat format=EditorOutputFormat.forSource(exportMime,exportAlpha);saved=getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,buildOutputValues(format,!preserveMetadata));if(saved==null)throw new IOException("MediaStore insert returned null");try(OutputStream out=getContentResolver().openOutputStream(saved,"w")){if(out==null||!outputBitmap.compress(format.compressFormat,format.quality,out))throw new IOException("Unable to encode edited image");}if(preserveMetadata)EditorMetadata.copy(getContentResolver(),sourceUri,saved);ContentValues publish=new ContentValues();publish.put(MediaStore.Images.Media.IS_PENDING,0);if(getContentResolver().update(saved,publish,null,null)!=1)throw new IOException("Unable to publish edited image");}catch(OutOfMemoryError e){Log.e(TAG,"Full-resolution export ran out of memory",e);if(saved!=null)getContentResolver().delete(saved,null,null);saved=null;}catch(Exception e){Log.e(TAG,"Unable to save edited image",e);if(saved!=null)getContentResolver().delete(saved,null,null);saved=null;}finally{if(outputBitmap!=null&&outputBitmap!=original&&!outputBitmap.isRecycled())outputBitmap.recycle();if(original!=null&&!original.isRecycled())original.recycle();}Uri result=saved;runOnUiThread(()->{if(isFinishing()||isDestroyed())return;setEditingEnabled(true);if(result==null){status.setText(R.string.editor_save_failed);Toast.makeText(this,R.string.editor_save_failed_full_resolution,Toast.LENGTH_LONG).show();}else{status.setText(preserveMetadata?R.string.editor_saved:R.string.editor_private_saved);Toast.makeText(this,preserveMetadata?R.string.editor_saved:R.string.editor_private_saved,Toast.LENGTH_SHORT).show();setResult(RESULT_OK,new Intent().setData(result).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION));}});});}

    private ContentValues buildOutputValues(EditorOutputFormat format,boolean privateCopy){ContentValues v=new ContentValues();String stamp=new SimpleDateFormat("yyyyMMdd_HHmmss",Locale.US).format(new Date());String base=sourceDisplayName==null?"Lemenzo":stripExtension(sourceDisplayName);v.put(MediaStore.Images.Media.DISPLAY_NAME,base+(privateCopy?"_private_edited_":"_edited_")+stamp+format.extension);v.put(MediaStore.Images.Media.MIME_TYPE,format.mimeType);String path=sourceRelativePath;if(path==null||path.trim().isEmpty())path=Environment.DIRECTORY_PICTURES+"/Lemenzo";v.put(MediaStore.Images.Media.RELATIVE_PATH,path);v.put(MediaStore.Images.Media.IS_PENDING,1);return v;}
    private String stripExtension(String n){int d=n.lastIndexOf('.');return d>0?n.substring(0,d):n;}
    private void setEditingEnabled(boolean enabled){int[] ids={R.id.reset_button,R.id.undo_button,R.id.redo_button,R.id.rotate_left_button,R.id.rotate_right_button,R.id.flip_button,R.id.crop_button,R.id.free_crop_button,R.id.draw_button,R.id.redact_button,R.id.text_button,R.id.clear_markup_button,R.id.auto_enhance_button,R.id.filter_original_button,R.id.filter_vivid_button,R.id.filter_mono_button,R.id.filter_cool_button,R.id.filter_warm_button,R.id.save_copy_button,R.id.save_private_copy_button,R.id.editor_tab_adjust,R.id.editor_tab_crop,R.id.editor_tab_filters,R.id.editor_tab_markup,R.id.brightness_seek,R.id.contrast_seek,R.id.saturation_seek,R.id.warmth_seek,R.id.exposure_seek,R.id.tint_seek,R.id.highlights_seek,R.id.shadows_seek,R.id.sharpness_seek,R.id.straighten_seek,R.id.perspective_x_seek,R.id.perspective_y_seek,R.id.vignette_seek};for(int id:ids)findViewById(id).setEnabled(enabled);annotationOverlay.setEnabled(enabled);cropOverlay.setEnabled(enabled);if(enabled)updateHistoryButtons();}

    @Override protected void onDestroy(){previewGeneration.incrementAndGet();mainHandler.removeCallbacksAndMessages(this);previewExecutor.shutdownNow();executor.shutdownNow();preview.setImageDrawable(null);renderedPreviewBitmap=null;basePreviewBitmap=null;super.onDestroy();}
}
