package com.qualcomm.qti.snpe.imageclassifiers.thread;

import static com.qualcomm.qti.snpe.imageclassifiers.detector.RetinaDetector.IMG_HEIGHT;
import static com.qualcomm.qti.snpe.imageclassifiers.detector.RetinaDetector.IMG_WIDTH;

import static org.bytedeco.javacpp.Loader.getCacheDir;

import android.app.Application;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.Log;

import com.qualcomm.qti.snpe.imageclassifiers.MainActivity;
import com.qualcomm.qti.snpe.imageclassifiers.R;
import com.qualcomm.qti.snpe.imageclassifiers.detector.Bbox;
import com.qualcomm.qti.snpe.imageclassifiers.detector.RetinaDetector;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;

public class AiSlicingThread extends Thread{
    /** class's constance*/
    private static final String LOGTAG = AiSlicingThread.class.getSimpleName();
    private static final int MAX_QUEUE_SIZE = 20;
    private static final String AI_SLICING_THREAD = "AI slicing thread";
    public static final int TOTAL_SLICING_PER_IMAGE = 7;

    /** class main attribute */
    private LinkedBlockingDeque<ImageSlice> aiSlicesQueue;
    private RetinaDetector mDetector;
    private AISliceMergingThread aiSliceMergingThread;
    private boolean isProcess = true;
    private Context mContext;

    /** Constructor 1: receive exist detector */
    public AiSlicingThread(RetinaDetector mDetector, AISliceMergingThread aiSliceMergingThread) {
        this.aiSlicesQueue = new LinkedBlockingDeque<>();
        this.aiSliceMergingThread = aiSliceMergingThread;
        this.mDetector = mDetector;
    }

    /** Constructor 2: using default detector */
    public AiSlicingThread(Application application, Context context, AISliceMergingThread aiSliceMergingThread) {
        this.aiSliceMergingThread = aiSliceMergingThread;
        this.aiSlicesQueue = new LinkedBlockingDeque<>();
        mContext = context;
        this.aiSlicesQueue = new LinkedBlockingDeque<ImageSlice>();
        this.mDetector = new RetinaDetector(context, application, R.raw.retina_mb_nosm_h288_w512_quantized);
        this.setName(AI_SLICING_THREAD);
    }

    /** do loop slicing */
    @Override
    public void run() {

        while (isProcess) {
            /** need sleep to cpu scheduling*/
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (aiSlicesQueue.size() > 0){
                try {
                    ImageSlice imageSlice = aiSlicesQueue.takeFirst();
                    Bitmap bmp = imageSlice.getBmp();
                    int frameId = imageSlice.getFrameId();
                    SliceCropBox sliceCropBox = imageSlice.getSliceCropBox();

                    /** Preprocess input image */
                    Bitmap croppedBmp1 = Bitmap.createBitmap(bmp,
                            sliceCropBox.getX(), sliceCropBox.getY(),
                            sliceCropBox.getWidth(), sliceCropBox.getHeight());

                    final float scaleX = IMG_WIDTH / (float) (croppedBmp1.getWidth());
                    final float scaleY = IMG_HEIGHT / (float) (croppedBmp1.getHeight());
                    final Matrix scalingMatrix = new Matrix();
                    scalingMatrix.postScale(scaleX, scaleY);

                    Bitmap resizedcroppedBmp1 = Bitmap.createBitmap(croppedBmp1,
                            0, 0,
                            croppedBmp1.getWidth(), croppedBmp1.getHeight(),scalingMatrix,false);
                    List<Bbox> detectedBoxes =  startAIFlowDetect(mDetector, resizedcroppedBmp1, frameId,scaleX,scaleY,sliceCropBox.getX(),sliceCropBox.getY());
                    Log.d(LOGTAG +"_boxResultInfo", "box count= " + detectedBoxes.size());
//                    final Bitmap bmpcopy = bmp.copy(Bitmap.Config.ARGB_8888, true);
//                    Canvas canvas = new Canvas(bmpcopy);
//
//                    Paint paint = new Paint();
//                    //paint.setAlpha(0xA0); // the transparency
//                    paint.setColor(Color.RED); // color is red
//                    paint.setStyle(Paint.Style.STROKE); // stroke or fill or ...
//                    paint.setStrokeWidth(1); // the stroke width
//                    for (Bbox box0 : detectedBoxes){
//                        Log.d(LOGTAG + "singleBoxInfo", "co-ord= " + box0.x1 + " " + box0.x2 +" " + box0.y1 + " " + box0.y2 + " | conf = " + box0.conf);
//                        Rect r = new Rect((int)box0.x1,  (int)box0.y1, (int)box0.x2, (int)box0.y2);
//                        canvas.drawRect(r, paint);
//
//                    }
//                    String filename = System.currentTimeMillis() + "_"+frameId + "_" + this.getName() ;
                    aiSliceMergingThread.addItem(new AIResult(frameId, detectedBoxes, bmp));
//                    Log.d(LOGTAG + "detecting slice", "saving slice image");
//                    savebitmap(bmpcopy, filename);



                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

        /** Call to destroy detector to end thread process */
        if (mDetector != null) {
            mDetector.close();
        }
    }

    public boolean addSlicedImage(ImageSlice imageSlice){
        if (aiSlicesQueue.size() >= MAX_QUEUE_SIZE){
            try {
                aiSlicesQueue.takeFirst();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return aiSlicesQueue.offerLast(imageSlice);
    }

    private List<Bbox> startAIFlowDetect(RetinaDetector mDetector, Bitmap bmp, int frameId,float scaleX,float scaleY,int startX,int startY) throws IOException {
        List<Bbox> detectedBoxes = new ArrayList<>();
        final Bitmap resizedBmpCopy = bmp.copy(Bitmap.Config.ARGB_8888, true);

        detectedBoxes = mDetector.detectFrame(bmp);
        for (Bbox box0 : detectedBoxes){
            box0.x1 = box0.x1 / scaleX +startX;
            box0.x2 = box0.x2 /scaleX + startX;
            box0.y1 = box0.y1 /scaleY+ startY ;
            box0.y2 = box0.y2 /scaleY + startY;

        }
//        Canvas canvas = new Canvas(resizedBmpCopy);
//
//        Paint paint = new Paint();
//        //paint.setAlpha(0xA0); // the transparency
//        paint.setColor(Color.RED); // color is red
//        paint.setStyle(Paint.Style.STROKE); // stroke or fill or ...
//        paint.setStrokeWidth(1); // the stroke width
//        for (Bbox box0 : detectedBoxes){
//            Log.d(LOGTAG + "singleBoxInfo", "co-ord= " + box0.x1 + " " + box0.x2 +" " + box0.y1 + " " + box0.y2 + " | conf = " + box0.conf);
//            Rect r = new Rect((int)box0.x1,  (int)box0.y1, (int)box0.x2, (int)box0.y2);
//
//            canvas.drawRect(r, paint);
//        }
//
//        String filename = System.currentTimeMillis() + "_"+frameId + "_" + this.getName() ;
//        savebitmap(resizedBmpCopy, filename);
        return detectedBoxes;
    }

    public File savebitmap(Bitmap bmp, String filename) throws IOException {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.JPEG, 60, bytes);
        File f = new File(mContext.getCacheDir()
                + File.separator + filename +".jpg");
        Log.d(LOGTAG + "fpath", "file-path= " + (getCacheDir()
                + File.separator + filename +".jpg"));
        f.createNewFile();
        FileOutputStream fo = new FileOutputStream(f);
        fo.write(bytes.toByteArray());
        fo.close();
        return f;
    }

}
