package net.avenwu.support.util;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import org.xmlpull.v1.XmlPullParser;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * modified from CalligraphyLayoutInflater(Created by chris on 19/12/2013)
 * <p/>
 * Created by chaobin on 10/8/15.
 */
public class TypefaceLayoutInflator extends LayoutInflater {
    public TypefaceLayoutInflator(Context context) {
        super(context);
    }

    public TypefaceLayoutInflator(LayoutInflater original, Context newContext) {
        super(original, newContext);
    }

    @Override
    public LayoutInflater cloneInContext(Context newContext) {
        return new TypefaceLayoutInflator(this, newContext);
    }

    @Override
    protected View onCreateView(View parent, String name, AttributeSet attrs) throws ClassNotFoundException {
        View view = super.onCreateView(parent, name, attrs);
        onViewCreatedInternal(view, getContext(), attrs);
        return view;
    }

    @Override
    protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
        View view = super.onCreateView(name, attrs);
        onViewCreatedInternal(view, getContext(), attrs);
        return view;
    }

    @Override
    public void setFactory2(Factory2 factory) {
        // Only set our factory and wrap calls to the Factory2 trying to be set!
        if (!(factory instanceof WrapperFactory2)) {
//            LayoutInflaterCompat.setFactory(this, new WrapperFactory2(factory2, mCalligraphyFactory));
            super.setFactory2(new WrapperFactory2(factory));
        } else {
            super.setFactory2(factory);
        }
    }

    @Override
    public void setFactory(LayoutInflater.Factory factory) {
        // Only set our factory and wrap calls to the Factory trying to be set!
        if (!(factory instanceof WrapperFactory)) {
            super.setFactory(new WrapperFactory(factory, this));
        } else {
            super.setFactory(factory);
        }
    }

    @Override
    public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
        setPrivateFactoryInternal();
        return super.inflate(parser, root, attachToRoot);
    }

    private boolean mSetPrivateFactory = false;

    private void setPrivateFactoryInternal() {
        // Already tried to set the factory.
        if (mSetPrivateFactory) return;
        // Reflection (Or Old Device) skip.
//        if (!CalligraphyConfig.get().isReflection()) return;
        // Skip if not attached to an activity.
        if (!(getContext() instanceof Factory2)) {
            mSetPrivateFactory = true;
            return;
        }
        final Method setPrivateFactoryMethod = ReflectionUtils
            .getMethod(LayoutInflater.class, "setPrivateFactory");

        if (setPrivateFactoryMethod != null) {
            ReflectionUtils.invokeMethod(this,
                setPrivateFactoryMethod,
                new PrivateWrapperFactory2((Factory2) getContext(), this));
        }
        mSetPrivateFactory = true;
    }

    private static class WrapperFactory implements LayoutInflater.Factory {
        private final Factory mFactory;
        private final TypefaceLayoutInflator mInflater;

        public WrapperFactory(Factory factory, TypefaceLayoutInflator inflator) {
            mFactory = factory;
            mInflater = inflator;
        }

        @Override
        public View onCreateView(String name, Context context, AttributeSet attrs) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                try {
                    View view = mInflater.createCustomViewInternal(
                        null, mFactory.onCreateView(name, context, attrs), name, context, attrs);
                    onViewCreatedInternal(view, context, attrs);
                    return view;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            View view = mFactory.onCreateView(name, context, attrs);
            onViewCreatedInternal(view, context, attrs);
            return view;
        }
    }

    static void onViewCreatedInternal(View view, final Context context, AttributeSet attrs) {
        if (view instanceof TextView) {
            TypefaceUtils.setTypeface(context, (TextView) view, "fonts/RobotoCondensed-Regular.ttf");
        }
    }

    /**
     * Nasty method to inflate custom layouts that haven't been handled else where. If this fails it
     * will fall back through to the PhoneLayoutInflater method of inflating custom views where
     * Calligraphy will NOT have a hook into.
     *
     * @param parent      parent view
     * @param view        view if it has been inflated by this point, if this is not null this method
     *                    just returns this value.
     * @param name        name of the thing to inflate.
     * @param viewContext Context to inflate by if parent is null
     * @param attrs       Attr for this view which we can steal fontPath from too.
     * @return view or the View we inflate in here.
     */
    private View createCustomViewInternal(View parent, View view, String name, Context
        viewContext, AttributeSet attrs) throws Exception {
        // I by no means advise anyone to do this normally, but Google have locked down access to
        // the createView() method, so we never get a callback with attributes at the end of the
        // createViewFromTag chain (which would solve all this unnecessary rubbish).
        // We at the very least try to optimise this as much as possible.
        // We only call for customViews (As they are the ones that never go through onCreateView(...)).
        // We also maintain the Field reference and make it accessible which will make a pretty
        // significant difference to performance on Android 4.0+.

        // If CustomViewCreation is off skip this.
        if (view == null && name.indexOf('.') > -1) {
            if (mConstructorArgs == null)
                mConstructorArgs = ReflectionUtils.getField(LayoutInflater.class, "mConstructorArgs");

            final Object[] mConstructorArgsArr = (Object[]) ReflectionUtils.getValue(mConstructorArgs, this);
            final Object lastContext = mConstructorArgsArr[0];
            // The LayoutInflater actually finds out the correct context to use. We just need to set
            // it on the mConstructor for the internal method.
            // Set the constructor ars up for the createView, not sure why we can't pass these in.
            mConstructorArgsArr[0] = viewContext;
            ReflectionUtils.setValue(mConstructorArgs, this, mConstructorArgsArr);
            try {
                view = createView(name, null, attrs);
            } catch (ClassNotFoundException ignored) {
            } finally {
                mConstructorArgsArr[0] = lastContext;
                ReflectionUtils.setValue(mConstructorArgs, this, mConstructorArgsArr);
            }
        }
        return view;
    }

    private Field mConstructorArgs = null;

    @TargetApi(11)
    private static class WrapperFactory2 implements Factory2 {
        protected final Factory2 mFactory2;

        public WrapperFactory2(Factory2 factory2) {
            mFactory2 = factory2;
        }

        @Override
        public View onCreateView(String name, Context context, AttributeSet attrs) {
            View view = mFactory2.onCreateView(name, context, attrs);
            onViewCreatedInternal(view, context, attrs);
            return view;
        }

        @Override
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
            View view = mFactory2.onCreateView(parent, name, context, attrs);
            onViewCreatedInternal(view, context, attrs);
            return view;
        }
    }

    @TargetApi(11)
    private static class PrivateWrapperFactory2 extends WrapperFactory2 {

        private final TypefaceLayoutInflator mInflater;

        public PrivateWrapperFactory2(Factory2 factory2, TypefaceLayoutInflator inflater) {
            super(factory2);
            mInflater = inflater;
        }

        @Override
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
            try {
                View view = mInflater.createCustomViewInternal(parent,
                    mFactory2.onCreateView(parent, name, context, attrs),
                    name, context, attrs
                );
                onViewCreatedInternal(view, context, attrs);
                return view;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return super.onCreateView(parent, name, context, attrs);
        }
    }
}