package com.cherish.settings.preferences;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;

import com.android.settings.R;

public class SeekBarPreferenceCham extends Preference implements SeekBar.OnSeekBarChangeListener {

    private final String TAG = getClass().getName();

    private static final String ANDROIDNS = "http://schemas.android.com/apk/res/android";
    private static final String PDXNS = "http://schemas.android.com/apk/res-auto";
    private static final int DEFAULT_VALUE = 50;

    private int mMaxValue      = 100;
    private int mMinValue      = 0;
    private int mInterval      = 1;
    private int mDefaultValue  = -1;
    private int mCurrentValue;
    private String mUnitsLeft  = "";
    private String mUnitsRight = "";
    private SeekBar mSeekBar;
    //private TextView mTitle;
    private TextView mUnitsLeftText;
    private TextView mUnitsRightText;
    private ImageView mImagePlus;
    private ImageView mImageMinus;
    private Drawable mProgressThumb;
    private int mThumbDefaultValueColor;

    private TextView mStatusText;
    private TextView mPopupValue;
    private boolean mTrackingTouch = false;
    private boolean mPopupAdded = false;
    private int mPopupWidth = 0;
    private boolean initialised = false;

    public SeekBarPreferenceCham(Context context, AttributeSet attrs) {
        super(context, attrs);
        setLayoutResource(R.layout.seek_bar_preference);
        setValuesFromXml(attrs, context);
    }

    public SeekBarPreferenceCham(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setLayoutResource(R.layout.seek_bar_preference);
        setValuesFromXml(attrs, context);
    }

    private void setValuesFromXml(AttributeSet attrs, Context context) {
        final TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.SeekBarPreference);

        mMaxValue = attrs.getAttributeIntValue(ANDROIDNS, "max", 100);
        mMinValue = attrs.getAttributeIntValue(ANDROIDNS, "min", 0);
        mDefaultValue = attrs.getAttributeIntValue(ANDROIDNS, "defaultValue", -1);
        if (mDefaultValue != attrs.getAttributeIntValue(ANDROIDNS, "defaultValue", -2)) {
            mDefaultValue = (mMinValue + mMaxValue) / 2;
            Log.w(TAG, "Preference with key \"" + getKey() +
                    "\" does not have a default value set in xml, assuming " + mDefaultValue +
                    " until further changes");
        }
        if (mDefaultValue < mMinValue || mDefaultValue > mMaxValue) {
            throw new IllegalArgumentException("Default value is out of range!");
        }
        mUnitsLeft = getAttributeStringValue(attrs, PDXNS, "unitsLeft", "");
        mUnitsRight = getAttributeStringValue(attrs, PDXNS, "unitsRight", "");
        Integer idR = a.getResourceId(R.styleable.SeekBarPreference_unitsRight, 0);
        if (idR > 0) {
            mUnitsRight = context.getResources().getString(idR);
        }
        Integer idL = a.getResourceId(R.styleable.SeekBarPreference_unitsLeft, 0);
        if (idL > 0) {
            mUnitsLeft = context.getResources().getString(idL);
        }
        try {
            String newInterval = attrs.getAttributeValue(PDXNS, "interval");
            if(newInterval != null)
                mInterval = Integer.parseInt(newInterval);
        }
        catch(Exception e) {
            Log.e(TAG, "Invalid interval value", e);
        }

        mThumbDefaultValueColor = a.getColor(
                R.styleable.SeekBarPreference_thumb_default_value_color, 0xff000000);
        a.recycle();
    }

    private String getAttributeStringValue(AttributeSet attrs, String namespace, String name, String defaultValue) {
        String value = attrs.getAttributeValue(namespace, name);
        if(value == null)
            value = defaultValue;

        return value;
    }

    @Override
    public void onDependencyChanged(Preference dependency, boolean disableDependent) {
        super.onDependencyChanged(dependency, disableDependent);
        this.setShouldDisableView(true);
        //if (mTitle != null)
        //    mTitle.setEnabled(!disableDependent);
        if (mSeekBar != null)
            mSeekBar.setEnabled(!disableDependent);
        if (mImagePlus != null)
            mImagePlus.setEnabled(!disableDependent);
        if (mImageMinus != null)
            mImageMinus.setEnabled(!disableDependent);
    }


    @Override
    public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);
        mSeekBar = (SeekBar) holder.findViewById(R.id.seekbar);
        // Remove possible previously attached change listener to prevent setting wrong values
        mSeekBar.setOnSeekBarChangeListener(null);
        mSeekBar.setMax(mMaxValue - mMinValue);
        //mTitle = (TextView) holder.findViewById(android.R.id.title);
        mUnitsLeftText = (TextView) holder.findViewById(R.id.seekBarPrefUnitsLeft);
        mUnitsRightText = (TextView) holder.findViewById(R.id.seekBarPrefUnitsRight);
        mImagePlus = (ImageView) holder.findViewById(R.id.imagePlus);
        mImagePlus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mSeekBar.setProgress((mCurrentValue + mInterval) - mMinValue);
            }
        });
        mImagePlus.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                mSeekBar.setProgress((mCurrentValue + (mMaxValue-mMinValue)/10) - mMinValue);
                return true;
            }
        });
        mImageMinus = (ImageView) holder.findViewById(R.id.imageMinus);
        mImageMinus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mSeekBar.setProgress((mCurrentValue - mInterval) - mMinValue);
            }
        });
        mImageMinus.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                mSeekBar.setProgress((mCurrentValue - (mMaxValue-mMinValue)/10) - mMinValue);
                return true;
            }
        });
        mProgressThumb = mSeekBar.getThumb();
        mStatusText = (TextView) holder.findViewById(R.id.seekBarPrefValue);
        mStatusText.setMinimumWidth(30);
        mStatusText.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                final String defaultValue = getContext().getString(R.string.seekbar_default_value_set,
                        mDefaultValue);
                if (mDefaultValue != -1) {
                    if (mDefaultValue != mCurrentValue) {
                        mCurrentValue = mDefaultValue;
                        updateView();
                        Toast.makeText(getContext(), defaultValue, Toast.LENGTH_LONG).show();
                    } else {
                        Toast.makeText(getContext(), R.string.seekbar_default_value_already_set,
                                Toast.LENGTH_LONG).show();
                    }
                } else {
                    Toast.makeText(getContext(), R.string.seekbar_no_default_value,
                            Toast.LENGTH_LONG).show();
                }
                return true;
            }
        });

        LayoutInflater mInflater = (LayoutInflater) getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mPopupValue = (TextView) mInflater.inflate(R.layout.seek_bar_value_popup, null, false);
        mPopupValue.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    int width = mPopupValue.getWidth();
                    if (width != mPopupWidth) {
                        mPopupWidth = mPopupValue.getWidth();
                        startUpdateViewValue();
                    }
                }
        });

        initialised = true;
        updateView();
        mSeekBar.setOnSeekBarChangeListener(this);
    }

    /**
     * Update a SeekBarPreferenceCham view with our current state
     * @param view
     */
    protected void updateView() {
        if (!initialised) {
            return;
        }
        try {
            mStatusText.setText(String.valueOf(mCurrentValue));
            mSeekBar.setProgress(mCurrentValue - mMinValue);

            mUnitsRightText.setText(mUnitsRight);
            mUnitsLeftText.setText(mUnitsLeft);

            updateCurrentValueText();
        }
        catch(Exception e) {
            Log.e(TAG, "Error updating seek bar preference", e);
        }
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        int newValue = progress + mMinValue;
        if(newValue > mMaxValue)
            newValue = mMaxValue;
        else if(newValue < mMinValue)
            newValue = mMinValue;
        else if(mInterval != 1 && newValue % mInterval != 0)
            newValue = Math.round(((float)newValue)/mInterval)*mInterval;

        // change rejected, revert to the previous value
        if(!callChangeListener(newValue)){
            seekBar.setProgress(mCurrentValue - mMinValue);
            return;
        }
        // change accepted, store it
        mCurrentValue = newValue;
        updateCurrentValueText();

        if (fromUser) {
            startUpdateViewValue();
        } else {
            stopUpdateViewValue();
        }

        persistInt(newValue);
    }

    private void updateCurrentValueText() {
        if (mCurrentValue == mDefaultValue && mDefaultValue != -1) {
            mStatusText.setText(R.string.seekbar_default_string);
            mProgressThumb.setColorFilter(mThumbDefaultValueColor, PorterDuff.Mode.SRC_IN);
            mUnitsLeftText.setVisibility(View.GONE);
            mUnitsRightText.setVisibility(View.GONE);
        } else {
            mStatusText.setText(String.valueOf(mCurrentValue));
            mProgressThumb.clearColorFilter();
            mUnitsLeftText.setVisibility(View.VISIBLE);
            mUnitsRightText.setVisibility(View.VISIBLE);
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        startUpdateViewValue();
        mTrackingTouch = true;
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        notifyChanged();
        stopUpdateViewValue();
        mTrackingTouch = false;
    }

    @Override
    protected Object onGetDefaultValue(TypedArray ta, int index){
        int defaultValue = ta.getInt(index, DEFAULT_VALUE);
        return defaultValue;
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        int defaultVal;
        if (defaultValue instanceof Integer) {
            defaultVal = (Integer) defaultValue;
        } else {
            defaultVal = mDefaultValue;
        }
        setValue(restoreValue ? getPersistedInt(defaultVal) : defaultVal);
    }

    public void setValue(int value) {
        mCurrentValue = value;
        updateView();
    }

    private Drawable getSeekBarThumb() {
        return mProgressThumb;
    }

    private void startUpdateViewValue() {
        if (!mTrackingTouch) return;
        Rect thumbRect = getSeekBarThumb().getBounds();
        int[] seekbarPos = new int[2];
        int[] offsetPos = new int[2];
        mSeekBar.getLocationInWindow(seekbarPos);
        View mainContentView = /*mSeekBar.getRootView().findViewById(R.id.content_main);
        if (mainContentView == null) {
            mainContentView =*/ mSeekBar.getRootView().findViewById(android.R.id.content);
        //}
        if (mainContentView == null) {
            Log.w(TAG, "Could not find main content view to calculate value view offset");
            offsetPos[0] = 0;
            offsetPos[1] = 0;
        } else {
            mainContentView.getLocationInWindow(offsetPos);
        }
        mPopupValue.setText(mUnitsLeft + mCurrentValue + mUnitsRight);
        WindowManager.LayoutParams wp = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_APPLICATION,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);
        wp.gravity = Gravity.LEFT | Gravity.TOP;
        wp.x = thumbRect.centerX() + seekbarPos[0] - offsetPos[0] - (mPopupWidth-thumbRect.width()) / 2 +
                (int) getContext().getResources()
                        .getDimension(R.dimen.seek_bar_preference_cham_value_x_offset);
        wp.y = seekbarPos[1] - offsetPos[1] +
                (int) getContext().getResources()
                        .getDimension(R.dimen.seek_bar_preference_cham_value_y_offset);
        mPopupValue.setLayoutParams(wp);
        if (mPopupAdded) {
            wp = (WindowManager.LayoutParams) mPopupValue.getLayoutParams();
            ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
                    .updateViewLayout(mPopupValue, wp);
        } else {
            ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
                    .addView(mPopupValue, wp);
            mPopupAdded = true;
        }
        mPopupValue.setVisibility(View.VISIBLE);
    }

    private void stopUpdateViewValue() {
        if (!mPopupAdded) return;
        ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).removeView(mPopupValue);
        mPopupAdded = false;
    }

    public void setMax(int max) {
        mMaxValue = max;
        updateView();
    }

    public void setMin(int min) {
        mMinValue = min;
        updateView();
    }

    @Override
    public void setDefaultValue(Object defaultValue) {
        super.setDefaultValue(defaultValue);
        if (defaultValue instanceof Integer) {
            mDefaultValue = (Integer) defaultValue;
            updateView();
        }
    }

}
