package xdroid.widget;

import android.annotation.TargetApi;
import android.content.Context;
import android.database.DataSetObserver;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

public class HorizontalSpinner extends FrameLayout {
    private HorizontalScrollView mScrollView;
    private LinearLayout mLinearLayout;

    private Adapter mAdapter;
    private View mCurrentView;
    private int mCurrentIndex;
    private boolean mOpened;

    private Listener mListener;

    private final DataSetObserver mObserver;
    private final OnClickListener mOnClickListener;
    private final OnClickListener mOnSwitchListener;

    public HorizontalScrollView getScrollView() {
        return mScrollView;
    }

    public LinearLayout getLinearLayout() {
        return mLinearLayout;
    }

    public Adapter getAdapter() {
        return mAdapter;
    }

    public View getCurrentView() {
        return mCurrentView;
    }

    public void setCurrentView(View currentView) {
        mCurrentView = currentView;
    }

    public int getCurrentIndex() {
        return mCurrentIndex;
    }

    public void setCurrentIndex(int currentIndex) {
        mCurrentIndex = currentIndex;
    }

    public boolean isOpened() {
        return mOpened;
    }

    public void setOpened(boolean opened) {
        mOpened = opened;
    }

    public Listener getListener() {
        return mListener;
    }

    public void setListener(Listener listener) {
        mListener = listener;
    }

    public HorizontalSpinner(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public HorizontalSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public HorizontalSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    {
        mObserver = new DataSetObserver() {
            @Override
            public void onChanged() {
                onDataSetChanged();
            }
        };

        mOnClickListener = new OnClickListener() {
            @Override
            public void onClick(View v) {
                onItemClicked(v);
            }
        };

        mOnSwitchListener = new OnClickListener() {
            @Override
            public void onClick(View v) {
                onSwitchClicked();
            }
        };
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        mLinearLayout = new LinearLayout(getContext());
        mLinearLayout.setOrientation(LinearLayout.HORIZONTAL);

        mScrollView = new HorizontalScrollView(getContext());
        mScrollView.addView(mLinearLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        mScrollView.setHorizontalScrollBarEnabled(isHorizontalScrollBarEnabled());

        addView(mScrollView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

        updateState();
    }

    @Override
    public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) {
        super.setHorizontalScrollBarEnabled(horizontalScrollBarEnabled);

        if (mScrollView != null) {
            mScrollView.setHorizontalScrollBarEnabled(horizontalScrollBarEnabled);
        }
    }

    public void setAdapter(Adapter adapter) {
        if (mAdapter != null) {
            mAdapter.unregisterDataSetObserver(mObserver);
        }

        mAdapter = adapter;

        if (mAdapter != null) {
            mAdapter.registerDataSetObserver(mObserver);
        }

        onDataSetChanged();
    }

    protected void onDataSetChanged() {
        int oldCount = mLinearLayout.getChildCount();
        int newCount = mAdapter.getCount();

        for (int i = 0, n = Math.max(oldCount, newCount); i < n; ++i) {
            View oldChild = mLinearLayout.getChildAt(i);
            if (oldChild != null) {
                oldChild.setOnClickListener(null);
            }

            View newChild = mAdapter.getView(i, oldChild, mLinearLayout);
            if (newChild != null) {
                newChild.setOnClickListener(mOnClickListener);

                if (oldChild != newChild) {
                    if (i < oldCount) {
                        mLinearLayout.removeViewAt(i);
                        mLinearLayout.addView(newChild, i);
                    } else {
                        mLinearLayout.addView(newChild);
                    }
                }
            }
        }

        updateCurrentView();
        updateState();
    }

    protected void onItemClicked(View view) {
        setCurrentItem(mLinearLayout.indexOfChild(view));

        // NOTE this may be customized by attributes
        switchState();
    }

    protected void onSwitchClicked() {
        switchState();
    }

    public void switchState() {
        mOpened = !mOpened;

        if (mListener != null) {
            mListener.onSwitched(this, mOpened);
        }

        updateState();
    }

    private void updateState() {
        if (mOpened) {
            if (mCurrentView != null) {
                mCurrentView.setVisibility(GONE);
            }

            mScrollView.setVisibility(VISIBLE);
        } else {
            if (mCurrentView != null) {
                mCurrentView.setVisibility(VISIBLE);
            }

            mScrollView.setVisibility(GONE);
        }
    }

    public void setCurrentItem(int index) {
        final int oldPosition = mCurrentIndex;
        mCurrentIndex = index;

        if (oldPosition != index) {
            if (mListener != null) {
                mListener.onCurrentItemChanged(this, index);
            }

            updateCurrentView();
            updateCurrentChildViews(oldPosition);
        }
    }

    private void updateCurrentView() {
        if (mCurrentView != null) {
            mCurrentView.setOnClickListener(null);
        }

        mCurrentView = mAdapter.getView(mCurrentIndex, mCurrentView, this);

        if (mCurrentView != null) {
            if (mCurrentView.getParent() == null) {
                addView(mCurrentView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            }

            mCurrentView.setOnClickListener(mOnSwitchListener);
        }
    }

    private void updateCurrentChildViews(int oldIndex) {
        refreshChildItem(oldIndex);
        refreshChildItem(mCurrentIndex);
    }

    private void refreshChildItem(int index) {
        View convertView = mLinearLayout.getChildAt(index);
        View view = mAdapter.getView(index, convertView, mLinearLayout);

        if (convertView != null) {
            if (convertView != view) {
                mLinearLayout.removeViewAt(index);
                mLinearLayout.addView(view, index);
            }
        } else {
            mLinearLayout.addView(view, index);
        }
    }

    public interface Listener {
        void onCurrentItemChanged(HorizontalSpinner parent, int index);

        void onSwitched(HorizontalSpinner parent, boolean opened);
    }
}