package com.example.sphinx.mix.utils;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.StateSet;
import android.util.TypedValue;
import android.util.Xml;

import com.example.sphinx.mix.R;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;

public abstract class DrawableUtils {

    protected abstract Drawable inflateDrawable(Context context, XmlPullParser parser, AttributeSet attrs) throws IOException, XmlPullParserException;

    static Drawable createDrawable(Context context, int resId) {
        if (resId <= 0) return null;

        final TypedValue typedValue = new TypedValue();
        final Resources res = context.getResources();
        res.getValue(resId, typedValue, true);
        Drawable dr = null;

        if (typedValue.type >= TypedValue.TYPE_FIRST_COLOR_INT
                && typedValue.type <= TypedValue.TYPE_LAST_COLOR_INT) {
            dr = new ColorDrawable(ThemeUtils.replaceColorById(context, resId));
        } else {
            try {
                if (typedValue.string != null && typedValue.string.toString().endsWith("xml")) {
                    final XmlResourceParser rp = res.getXml(resId);
                    final AttributeSet attrs = Xml.asAttributeSet(rp);
                    int type;

                    while ((type = rp.next()) != XmlPullParser.START_TAG &&
                            type != XmlPullParser.END_DOCUMENT) {
                        // Empty loop
                    }
                    if (type != XmlPullParser.START_TAG) {
                        throw new XmlPullParserException("No start tag found");
                    }

                    dr = createFromXmlInner(context, rp, attrs);
                    rp.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (XmlPullParserException e) {
                e.printStackTrace();
            }
        }
        return dr;
    }

    static Drawable createFromXmlInner(Context context, XmlPullParser parser, AttributeSet attrs) throws IOException, XmlPullParserException {
        final DrawableUtils drawableUtils;

        final String name = parser.getName();
        switch (name) {
            case "selector":
                drawableUtils = new StateListDrawableUtils();
                break;
            case "shape":
                drawableUtils = new GradientDrawableUtils();
                break;
            case "layer-list":
                drawableUtils = new LayerDrawableUtils();
                break;
            default:
                drawableUtils = null;
        }
        return drawableUtils == null ? null : drawableUtils.inflateDrawable(context, parser, attrs);
    }

    /**
     * Extracts state_ attributes from an attribute set.
     *
     * @param attrs The attribute set.
     * @return An array of state_ attributes.
     */
    protected int[] extractStateSet(AttributeSet attrs) {
        int j = 0;
        final int numAttrs = attrs.getAttributeCount();
        int[] states = new int[numAttrs];
        for (int i = 0; i < numAttrs; i++) {
            final int stateResId = attrs.getAttributeNameResource(i);
            if (stateResId == 0) {
                break;
            } else if (stateResId == android.R.attr.drawable
                    || stateResId == android.R.attr.id
                    || stateResId == R.attr.drawableTint
                    || stateResId == R.attr.drawableTintMode) {
                // Ignore attributes from StateListDrawableItem and
                // AnimatedStateListDrawableItem.
                continue;
            } else {
                states[j++] = attrs.getAttributeBooleanValue(i, false)
                        ? stateResId : -stateResId;
            }
        }
        states = StateSet.trimStateSet(states, j);
        return states;
    }

    static int getAttrTintColor(Context context, AttributeSet attrs, int attr, int defaultValue) {
        final TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{attr});
        final int tintColor = ThemeUtils.replaceColor(context, a.getColor(0, defaultValue));
        a.recycle();
        return tintColor;
    }

    static Drawable getAttrDrawable(Context context, AttributeSet attrs, int attr) {
        final TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{attr});
        final int resId = a.getResourceId(0, 0);
        Drawable drawable = null;
        if (resId != 0) {
            drawable = createDrawable(context, resId);
            if (drawable == null) {
                drawable = a.getDrawable(0);
            }
        }
        a.recycle();
        return drawable;
    }

    static ColorFilter getAttrColorFilter(Context context, AttributeSet attrs, int tintAttr, int tintModeAttr) {
        final int color = getAttrColor(context, attrs, tintAttr, Color.TRANSPARENT);
        if (color == Color.TRANSPARENT) return null;
        return new PorterDuffColorFilter(color, getTintMode(context, attrs, tintModeAttr));
    }

    static ColorStateList getTintColorList(Context context, AttributeSet attrs, int tintAttr) {
        TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{tintAttr});
        if (!a.hasValue(0)) {
            a.recycle();
            return null;
        }
        final ColorStateList cls = TintManager.get(context).getColorStateList(a.getResourceId(0, 0));
        a.recycle();
        return cls;
    }

    static PorterDuff.Mode getTintMode(Context context, AttributeSet attrs, int tintModeAttr) {
        TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{tintModeAttr});
        final int tintModeValue = a.getInt(0, 0);
        a.recycle();
        return parseTintMode(tintModeValue, PorterDuff.Mode.SRC_IN);
    }

    static int getAttrDimensionPixelSize(Context context, AttributeSet attrs, int attr) {
        return getAttrDimensionPixelSize(context, attrs, attr, 0);
    }

    static int getAttrDimensionPixelSize(Context context, AttributeSet attrs, int attr, int defaultValue) {
        TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{attr});
        final int value = a.getDimensionPixelSize(0, defaultValue);
        a.recycle();
        return value;
    }

    static int getAttrColor(Context context, AttributeSet attrs, int attr, int defaultValue) {
        TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{attr});
        final int colorId = a.getResourceId(0, 0);
        final int value = colorId != 0 ? ThemeUtils.replaceColorById(context, colorId) : ThemeUtils.replaceColor(context, a.getColor(0, defaultValue));
        a.recycle();
        return value;
    }

    static int getAttrDimensionPixelOffset(Context context, AttributeSet attrs, int attr) {
        return getAttrDimensionPixelOffset(context, attrs, attr, 0);
    }

    static int getAttrDimensionPixelOffset(Context context, AttributeSet attrs, int attr, int defaultValue) {
        TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{attr});
        final int value = a.getDimensionPixelOffset(0, defaultValue);
        a.recycle();
        return value;
    }

    static float getAttrDimension(Context context, AttributeSet attrs, int attr) {
        return getAttrDimension(context, attrs, attr, 0);
    }

    static float getAttrDimension(Context context, AttributeSet attrs, int attr, int defaultValue) {
        TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{attr});
        final float value = a.getDimension(0, defaultValue);
        a.recycle();
        return value;
    }

    static float getAttrFloat(Context context, AttributeSet attrs, int attr, float defaultValue) {
        TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{attr});
        final float alphaMod = a.getFloat(0, defaultValue);
        a.recycle();
        return alphaMod;
    }

    static int getAttrInt(Context context, AttributeSet attrs, int attr, int defaultValue) {
        TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{attr});
        final int value = a.getInt(0, defaultValue);
        a.recycle();
        return value;
    }

    static boolean getAttrBoolean(Context context, AttributeSet attrs, int attr, boolean defaultValue) {
        TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{attr});
        final boolean value = a.getBoolean(0, defaultValue);
        a.recycle();
        return value;
    }

    static boolean getAttrHasValue(Context context, AttributeSet attrs, int attr) {
        TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{attr});
        final boolean flag = a.hasValue(0);
        a.recycle();
        return flag;
    }

    static int getAttrResourceId(Context context, AttributeSet attrs, int attr, int defaultValue) {
        TypedArray a = obtainAttributes(context.getResources(), context.getTheme(), attrs, new int[]{attr});
        final int id = a.getResourceId(0, defaultValue);
        a.recycle();
        return id;
    }

    static TypedArray obtainAttributes(
            Resources res, Resources.Theme theme, AttributeSet set, int[] attrs) {
        if (theme == null) {
            return res.obtainAttributes(set, attrs);
        }
        return theme.obtainStyledAttributes(set, attrs, 0, 0);
    }

    public static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
        switch (value) {
            case 3:
                return PorterDuff.Mode.SRC_OVER;
            case 5:
                return PorterDuff.Mode.SRC_IN;
            case 9:
                return PorterDuff.Mode.SRC_ATOP;
            case 14:
                return PorterDuff.Mode.MULTIPLY;
            case 15:
                return PorterDuff.Mode.SCREEN;
            case 16:
                return Build.VERSION.SDK_INT >= 11 ? PorterDuff.Mode.valueOf("ADD")
                        : defaultMode;
            default:
                return defaultMode;
        }
    }
}