/*
 * Copyright (c) 2018 CoderLengary
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.lengary_l.wanandroid.component.banner;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.example.lengary_l.wanandroid.component.banner.ViewPager.CustomBannerViewPager;
import com.example.lengary_l.wanandroid.component.banner.interfaze.ImageLoader;
import com.example.lengary_l.wanandroid.component.banner.interfaze.OnBannerListener;
import com.example.lengary_l.wanandroid.R;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by CoderLengary
 */
public class CustomBanner extends FrameLayout implements ViewPager.OnPageChangeListener {


    //指示器
    private int indicatorWidth;
    private int indicatorHeight;
    private int indicatorLeftMargin;
    private int indicatorRightMargin;
    private int defaultWidth;
    private int selectIndicatorRes ;
    private int unselectIndicatorRes ;
    private List<ImageView> indicatorViews;
    private int indicatorGravity ;

    //指示器容器
    private LinearLayout indicatorContainer;

    //ViewPager
    private CustomBannerViewPager viewPager;

    //滑动时间
    private int delayTime ;
    private int scrollDuration ;

    //自动滑动和手动滑动
    private boolean isAutoPlay ;
    private boolean isManuallyScrollable ;

    //图片加载器
    private ImageLoader imageLoader;

    //图片
    private List<String> imageUrls;
    private List<View> imageViews;
    private int count;
    private int scaleType;

    //点击监听器
    private OnBannerListener listener;

    //滑动监听器
    private ViewPager.OnPageChangeListener onPageChangeListener;


    private Context context;
    private BannerPagerAdapter adapter;
    private int currentItem;

    private FrameLayout bannerDetailLayout;
    private int screenHeight;

    private WeakHandler handler = new WeakHandler();

    private static final String TAG = "CustomBanner";
    public CustomBanner(@NonNull Context context) {
        this(context, null);
    }

    public CustomBanner(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }


    public CustomBanner(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        imageUrls = new ArrayList<>();
        imageViews = new ArrayList<>();
        indicatorViews = new ArrayList<>();
        DisplayMetrics dm = getResources().getDisplayMetrics();
        defaultWidth = dm.widthPixels/80;
        screenHeight = dm.heightPixels;
        indicatorGravity = Gravity.CENTER;
        initViews(context, attrs);
    }

    //数据和界面初始化
    private void initViews(@NonNull Context context, @Nullable AttributeSet attrs) {
        initAttr(context, attrs);
        View view = LayoutInflater.from(context).inflate(R.layout.banner, this, true);

        indicatorContainer = view.findViewById(R.id.circle_indicator_container);
        viewPager = view.findViewById(R.id.custom_view_pager);

        setViewPagerScrollDuration();
    }



    private void initAttr(@NonNull Context context, @Nullable AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomBanner);
        indicatorWidth = typedArray.getDimensionPixelOffset(R.styleable.CustomBanner_indicatorWidth, defaultWidth);
        indicatorHeight = typedArray.getDimensionPixelOffset(R.styleable.CustomBanner_indicatorHeight, defaultWidth);
        indicatorLeftMargin = typedArray.getDimensionPixelOffset(R.styleable.CustomBanner_indicatorLeftMargin, BannerConfig.MARGINING_SIZE);
        indicatorRightMargin = typedArray.getDimensionPixelOffset(R.styleable.CustomBanner_indicatorRightMargin, BannerConfig.MARGINING_SIZE);
        selectIndicatorRes = typedArray.getResourceId(R.styleable.CustomBanner_selectIndicatorRes, R.drawable.gray_radius);
        unselectIndicatorRes = typedArray.getResourceId(R.styleable.CustomBanner_unselectIndicatorRes, R.drawable.white_radius);

        delayTime = typedArray.getInt(R.styleable.CustomBanner_delayTime, BannerConfig.DELAY_TIME);
        scrollDuration = typedArray.getInt(R.styleable.CustomBanner_scrollDuration, BannerConfig.SCROLL_DURATION);

        isAutoPlay = typedArray.getBoolean(R.styleable.CustomBanner_isAutoPlay, BannerConfig.IS_AUTO_PLAY);
        isManuallyScrollable = typedArray.getBoolean(R.styleable.CustomBanner_isManuallyScrollable, BannerConfig.IS_SCROLL);

        scaleType = typedArray.getInteger(R.styleable.CustomBanner_scaleType, 1);
        typedArray.recycle();
    }

    private CustomBanner setIndicatorGravity(int type) {
        switch (type) {
            case BannerConfig.LEFT:
                indicatorGravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
                break;

            case BannerConfig.CENTER:
                indicatorGravity = Gravity.CENTER;
                break;

            case BannerConfig.RIGHT:
                indicatorGravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
                break;

            default:
                indicatorGravity = Gravity.CENTER;
        }
        return this;
    }

    //ViewPager滑动的时长是很短的,我们需要通过反射自定义时长
    private void setViewPagerScrollDuration() {
        try {
            Field mField = ViewPager.class.getDeclaredField("mScroller");
            //mScroller是私有的变量
            mField.setAccessible(true);

            BannerScroller scroller = new BannerScroller(viewPager.getContext());
            scroller.setDuration(scrollDuration);
            //设置当前的viewPager的Scroller是我们定义的scroller
            mField.set(viewPager, scroller);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }


    public CustomBanner setDelayTime(int time) {
        delayTime = time;
        return this;
    }

    public CustomBanner setScrollDuration(int time) {
        scrollDuration = time;
        return this;
    }

    public CustomBanner isAutoPlay(boolean e) {
        isAutoPlay = e;
        return this;
    }



    public CustomBanner setImageLoader(ImageLoader imageLoader) {
        this.imageLoader = imageLoader;
        return this;
    }

    public CustomBanner setImages(List<String> imageUrls) {

        this.imageUrls = imageUrls;
        count = imageUrls.size();
        return this;
    }


    private void displayImages(List<String> imageUrls) {
        imageViews.clear();
        if (!imageUrls.isEmpty()) {
            for (int i = 0; i < count + 2; i++) {
                ImageView view;
                if (imageLoader != null) {
                     view = imageLoader.createImageView(context);
                }else {
                    view = new ImageView(context);
                }
                setScaleType(view);

                //由于普通循环会产生闪屏现象,所以用以下方法解决
                //多生成两张图片,位置为0的图片是urls的最后一个,位置为最后的图片是urls的第一个
                //中间的其它图片就是url里面的所有图片
                //所以如果urls有3个【图片地址0,图片地址1,图片地址2】
                //那么最终生成的图片有5个【图片地址2, 图片地址0,图片地址1,图片地址2, 图片地址0】
                //当右滑动到最后一个图片的时候,currentItem立刻指向第二张图片(虽然位置不同,但是图片是相同的)
                //当左滑动到第一张图片的时候,currentItem立刻指向倒数第二张图片(同理)
                //这样就巧妙造成了“循环滑动”的假象
                String url;
                if (i == 0) {
                    url = imageUrls.get(count - 1);
                } else if (i == count + 1){
                    url = imageUrls.get(0);
                } else {
                    url = imageUrls.get(i - 1);
                }
                imageLoader.displayImage(context, url, view);
                imageViews.add(view);

            }
        }
    }

    //特别注意:当urls有三个子项的时候
    //当前viewPager的Position: 0   1 2 3   4
    //应当返回的Url的Position : 2   0 1 2   0
    private int getRealUrlPosition(int fakePosition) {
        if (fakePosition == 0) {
            return count - 1;
        }else if (fakePosition == (count+1)) {
            return 0;
        }else {
            return fakePosition - 1;
        }
    }

    //特别注意:当urls有三个子项的时候
    //当前viewPager的Position    : 0   1 2 3    4
    //应当返回的真正的页面Position : 3   1 2 3   1
    private int getRealCurrentPosition(int fakePosition) {
        if (fakePosition == 0) {
            return count;
        }else if (fakePosition == (count + 1)) {
            return 1;
        }else {
            return fakePosition;
        }
    }

    private void setScaleType(ImageView view) {
        switch (scaleType) {
            case 0:
                view.setScaleType(ImageView.ScaleType.CENTER);
                break;
            case 1:
                view.setScaleType(ImageView.ScaleType.CENTER_CROP);
                break;
            case 2:
                view.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
                break;
            case 3:
                view.setScaleType(ImageView.ScaleType.FIT_CENTER);
                break;
            case 4:
                view.setScaleType(ImageView.ScaleType.FIT_END);
                break;
            case 5:
                view.setScaleType(ImageView.ScaleType.FIT_START);
                break;
            case 6:
                view.setScaleType(ImageView.ScaleType.FIT_XY);
                break;
            case 7:
                view.setScaleType(ImageView.ScaleType.MATRIX);
                break;
        }
    }

    private void displayIndicator() {
        indicatorViews.clear();
        indicatorContainer.removeAllViews();
        for (int i = 0; i < count; i++) {
            ImageView imageView = new ImageView(context);
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(indicatorWidth, indicatorHeight);
            params.leftMargin = indicatorLeftMargin;
            params.rightMargin = indicatorRightMargin;
            if (i == 0) {
                imageView.setImageResource(R.drawable.gray_radius);
            }else {
                imageView.setImageResource(R.drawable.white_radius);
            }
            indicatorViews.add(imageView);
            indicatorContainer.addView(imageView, params);
        }
    }


    public CustomBanner setOnBannerListener(OnBannerListener listener) {
        this.listener = listener;
        return this;
    }

    public CustomBanner setOnPageChangeListener(ViewPager.OnPageChangeListener onPageChangeListener) {
        this.onPageChangeListener = onPageChangeListener;
        return this;
    }


    public CustomBanner start() {

        displayImages(imageUrls);
        displayIndicator();
        setIndicatorContainerGravity();
        setViewPager();
        if (isAutoPlay) {
            startAutoPlay();
        }
        return this;
    }

    public void startAutoPlay() {
        handler.removeCallbacks(task);
        handler.postDelayed(task, delayTime);
    }

    public void stopAutoPlay() {
        handler.removeCallbacks(task);
    }


    private final Runnable task = new Runnable() {
        @Override
        public void run() {
            if (count > 1 && isAutoPlay) {

                //手动左滑到0,会停止自动滑动,同时currentItem会被重置为3,手指离开
                //开始自动滑动,currentItem+1=4 4%4+1=1 currentItem = 1 currentItem = 4 与 =1显示的图片是一样的。smoothScroll为false,无动画效果
                //当是其他情况的时候,smoothScroll为true,动画效果显现出来
                currentItem = currentItem % (count + 1) + 1;
                if (currentItem == 1) {
                    viewPager.setCurrentItem(currentItem, false);
                    handler.post(task);
                }else {
                    viewPager.setCurrentItem(currentItem);
                    handler.postDelayed(task, delayTime);
                }

            }

        }
    };

    //处理手动触摸逻辑,手指按下就停止滑动,手指放开就开始滑动
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        if (isAutoPlay) {
            int action = ev.getAction();
            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
                    || action == MotionEvent.ACTION_OUTSIDE) {
                startAutoPlay();
            }else if (action == MotionEvent.ACTION_DOWN) {
                stopAutoPlay();
            }
        }
        return super.dispatchTouchEvent(ev);
    }

    private void setIndicatorContainerGravity() {
        indicatorContainer.setGravity(indicatorGravity);
    }

    private void setViewPager() {
        if (adapter == null) {

            adapter = new BannerPagerAdapter();
            viewPager.addOnPageChangeListener(this);
        }
        currentItem = 1;
        viewPager.setAdapter(adapter);
        viewPager.setFocusable(true);
        viewPager.setCurrentItem(currentItem);
        if (isManuallyScrollable && count > 1) {
            viewPager.setScrollable(true);
        } else {
            viewPager.setScrollable(false);
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (onPageChangeListener != null) {
            onPageChangeListener.onPageScrolled(getRealUrlPosition(position), positionOffset, positionOffsetPixels);
        }
    }

    @Override
    public void onPageSelected(int position) {
        currentItem = position;
        if (onPageChangeListener != null) {
            onPageChangeListener.onPageSelected(position);
        }
        int realPosition = getRealUrlPosition(position);
        for (int i =0; i < count; i++) {
            indicatorViews.get(i).setImageResource(i == realPosition ? R.drawable.gray_radius : R.drawable.white_radius);
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if (onPageChangeListener != null) {
            onPageChangeListener.onPageScrollStateChanged(state);
        }

        //当currentItem为0的时候,会被重置为3
        //当currentItem为4的时候,会被重置为1
        switch (state) {
            case ViewPager.SCROLL_STATE_IDLE://无操作
                viewPager.setCurrentItem(getRealCurrentPosition(currentItem), false);
                break;
            case ViewPager.SCROLL_STATE_DRAGGING://开始滑动
                viewPager.setCurrentItem(getRealCurrentPosition(currentItem), false);
                break;

            case ViewPager.SCROLL_STATE_SETTLING://停止滑动
                break;
        }

    }


    /*
     ViewPager里面对每个页面的管理是key-value形式的,
     也就是说每个page都有个对应的id(id是object类型),
     需要对page操作的时候都是通过id来完成的
     */
    class BannerPagerAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return imageViews.size();
        }

        //判断当前page的id是不是object
        @Override
        public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
            return view == object;
        }

        //返回每个page的id,这里我们选择直接把view作为id
        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, final int position) {

            View view = imageViews.get(position);
            container.addView(view);
            if (listener != null) {
                view.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        listener.onBannerClick(getRealUrlPosition(position));
                    }
                });
            }
            return view;
        }

        //超过limit需要执行删除页面逻辑,我们要移除view,只要把当前页面的ID object传入就好(Object 就是 view)
        @Override
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            container.removeView((View) object);
        }
    }




}