/**
 * Copyright 2016 Bartosz Schiller
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.barteksc.pdfviewer;

import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.AsyncTask;

import com.github.barteksc.pdfviewer.model.PagePart;
import com.shockwave.pdfium.PdfDocument;
import com.shockwave.pdfium.PdfiumCore;

import java.util.ArrayList;
import java.util.List;

class RenderingAsyncTask extends AsyncTask<Void, PagePart, Void> {

    private PdfiumCore pdfiumCore;
    private PdfDocument pdfDocument;

    private final List<RenderingTask> renderingTasks = new ArrayList<>();

    private PDFView pdfView;

    private RectF renderBounds = new RectF();
    private Rect roundedRenderBounds = new Rect();
    private Matrix renderMatrix = new Matrix();

    public RenderingAsyncTask(PDFView pdfView, PdfiumCore pdfiumCore, PdfDocument pdfDocument) {
        this.pdfView = pdfView;
        this.pdfiumCore = pdfiumCore;
        this.pdfDocument = pdfDocument;
    }

    public void addRenderingTask(int userPage, int page, float width, float height, RectF bounds, boolean thumbnail, int cacheOrder, boolean bestQuality, boolean annotationRendering) {
        RenderingTask task = new RenderingTask(width, height, bounds, userPage, page, thumbnail, cacheOrder, bestQuality, annotationRendering);
        renderingTasks.add(task);
        wakeUp();
    }

    @Override
    protected void onPreExecute() {
    }

    @Override
    protected Void doInBackground(Void... params) {
        while (!isCancelled()) {

            // Proceed all tasks
            while (!renderingTasks.isEmpty()) {
                RenderingTask task = renderingTasks.get(0);
                PagePart part = proceed(task);

                if (renderingTasks.remove(task)) {
                    publishProgress(part);
                } else {
                    part.getRenderedBitmap().recycle();
                }
            }

            // Wait for new task, return if canceled
            if (!waitForRenderingTasks() || isCancelled()) {
                return null;
            }

        }
        return null;

    }

    @Override
    protected void onProgressUpdate(PagePart... part) {
        pdfView.onBitmapRendered(part[0]);
    }

    private boolean waitForRenderingTasks() {
        try {
            synchronized (renderingTasks) {
                renderingTasks.wait();
            }
            return true;
        } catch (InterruptedException e) {
            return false;
        }
    }

    private PagePart proceed(RenderingTask renderingTask) {
        int w = Math.round(renderingTask.width);
        int h = Math.round(renderingTask.height);
        Bitmap render = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        calculateBounds(w, h, renderingTask.bounds);

        pdfiumCore.renderPageBitmap(pdfDocument, render, renderingTask.page,
                roundedRenderBounds.left, roundedRenderBounds.top,
                roundedRenderBounds.width(), roundedRenderBounds.height(), renderingTask.annotationRendering);

        if (!renderingTask.bestQuality) {
            Bitmap cpy = render.copy(Bitmap.Config.RGB_565, false);
            render.recycle();
            render = cpy;
        }

        return new PagePart(renderingTask.userPage, renderingTask.page, render, //
                renderingTask.width, renderingTask.height, //
                renderingTask.bounds, renderingTask.thumbnail, //
                renderingTask.cacheOrder);
    }

    private void calculateBounds(int width, int height, RectF pageSliceBounds) {
        renderMatrix.reset();
        renderMatrix.postTranslate(-pageSliceBounds.left * width, -pageSliceBounds.top * height);
        renderMatrix.postScale(1 / pageSliceBounds.width(), 1 / pageSliceBounds.height());

        renderBounds.set(0, 0, width, height);
        renderMatrix.mapRect(renderBounds);
        renderBounds.round(roundedRenderBounds);
    }

    public void removeAllTasks() {
        renderingTasks.clear();
    }

    public void wakeUp() {
        synchronized (renderingTasks) {
            renderingTasks.notify();
        }
    }

    private class RenderingTask {

        float width, height;

        RectF bounds;

        int page;

        int userPage;

        boolean thumbnail;

        int cacheOrder;

        boolean bestQuality;

        boolean annotationRendering;

        public RenderingTask(float width, float height, RectF bounds, int userPage, int page, boolean thumbnail, int cacheOrder, boolean bestQuality, boolean annotationRendering) {
            super();
            this.page = page;
            this.width = width;
            this.height = height;
            this.bounds = bounds;
            this.userPage = userPage;
            this.thumbnail = thumbnail;
            this.cacheOrder = cacheOrder;
            this.bestQuality = bestQuality;
            this.annotationRendering = annotationRendering;
        }

    }

}

@@ -2255,33 +2255,98 @@
 le (
-!renderingTasks.isEmpty()
+true) %7B%0D%0A                RenderingTask task;%0D%0A                synchronized (renderingTasks
 ) %7B%0D
@@ -2354,33 +2354,41 @@
                 
-R
+    if(!r
 enderingTask tas
@@ -2387,40 +2387,259 @@
 Task
- task = renderingTasks.get(0);%0D%0A
+s.isEmpty()) %7B%0D%0A                        task = renderingTasks.get(0);%0D%0A                    %7D else %7B%0D%0A                        break;%0D%0A                    %7D%0D%0A                %7D%0D%0A%0D%0A                if(task != null) %7B // very rare case, but may happen%0D%0A    
     
@@ -2680,24 +2680,28 @@
 d(task);%0D%0A%0D%0A
+    
             
@@ -2756,24 +2756,28 @@
             
+   
+ 
 publishProgr
@@ -2796,32 +2796,36 @@
                 
+    
 %7D else %7B%0D%0A      
@@ -2838,16 +2838,20 @@
         
+ 
+   
 part.get
@@ -2871,32 +2871,55 @@
 p().recycle();%0D%0A
+                    %7D%0D%0A
                 
@@ -5040,32 +5040,77 @@
 veAllTasks() %7B%0D%0A
+        synchronized (renderingTasks) %7B%0D%0A    
         renderin
@@ -5122,24 +5122,35 @@
 s.clear();%0D%0A
+        %7D%0D%0A
     %7D%0D%0A%0D%0A   

Fix NPE and IndexOutOfBound bugs when rendering parts