package o2mc.io.dimmldependency.Datastreams;

import android.app.Activity;
import android.app.Application;
import android.view.View;
import android.view.ViewGroup;

import org.json.JSONObject;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;

import o2mc.io.dimmldependency.viewMapper.DatastreamsViewmapper;

/**
 * Created by nickyromeijn on 30/03/16.
 * This class handles injecting click listeners as middleware
 * and tracks views when they are loaded into memory
 */
public class DatastreamsHandler {

    private DatastreamsConfiguration datastreamsConfiguration;
    private DatastreamsDispatcher datastreamsDispatcher;

    private HashMap trackers = null;
    private Activity context;

    private String alias;
    private String identity;

    public DatastreamsHandler(Application context) {
        datastreamsConfiguration = new DatastreamsConfiguration("", "", this, context);
        datastreamsDispatcher = new DatastreamsDispatcher(this);
        datastreamsConfiguration.execute();
    }

    public DatastreamsDispatcher getDispatcher(){
        return datastreamsDispatcher;
    }

    public DatastreamsSettings getSettings(){
        return datastreamsConfiguration.getSettings();
    }

    public void update(Activity activity, String activityCycle, JSONObject generalInfo){
        context = activity;
        datastreamsDispatcher.update(activity, generalInfo);
        listen(activityCycle);
    }

    private void listen(String activityCycle){
        if(trackers != null) {
            trackActivities(context.getClass().getSimpleName(), context, activityCycle);
            setClicklisteners(context.getClass().getSimpleName(), context);
        }
    }

    public void callback(HashMap trackers){
        setTrackers(trackers);
    }

    /**
     * This method causes all views which are defined as trackByView within the configuration
     * to be saved into a datacontainer when they are actually viewed on screen
     * @param activityName
     * @param activity
     * @param activityCycleType
     */
    private void trackActivities(String activityName, Activity activity, String activityCycleType) {
        ArrayList<View> views = new ArrayList<View>();
        ViewGroup viewGroup = (ViewGroup) activity.getWindow().getDecorView();

        HashMap hmap = trackers;
        HashMap hmap2 = (HashMap) hmap.get(activityName);

        View v = null;

        if (hmap2 != null) {
            ArrayList<TrackingData> trackerlist = (ArrayList<TrackingData>) hmap2.get("view");
            if (trackerlist != null) {
                for (TrackingData trackingData : trackerlist) {
                    if (Objects.equals(trackingData.getId().toLowerCase(), "undefined")) {
                        DatastreamsViewmapper.findViews(viewGroup, views);
                        int indexCounter = 0;
                        for (View vl : views) {
                            if (indexCounter == trackingData.getIndexInActivity()) {
                                v = vl;
                            }
                            indexCounter++;
                        }
                    } else {
                        v = activity.findViewById(context.getResources().getIdentifier(trackingData.getId(), "id", context.getPackageName()));
                    }
                    if (v != null) {
                        DataContainer dc = DataContainerFactory.build(v, activityCycleType, trackingData);
                        dc.setActivity(activityName);
                        datastreamsDispatcher.dispatch(dc);
                        dispatchChainLinks(trackingData, activity);
                    }
                }
            }
        }
    }

    public void setTrackers(HashMap t){
        trackers = t;
    }

    public HashMap getTrackers() {
        return trackers;
    }


    /**
     * This method injects a 'middleware' clicklistener to each of the views which are tagged as trackByClick
     * It gets the old clicklistener through reflection and executes the old clicklistener in the newly set one
     * Making it possible to execute code when a certain button is pressed
     * @param activityName
     * @param activity
     */
    private void setClicklisteners(String activityName, Activity activity) {
            HashMap hmap = trackers;
            HashMap hmap2 = (HashMap) hmap.get(activityName);

            ArrayList<View> views = new ArrayList<View>();
            ViewGroup viewGroup = (ViewGroup) activity.getWindow().getDecorView();

            View v = null;

            if (hmap2 != null) {
                ArrayList<TrackingData> trackerlist = (ArrayList<TrackingData>) hmap2.get("click");
                if (trackerlist != null) {
                    for (TrackingData trackingData : trackerlist) {
                        if (Objects.equals(trackingData.getId().toLowerCase(), "undefined")) {
                            DatastreamsViewmapper.findViews(viewGroup, views);
                            int indexCounter = 0;
                            for (View vl : views) {
                                if (indexCounter == trackingData.getIndexInActivity()) {
                                    v = vl;
                                }
                                indexCounter++;
                            }
                        } else {
                            v = activity.findViewById(context.getResources().getIdentifier(trackingData.getId(), "id", context.getPackageName()));
                        }
                        if (v != null) {
                            try {
                                v.setOnClickListener(getOnClickListener(v, activityName, getCurrentClickListener(v), trackingData, activity));
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            } catch (NoSuchFieldException e) {
                                e.printStackTrace();
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
    }

    /**
     * Get the current clicklistener of the given view
     * Weirdly enough it is not possible to set multiple clicklisteners on a single view
     * @param view
     * @return
     * @throws ClassNotFoundException
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    public View.OnClickListener getCurrentClickListener(View view) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Field listenerInfoField = null;
        View.OnClickListener myListener = null;
        listenerInfoField = Class.forName("android.view.View").getDeclaredField("mListenerInfo");
        if (listenerInfoField != null) {
            listenerInfoField.setAccessible(true);
        }
        Object myLiObject = null;
        myLiObject = listenerInfoField.get(view);

        // get the field mOnClickListener, that holds the listener and cast it to a listener
        Field listenerField = null;
        listenerField = Class.forName("android.view.View$ListenerInfo").getDeclaredField("mOnClickListener");
        if (listenerField != null && myLiObject != null) {
            myListener = (View.OnClickListener) listenerField.get(myLiObject);
        }
        return myListener;
    }


    public View.OnClickListener getOnClickListener(final View view, final String activity, final View.OnClickListener oldListener, final TrackingData trackingData, final Activity ac) {
        return new View.OnClickListener() {
            public void onClick(View v) {
                DataContainer dc = DataContainerFactory.build(view, "click", trackingData);
                dc.setActivity(activity);
                datastreamsDispatcher.dispatch(dc);
                dispatchChainLinks(trackingData, ac);
                if (oldListener != null) {
                    v.setOnClickListener(oldListener);
                    v.performClick();
                }
            }
        };
    }

    public void dispatchChainLinks(TrackingData trackingData, Activity ac){
        for (TrackingData t : trackingData.getChainLinks()) {
            View chain = ac.findViewById(context.getResources().getIdentifier(t.getId(), "id", context.getPackageName()));
            t.setAlias(trackingData.getAlias());
            datastreamsDispatcher.dispatch(DataContainerFactory.build(chain, "chain", t));
        }
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    public void setIdentity(String identity) {
        this.identity = identity;
    }

    public String getAlias() {
        return alias;
    }

    public String getIdentity() {
        return identity;
    }
}
