/*
 * Copyright 2019. Google LLC
 *
 * 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
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.google.android.apps.playgames.simpleengine;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.WindowManager;
import java.util.ArrayList;

class BitmapTextureMaker implements Runnable {

    private boolean mStartedLoading = false;
    private boolean mFinishedLoading = false;
    static final int DIM_WIDTH = 0;
    static final int DIM_HEIGHT = 1;
    private Context mContext = null;
    private int mScreenWidth = 0;
    private int mScreenHeight = 0;

    private ArrayList<BitmapEntry> mEntries = new ArrayList<BitmapEntry>();

    BitmapTextureMaker() {}

    public void request(int tag, int resId, String name, int dimType, float maxDim) {
        if (mStartedLoading) {
            Logger.e("Can't request a new bitmap after loading has started.");
            return;
        }

        BitmapEntry e = new BitmapEntry();
        e.tag = tag;
        e.resId = resId;
        e.dimType = dimType;
        e.maxDim = maxDim;
        e.name = name;
        mEntries.add(e);
        Logger.d("Bitmap requested: " + e.toString() + ", #" + (mEntries.size() - 1));
    }

    public void startLoading(Context ctx) {
        mStartedLoading = true;
        mContext = ctx.getApplicationContext();
        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        mScreenWidth = wm.getDefaultDisplay().getWidth();
        mScreenHeight = wm.getDefaultDisplay().getHeight();
        Logger.d(
                "Starting async load of bitmaps. Screen dimensions "
                        + mScreenWidth
                        + "screenX"
                        + mScreenHeight);
        Thread t = new Thread(this);
        t.start();
    }

    class BitmapEntry {

        int tag;
        int dimType;
        float maxDim;
        int resId;
        Bitmap bitmap = null;
        String name = "";

        @Override
        public String toString() {
            return "[BitmapEntry name="
                    + name
                    + ", "
                    + ((dimType == DIM_HEIGHT) ? "maxH=" : "maxW=")
                    + maxDim
                    + ", resId="
                    + resId
                    + ", bitmap="
                    + (bitmap == null ? "(null)" : "loaded!" + "]");
        }
    }

    @Override
    public void run() {
        for (BitmapEntry e : mEntries) {
            loadBitmapEntry(e);
        }
        mContext = null;
        mFinishedLoading = true;
        Logger.d("Finished loading bitmaps.");
    }

    void loadBitmapEntry(BitmapEntry e) {
        Logger.d("Loading bitmap entry " + e.toString());

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(mContext.getResources(), e.resId, options);
        int imageHeight = options.outHeight;
        int imageWidth = options.outWidth;
        float imageAspect = imageWidth / (float) imageHeight;
        float screenUnit = (float) mScreenHeight;

        Logger.d(e.name + " dimensions " + imageWidth + "screenX" + imageHeight);

        int reqWidth = 0, reqHeight = 0;

        if (e.dimType == DIM_HEIGHT) {
            reqHeight = (int) (e.maxDim * screenUnit);
            reqWidth = (int) (imageAspect * reqHeight);
        } else if (e.dimType == DIM_WIDTH) {
            reqWidth = (int) (e.maxDim * screenUnit);
            reqHeight = (int) (reqWidth / imageAspect);
        }

        Logger.d(e.name + " requested dimensions " + reqWidth + "screenX" + reqHeight);

        int inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        Logger.d(e.name + " in sample size " + inSampleSize);

        options.inSampleSize = inSampleSize;
        options.inJustDecodeBounds = false;
        e.bitmap = BitmapFactory.decodeResource(mContext.getResources(), e.resId, options);
        Logger.d(
                "Loaded bitmap for "
                        + e.name
                        + ", "
                        + e.bitmap.getWidth()
                        + "x"
                        + e.bitmap.getHeight());
    }

    // From http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
    private static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }

    public boolean isFinishedLoading() {
        return mFinishedLoading;
    }

    public boolean hasStartedLoading() {
        return mStartedLoading;
    }

    public Bitmap getBitmap(int index) {
        if (!mFinishedLoading) {
            Logger.e("Can't call getBitmap before BitmapTextureMaker is finished loading.");
            return null;
        }
        if (index < 0 || index >= mEntries.size()) {
            return null;
        }
        return mEntries.get(index).bitmap;
    }

    public int getBitmapCount() {
        return mEntries.size();
    }

    public int getTag(int index) {
        return (index >= 0 && index < mEntries.size()) ? mEntries.get(index).tag : 0;
    }

    public void dispose() {
        mFinishedLoading = mStartedLoading = false;
        for (BitmapEntry e : mEntries) {
            if (e.bitmap != null) {
                e.bitmap.recycle();
                e.bitmap = null;
            }
        }
    }
}
