package jp.sourceforge.nicoro;

import static jp.sourceforge.nicoro.Log.LOG_TAG;

import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.ScrollView;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;

import jp.gr.java_conf.shiseissi.commonlib.ViewUtil;

public class ListMenuView extends LinearLayout
implements RadioGroup.OnCheckedChangeListener, Handler.Callback {
//    private static final boolean DEBUG_LOGV = Release.IS_DEBUG & true;
//    private static final boolean DEBUG_LOGD = Release.IS_DEBUG & true;

    private static final int MSG_ID_SCROLL_RADIO_BUTTON_TO_DISPLAY = 0;

    private static final int[] LISTMENU_RADIO_ID = new int[] {
        R.id.radio_index,
        R.id.radio_ranking,
        R.id.radio_mylist,
        R.id.radio_video_tag,
        R.id.radio_related_video,
        R.id.radio_access_history,
        R.id.radio_bookmarks,
//        R.id.radio_nicorepo,
    };
    private static final int[] LISTMENU_STRING_ID = new int[] {
        R.string.item_list_menu_index,
        R.string.item_list_menu_ranking,
        R.string.item_list_menu_mylist,
        R.string.item_list_menu_video_tag,
        R.string.item_list_menu_related_video,
        R.string.item_list_menu_access_history,
        R.string.item_list_menu_bookmarks,
//        R.string.item_list_menu_nicorepo,
    };

    private WeakReference<FragmentManager> mFragmentManager =
        new WeakReference<FragmentManager>(null);
    private Intent mIntent;

    private ViewPager mFragmentList;
    private ListMenuAdapter mAdapter;
    private RadioGroup mListMenu;
//    private HorizontalScrollView mScrollListMenu;
    private FrameLayout mScrollListMenu;

    private ArrayList<WeakReference<Fragment>> mLists =
        new ArrayList<WeakReference<Fragment>>();

    private int mLastCheckedId = -1;
    private int mDefaultListRadioId = 0;

    private int mCurrentOrientation = Configuration.ORIENTATION_UNDEFINED;

    private final HandlerWrapper mHandler = new HandlerWrapper(this);

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_ID_SCROLL_RADIO_BUTTON_TO_DISPLAY:
                RadioButton radioButton = ViewUtil.findViewById(mListMenu,
                        mListMenu.getCheckedRadioButtonId());
                FrameLayout scroll = mScrollListMenu;
                if (radioButton != null && scroll != null
                        && !ListMenuView.scrollRadioButonToDisplay(radioButton, scroll)) {
                    // まだViewが表示される前に呼ばれることがあるので、後から再実行
                    scroll.getViewTreeObserver().addOnGlobalLayoutListener
                    (new ViewTreeObserver.OnGlobalLayoutListener() {
                        @Override
                        public void onGlobalLayout() {
                            RadioButton radioButton = ViewUtil.findViewById(mListMenu,
                                    mListMenu.getCheckedRadioButtonId());
                            FrameLayout scroll = mScrollListMenu;
                            if (radioButton != null && scroll != null
                                    && ListMenuView.scrollRadioButonToDisplay(radioButton, scroll)) {
                                scroll.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                            }
                        }
                    });
                }
                break;
            default:
                assert false : msg.what;
                break;
        }
        return true;
    }

    public ListMenuView(Context context) {
        super(context);
    }

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

    public void onCreateView(LayoutInflater inflater) {
        View v = this;
        mFragmentList = ViewUtil.findViewById(v, R.id.fragment_list);
        RadioGroup listMenu = ViewUtil.findViewById(v, R.id.list_menu);
        mListMenu = listMenu;
        ListMenuView.createRadioButton(inflater, listMenu);
        listMenu.setOnCheckedChangeListener(this);

        mScrollListMenu = (FrameLayout) mListMenu.getParent();

        mFragmentList.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                int currentId = mListMenu.getCheckedRadioButtonId();
                int newId = LISTMENU_RADIO_ID[position];
                if (currentId != newId) {
                    mListMenu.check(newId);
                }
            }
        });

        if (mAdapter != null) {
            mFragmentList.setAdapter(mAdapter);
        }
    }

    private static void createRadioButton(LayoutInflater inflater, RadioGroup listMenu) {
        final int[] id = LISTMENU_RADIO_ID;
        final int[] string = LISTMENU_STRING_ID;
        for (int i = 0; i < id.length; ++i) {
            RadioButton radioButton = ViewUtil.inflate(inflater,
                    R.layout.list_menu_radio_button,
                    listMenu, false);
            radioButton.setId(id[i]);
            radioButton.setText(string[i]);
            listMenu.addView(radioButton);
        }
    }

    public void onOrientationChanged(int orientation, boolean isDualPane) {
        if (mCurrentOrientation != Configuration.ORIENTATION_UNDEFINED
                && mCurrentOrientation != orientation
                && mListMenu != null) {
            if (!isDualPane) {
                int lastChecked = mListMenu.getCheckedRadioButtonId();

                // ダミーのレイアウト生成して、入れ替え不可能な部分はパラメータのみ適用
                LayoutInflater inflater = LayoutInflater.from(getContext());
                ViewGroup container = (ViewGroup) getParent();
                LinearLayout dummyRoot = ViewUtil.inflate(inflater,
                        R.layout.list_menu, container, false);
                ViewPager dummyFragmentList = ViewUtil.findViewById(
                        dummyRoot, R.id.fragment_list);
                RadioGroup dummyListMenu = ViewUtil.findViewById(
                        dummyRoot, R.id.list_menu);
                FrameLayout newScrollListMenu =
                    (FrameLayout) dummyListMenu.getParent();

                setLayoutParams(dummyRoot.getLayoutParams());
                setOrientation(dummyRoot.getOrientation());
                mFragmentList.setLayoutParams(dummyFragmentList.getLayoutParams());

                ViewUtil.removeViewFromParent(newScrollListMenu);
                ViewUtil.replaceView(mScrollListMenu, newScrollListMenu);
                mScrollListMenu = newScrollListMenu;

                RadioGroup listMenu = ViewUtil.findViewById(
                        mScrollListMenu, R.id.list_menu);
                mListMenu = listMenu;
                ListMenuView.createRadioButton(inflater, listMenu);
                listMenu.setOnCheckedChangeListener(this);
                listMenu.check(lastChecked);
            }

            // XXX 回転中はレイアウトがまだ整っていない場合がある
            mHandler.sendEmptyMessageDelayed(MSG_ID_SCROLL_RADIO_BUTTON_TO_DISPLAY,
                    400L);
        }
        mCurrentOrientation = orientation;
    }

    public void setCurrentRadioToDefaultRadio() {
        mDefaultListRadioId = mListMenu.getCheckedRadioButtonId();
    }

    // RadioGroup.OnCheckedChangeListener

    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        // 二重に呼ばれることがあるので、前回の値と比較
        if (checkedId == mLastCheckedId) {
            return;
        }
        mLastCheckedId = checkedId;

        FragmentManager manager = getFragmentManager();
        if (manager == null) {
            return;
        }

//        RadioButton radioButton = Util.findViewById(group, checkedId);
//        HorizontalScrollView scroll = mScrollListMenu;
//        if (!scrollRadioButonToDisplay(radioButton, scroll)) {
//            // まだViewが表示される前に呼ばれることがあるので、後から再実行
//            scroll.getViewTreeObserver().addOnGlobalLayoutListener
//            (new ViewTreeObserver.OnGlobalLayoutListener() {
//                @Override
//                public void onGlobalLayout() {
//                    RadioButton radioButton = Util.findViewById(mListMenu,
//                            mListMenu.getCheckedRadioButtonId());
//                    HorizontalScrollView scroll = mScrollListMenu;
//                    if (scrollRadioButonToDisplay(radioButton, scroll)) {
//                        scroll.getViewTreeObserver().removeGlobalOnLayoutListener(this);
//                    }
//                }
//            });
//        }

        mHandler.sendEmptyMessage(MSG_ID_SCROLL_RADIO_BUTTON_TO_DISPLAY);

        int len = LISTMENU_RADIO_ID.length;
        for (int pos = 0; pos < len; ++pos) {
            if (checkedId == LISTMENU_RADIO_ID[pos]) {
                mFragmentList.setCurrentItem(pos);
                break;
            }
        }
    }

    public void updateListMenu(Intent intent) {
        boolean hasId = intent.hasExtra(ListMenuActivity.INTENT_EXTRA_DEFAULT_LIST_RADIO);
        if (hasId || mDefaultListRadioId == 0) {
            int defaultListRadioId = intent.getIntExtra(ListMenuActivity.INTENT_EXTRA_DEFAULT_LIST_RADIO,
                    R.id.radio_index);
            mDefaultListRadioId = defaultListRadioId;
            if (hasId && mListMenu != null) {
                mListMenu.check(defaultListRadioId);
            }
        }
    }
    public void updateListMenu(Bundle bundle) {
        boolean hasId = bundle.containsKey(ListMenuActivity.INTENT_EXTRA_DEFAULT_LIST_RADIO);
        if (hasId || mDefaultListRadioId == 0) {
            int defaultListRadioId = bundle.getInt(ListMenuActivity.INTENT_EXTRA_DEFAULT_LIST_RADIO,
                    R.id.radio_index);
            mDefaultListRadioId = defaultListRadioId;
            if (hasId && mListMenu != null) {
                mListMenu.check(defaultListRadioId);
            }
        }
    }

    public void updateAllListMenu(ListMenuUpdater updater) {
        Bundle bundle = updater.getExtrasForListMenu();
        if (bundle == null) {
            return;
        }

        FragmentManager manager = getFragmentManager();
        if (manager != null) {
            BookmarksFragment bookmarks = findFragment(BookmarksFragment.class);
            ListMenuView.updateFragmentBookmarks(bundle, bookmarks);
            RelatedVideoFragment relatedVideo = findFragment(RelatedVideoFragment.class);
            ListMenuView.updateFragmentRelatedVideo(bundle, relatedVideo);
        }
        updateListMenu(bundle);
    }

    private static boolean scrollRadioButonToDisplay(RadioButton radioButton,
            FrameLayout scroll) {
        if (scroll instanceof HorizontalScrollView) {
            return scrollRadioButonToDisplayHorizontal(radioButton,
                    (HorizontalScrollView) scroll);
        } else if (scroll instanceof ScrollView) {
            return scrollRadioButonToDisplayVertical(radioButton,
                    (ScrollView) scroll);
        } else {
            throw new IllegalArgumentException(
                    "scroll must be HorizontalScrollView or ScrollView");
        }
    }

    private static boolean scrollRadioButonToDisplayHorizontal(RadioButton radioButton,
            HorizontalScrollView scroll) {
        int buttonWidth = radioButton.getWidth();
        int scrollWidth = scroll.getWidth();
        if (buttonWidth == 0 || scrollWidth == 0) {
            // サイズ0はやり直し
            return false;
        }
        int[] buttonLoc = new int[2];
        radioButton.getLocationInWindow(buttonLoc);
        int[] scrollLoc = new int[2];
        scroll.getLocationInWindow(scrollLoc);
        int buttonLeft = buttonLoc[0];
        int scrollLeft = scrollLoc[0];
        int target = 0;
        if (buttonLeft < scrollLeft) {
            target = buttonLeft - scrollLeft;
        } else {
            int buttonRight = buttonLeft + buttonWidth;
            int scrollRight = scrollLeft + scrollWidth;
            if (buttonRight > scrollRight) {
                target = buttonRight - scrollRight;
            }
        }
        if (target != 0) {
            scroll.smoothScrollBy(target, 0);
        }
        return true;
    }

    private static boolean scrollRadioButonToDisplayVertical(RadioButton radioButton,
            ScrollView scroll) {
        int buttonHeight = radioButton.getHeight();
        int scrollHeight = scroll.getHeight();
        if (buttonHeight == 0 || scrollHeight == 0) {
            // サイズ0はやり直し
            return false;
        }
        int[] buttonLoc = new int[2];
        radioButton.getLocationInWindow(buttonLoc);
        int[] scrollLoc = new int[2];
        scroll.getLocationInWindow(scrollLoc);
        int buttonTop = buttonLoc[1];
        int scrollTop = scrollLoc[1];
        int target = 0;
        if (buttonTop < scrollTop) {
            target = buttonTop - scrollTop;
        } else {
            int buttonBottom = buttonTop + buttonHeight;
            int scrollBottom = scrollTop + scrollHeight;
            if (buttonBottom > scrollBottom) {
                target = buttonBottom - scrollBottom;
            }
        }
        if (target != 0) {
            scroll.smoothScrollBy(0, target);
        }
        return true;
    }

    public void setFragmentManager(FragmentManager fm) {
        mFragmentManager = new WeakReference<FragmentManager>(fm);
        if (mAdapter == null) {
            mAdapter = new ListMenuAdapter(fm);
            if (mFragmentList != null) {
                mFragmentList.setAdapter(mAdapter);
            }
        }
    }

    private FragmentManager getFragmentManager() {
        return mFragmentManager.get();
    }

    public void setIntent(Intent intent) {
        mIntent = intent;
    }

    public void checkDefaultListRadio() {
        if (mDefaultListRadioId != 0) {
            mListMenu.check(mDefaultListRadioId);
        }
    }

    public void releaseHandler() {
        mHandler.release();
    }

    @SuppressWarnings("unchecked")
    public <T extends Fragment> T findFragment(Class<T> clz) {
        for (Iterator<WeakReference<Fragment>> it = mLists.iterator(); it.hasNext(); ) {
            Fragment f = it.next().get();
            if (f == null) {
                it.remove();
            } else if (f.getClass() == clz) {
                return (T) f;
            }
        }
        return null;
    }

    public static void updateFragmentBookmarks(Intent intent,
            BookmarksFragment bookmarks) {
        String url = intent.getStringExtra(
                ListMenuActivity.INTENT_EXTRA_BOOKMARKS_ADD_URL);
        String title = intent.getStringExtra(
                ListMenuActivity.INTENT_EXTRA_BOOKMARKS_ADD_TITLE);
        if (!TextUtils.isEmpty(url)) {
            if (bookmarks == null) {
                Log.w(LOG_TAG, "BookmarksFragment is not found");
            } else {
                bookmarks.setAddBookmark(url, title);
            }
        }
    }
    public static void updateFragmentBookmarks(Bundle bundle,
            BookmarksFragment bookmarks) {
        String url = bundle.getString(
                ListMenuActivity.INTENT_EXTRA_BOOKMARKS_ADD_URL);
        String title = bundle.getString(
                ListMenuActivity.INTENT_EXTRA_BOOKMARKS_ADD_TITLE);
        if (!TextUtils.isEmpty(url)) {
            if (bookmarks == null) {
                Log.w(LOG_TAG, "BookmarksFragment is not found");
            } else {
                bookmarks.setAddBookmark(url, title);
            }
        }
    }

    public static void updateFragmentRelatedVideo(Intent intent,
            RelatedVideoFragment relatedVideo) {
        if (intent.hasExtra(ListMenuActivity.INTENT_EXTRA_VIDEO_NUMBER)) {
            String videoNumber = intent.getStringExtra(
                    ListMenuActivity.INTENT_EXTRA_VIDEO_NUMBER);
            if (!TextUtils.isEmpty(videoNumber)) {
                if (relatedVideo == null) {
                    Log.w(LOG_TAG, "RelatedVideoFragment is not found");
                } else {
                    relatedVideo.setVideoNumber(videoNumber);
                }
            }
        }
    }
    public static void updateFragmentRelatedVideo(Bundle bundle,
            RelatedVideoFragment relatedVideo) {
        if (bundle.containsKey(ListMenuActivity.INTENT_EXTRA_VIDEO_NUMBER)) {
            String videoNumber = bundle.getString(
                    ListMenuActivity.INTENT_EXTRA_VIDEO_NUMBER);
            if (!TextUtils.isEmpty(videoNumber)) {
                if (relatedVideo == null) {
                    Log.w(LOG_TAG, "RelatedVideoFragment is not found");
                } else {
                    relatedVideo.setVideoNumber(videoNumber);
                }
            }
        }
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();

        SavedState ss = new SavedState(superState);
        ss.mCheckedListRadioId = mLastCheckedId;
        ss.mDefaultListRadioId = mDefaultListRadioId;
        return ss;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;

        super.onRestoreInstanceState(ss.getSuperState());
        mDefaultListRadioId = ss.mDefaultListRadioId;
        // 明示的に後からcheckしてFragment生成
        if (ss.mCheckedListRadioId != 0) {
            mListMenu.check(ss.mCheckedListRadioId);
        }
    }

    public static class SavedState extends BaseSavedState {
        int mCheckedListRadioId;
        int mDefaultListRadioId;

        SavedState(Parcelable superState) {
            super(superState);
            mCheckedListRadioId = -1;
            mDefaultListRadioId = 0;
        }

        private SavedState(Parcel in) {
            super(in);
            mCheckedListRadioId = in.readInt();
            mDefaultListRadioId = in.readInt();
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(mCheckedListRadioId);
            dest.writeInt(mDefaultListRadioId);
        }

        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            @Override
            public SavedState createFromParcel(Parcel source) {
                return new SavedState(source);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

    private class ListMenuAdapter extends FragmentStatePagerAdapter {
        public ListMenuAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            int id = LISTMENU_RADIO_ID[position];

            Fragment newList = null;
            Intent intent;
            switch (id) {
                case R.id.radio_bookmarks:
                    intent = mIntent;

                    BookmarksFragment bookmarks = new BookmarksFragment();
                    newList = bookmarks;
                    ListMenuView.updateFragmentBookmarks(intent, bookmarks);
                    break;
                case R.id.radio_access_history:
                    AccessHistoryFragment accessHistory = new AccessHistoryFragment();
                    newList = accessHistory;
                    break;
                case R.id.radio_related_video:
                    intent = mIntent;

                    RelatedVideoFragment relatedVideo = new RelatedVideoFragment();
                    newList = relatedVideo;
                    ListMenuView.updateFragmentRelatedVideo(intent, relatedVideo);
                    break;
                case R.id.radio_ranking:
                    RankingFragment ranking = new RankingFragment();
                    newList = ranking;
                    break;
                case R.id.radio_video_tag:
                    VideoTagFragment videoTag = new VideoTagFragment();
                    newList = videoTag;
                    break;
                case R.id.radio_mylist:
                    MylistFragment mylist = new MylistFragment();
                    newList = mylist;
                    break;
                case R.id.radio_index:
                    IndexFragment index = new IndexFragment();
                    newList = index;
                    break;
                default:
                    assert false : "invalid id: " + id;
                    return null;
            }

            mLists.add(new WeakReference<Fragment>(newList));
            return newList;
        }

        @Override
        public int getCount() {
            return LISTMENU_RADIO_ID.length;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            super.destroyItem(container, position, object);
            for (Iterator<WeakReference<Fragment>> it = mLists.iterator(); it.hasNext(); ) {
                Fragment f = it.next().get();
                if (f == null || f == object) {
                    it.remove();
                }
            }
        }
    }
}
