/*
 *    Copyright 2019 James Fenn
 *
 *    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
 *
 *        http://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.james.status.data;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;

import com.james.status.utils.StaticUtils;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import me.jfenn.androidutils.ImageUtils;

public class IconStyleData implements Parcelable {

    public static final int TYPE_VECTOR = 0;
    public static final int TYPE_IMAGE = 1;
    public static final int TYPE_FILE = 2;

    public String name;
    public int type;
    public int[] resource = new int[0];
    public String[] path = new String[0];
    private Map<Integer, Bitmap> icons;

    public IconStyleData(String name, int type, @DrawableRes int... resource) {
        this.name = name;
        this.type = type;
        this.resource = resource;
        icons = new HashMap<>();
    }

    public IconStyleData(String name, String... path) {
        this.name = name;
        type = TYPE_FILE;
        this.path = path;
        icons = new HashMap<>();
    }

    protected IconStyleData(Parcel in) {
        name = in.readString();
        resource = in.createIntArray();
        path = in.createStringArray();
        icons = new HashMap<>();
    }

    /**
     * Get the amount of icons in the style.
     *
     * @return An integer representing the amount of
     * icons in the style.
     */
    public int getSize() {
        if (type == TYPE_FILE) return path.length;
        else return resource.length;
    }

    /**
     * Get a drawable of the style at a particular index.
     *
     * @param context               The current application context.
     * @param value                 The index to obtain.
     * @return A created Drawable, or null if
     *                              something has gone horribly wrong.
     */
    @Nullable
    public Drawable getDrawable(Context context, int value) {
        if (value < 0 || value >= getSize()) return null;
        switch (type) {
            case TYPE_VECTOR:
                return VectorDrawableCompat.create(context.getResources(), resource[value], context.getTheme());
            case TYPE_IMAGE:
                return ContextCompat.getDrawable(context, resource[value]);
            case TYPE_FILE:
                String[] permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
                if (!StaticUtils.isPermissionsGranted(context, permissions)) {
                    if (context instanceof Activity)
                        StaticUtils.requestPermissions((Activity) context, permissions);

                    return null;
                } else {
                    try {
                        return Drawable.createFromPath(path[value]);
                    } catch (OutOfMemoryError e) {
                        e.printStackTrace();
                        return null;
                    }
                }
            default:
                return null;
        }
    }

    /**
     * Get a bitmap from the style at a particular index. This will
     * also cache the created bitmaps to increase performance of future
     * calls.
     *
     * @param context               The current application context.
     * @param value                 The index to obtain.
     * @return A created bitmap, or null if something
     *                              has gone horribly wrong.
     */
    @Nullable
    public Bitmap getBitmap(Context context, int value) {
        if (icons.containsKey(value))
            return icons.get(value);
        else {
            Drawable drawable = getDrawable(context, value);
            if (drawable != null) {
                Bitmap bitmap = ImageUtils.drawableToBitmap(drawable);
                icons.put(value, bitmap);
                return bitmap;
            } else return null;
        }
    }

    /**
     * Write the icon style to a SharedPreferences instance.
     *
     * @param editor                A SharedPreferences instance.
     * @param prefix                The preferences prefix.
     */
    public void writeToSharedPreferences(SharedPreferences.Editor editor, String prefix) {
        if (type == TYPE_FILE) {
            editor.putInt(prefix + name + "-length", getSize());
            for (int i = 0; i < getSize(); i++) {
                editor.putString(prefix + name + "-" + i, path[i]);
            }
        }
    }

    /**
     * Instantiate an IconStyleData from an index in SharedPreferences, based on a name.
     *
     * @param prefs                 A SharedPreferences instance.
     * @param prefix                The preferences prefix.
     * @param name                  The name of the style.
     * @return The created IconStyleData, or null if not found.
     */
    @Nullable
    public static IconStyleData fromSharedPreferences(SharedPreferences prefs, String prefix, String name) {
        if (prefs.contains(prefix + name + "-length")) {
            String[] path = new String[prefs.getInt(prefix + name + "-length", 0)];
            for (int i = 0; i < path.length; i++) {
                path[i] = prefs.getString(prefix + name + "-" + i, null);
            }

            return new IconStyleData(name, path);
        } else return null;
    }

    public static final Creator<IconStyleData> CREATOR = new Creator<IconStyleData>() {
        @Override
        public IconStyleData createFromParcel(Parcel in) {
            return new IconStyleData(in);
        }

        @Override
        public IconStyleData[] newArray(int size) {
            return new IconStyleData[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(name);
        parcel.writeIntArray(resource);
        parcel.writeStringArray(path);
    }

    public boolean equals(IconStyleData data) {
        return super.equals(data) || (data != null && ((Arrays.equals(data.resource, resource) && resource.length > 0) || (Arrays.equals(data.path, path) && path.length > 0) || data.name.matches(name)));
    }

    /**
     * Instantiate an icon style from the app resources using a set of strings.
     * If a resource is not found, this will return null. This is particularly
     * useful for adding styles that may not necessarily be included in the
     * published source code of the application; resources that may be under
     * a different license will only be used if they are available.
     *
     * @param name                  The user-facing name of the style.
     * @param type                  The type of icons the style uses, e.g. vector assets.
     * @param context               The current application context.
     * @param resourceNames         The names of the resources.
     * @return The created IconStyleData, or null.
     */
    @Nullable
    public static IconStyleData fromResource(String name, int type, Context context, String... resourceNames) {
        int[] resourceInts = new int[resourceNames.length];
        Resources resources = context.getResources();
        String packageName = context.getPackageName();
        for (int i = 0; i < resourceNames.length; i++) {
            int resource = resources.getIdentifier(resourceNames[i], "drawable", packageName);
            if (resource != 0)
                resourceInts[i] = resource;
            else return null;
        }

        return new IconStyleData(name, type, resourceInts);
    }
}