package com.gingersoft.gsa.cloud.download.mvp.presenter;

import android.app.Application;
import android.text.TextUtils;

import com.gingersoft.gsa.cloud.common.core.restaurant.RestaurantInfoManager;
import com.billy.cc.core.component.CC;
import com.gingersoft.gsa.cloud.common.core.user.UserContext;
import com.gingersoft.gsa.cloud.common.Api;
import com.gingersoft.gsa.cloud.common.bean.BaseResult;
import com.gingersoft.gsa.cloud.common.logan.LoganManager;
import com.gingersoft.gsa.cloud.common.utils.CommonConfiguration;
import com.gingersoft.gsa.cloud.common.utils.JsonUtils;
import com.gingersoft.gsa.cloud.common.core.restaurant.utils.RestaurantExpandInfoUtils;
import com.gingersoft.gsa.cloud.common.utils.gson.GsonUtils;
import com.gingersoft.gsa.cloud.component.ComponentName;
import com.gingersoft.gsa.cloud.common.constans.HttpsConstans;
import com.gingersoft.gsa.cloud.database.bean.ColorBean;
import com.gingersoft.gsa.cloud.database.bean.ComboItem;
import com.gingersoft.gsa.cloud.database.bean.Discount;
import com.gingersoft.gsa.cloud.database.bean.ExpandInfo;
import com.gingersoft.gsa.cloud.database.bean.Food;
import com.gingersoft.gsa.cloud.database.bean.FoodCombo;
import com.gingersoft.gsa.cloud.database.bean.FoodModifier;
import com.gingersoft.gsa.cloud.database.bean.Function;
import com.gingersoft.gsa.cloud.database.bean.Modifier;
import com.gingersoft.gsa.cloud.database.bean.PrinterDeviceBean;
import com.gingersoft.gsa.cloud.database.bean.PrinterListBean;
import com.gingersoft.gsa.cloud.database.utils.ColorDaoUtils;
import com.gingersoft.gsa.cloud.database.utils.ComboItemDaoUtils;
import com.gingersoft.gsa.cloud.database.utils.DiscountDaoUtils;
import com.gingersoft.gsa.cloud.database.utils.ExpandInfoDaoUtils;
import com.gingersoft.gsa.cloud.database.utils.FoodComboDaoUtils;
import com.gingersoft.gsa.cloud.database.utils.FoodDaoUtils;
import com.gingersoft.gsa.cloud.database.utils.FoodModifierDaoUtils;
import com.gingersoft.gsa.cloud.database.utils.FunctionDaoUtils;
import com.gingersoft.gsa.cloud.database.utils.ModifierDaoUtils;
import com.gingersoft.gsa.cloud.database.utils.PrinterDeviceDaoUtils;
import com.gingersoft.gsa.cloud.download.mvp.contract.DownloadContract;
import com.gingersoft.gsa.cloud.download.mvp.model.bean.ComboItemRespone;
import com.gingersoft.gsa.cloud.download.mvp.model.bean.DataDownLoadState;
import com.gingersoft.gsa.cloud.download.mvp.model.bean.FoodComboRespone;
import com.gingersoft.gsa.cloud.download.mvp.model.bean.FoodModifierRespone;
import com.gingersoft.gsa.cloud.download.mvp.model.bean.FunctionRespone;
import com.gingersoft.gsa.cloud.download.mvp.model.bean.ModifierRespone;
import com.gingersoft.gsa.cloud.download.mvp.model.downmanager.DownloadManager;
import com.gingersoft.gsa.cloud.download.mvp.model.downmanager.DownloadRequest;
import com.gingersoft.gsa.cloud.download.mvp.ui.activity.DownloadActivity;
import com.gingersoft.gsa.cloud.download.mvp.ui.adapter.DataDownLoadAdapter;
import com.gingersoft.gsa.cloud.common.function.XFunctionManager;
import com.jess.arms.di.scope.ActivityScope;
import com.jess.arms.http.imageloader.ImageLoader;
import com.jess.arms.integration.AppManager;
import com.jess.arms.mvp.BasePresenter;
import com.jess.arms.utils.DeviceUtils;
import com.jess.arms.utils.RxLifecycleUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import javax.inject.Inject;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import me.jessyan.rxerrorhandler.core.RxErrorHandler;
import me.jessyan.rxerrorhandler.handler.ErrorHandleSubscriber;


/**
 * ================================================
 * Description:
 * <p>
 * Created by MVPArmsTemplate on 12/26/2019 17:59
 * <a href="mailto:jess.yan.effort@gmail.com">Contact me</a>
 * <a href="https://github.com/JessYanCoding">Follow me</a>
 * <a href="https://github.com/JessYanCoding/MVPArms">Star me</a>
 * <a href="https://github.com/JessYanCoding/MVPArms/wiki">See me</a>
 * <a href="https://github.com/JessYanCoding/MVPArmsTemplate">模版请保持更新</a>
 * ================================================
 */
@ActivityScope
public class DownloadPresenter extends BasePresenter<DownloadContract.Model, DownloadContract.View> {
    @Inject
    RxErrorHandler mErrorHandler;
    @Inject
    Application mApplication;
    @Inject
    ImageLoader mImageLoader;
    @Inject
    AppManager mAppManager;

    private DownloadActivity IActivity;

    private DataDownLoadAdapter mAapter;
    private List<DataDownLoadState> mList = new ArrayList<>();

    private String[] downTitles = {"功能列表", "食品列表", "食品細項", "食品套餐", "套餐食品", "食品細項", "折扣信息", "擴展信息", "打印機列表", "顏色列表"};
    //下載所佔比例
    private int[] ratios = {5, 46, 22, 5, 5, 5, 3, 3, 3, 3};
    private String urlList[] = {
            HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + Api.download_function_list,
            HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + Api.food_list,
            HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + Api.modifier,
            HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + Api.food_package_relation,
            HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + Api.food_comboItem,
            HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + Api.food_modifiere_relation,
            HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + Api.restaurant_discount,
            HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + Api.expand_info,
            HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + Api.printerDevice_list,
            HttpsConstans.ROOT_SERVER_ADDRESS_FORMAL + Api.color_list};

    //当前请求响应成功数
    private int responseSuccessCount;
    //当前所需請求數
    private int requestCount = 0;
    //當前下載進度
    private float currentProgress = 0;

    //响应数据保存到本地库的数量
    private int saveSuccessCount;
    private Map<Integer, Object> responseDataMaps = new HashMap<>();

    private List<Observable> mRequestObservables = new ArrayList<>();
    private List<Disposable> mDownDisposable = new ArrayList<>();

    @Inject
    public DownloadPresenter(DownloadContract.Model model, DownloadContract.View rootView) {
        super(model, rootView);
        this.IActivity = (DownloadActivity) rootView;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        this.mErrorHandler = null;
        this.mAppManager = null;
        this.mImageLoader = null;
        this.mApplication = null;

        mRequestObservables.clear();
        mRequestObservables = null;
        stopDownDisposable();
    }

    public void initAdapter() {
        initDataDownItem();
        if (mAapter == null) {
            mAapter = new DataDownLoadAdapter(mApplication, mList);
            mRootView.setDownLoadListAdapter(mAapter);
        }
    }

    public void downAllList() {
        if (!DeviceUtils.netIsConnected(mApplication)) {
            mRootView.showMessage("Can not connect server, retry?");
        }

        requestCount = urlList.length;
        saveSuccessCount = 0;
        responseSuccessCount = 0;
        currentProgress = 0;
        IActivity.setTvProgress(0);

        stopDownDisposable();
        initDownState();
        setDownAverageRatio();
        addDownRequests();
        excuteDownRequests();
    }

    private void addDownRequests() {
        long userId = UserContext.newInstance().getMemberId();
        int restaurantId = RestaurantInfoManager.newInstance().getRestaurantId();
        mRequestObservables.add(mModel.downFunctionList(userId));
        mRequestObservables.add(mModel.downFoodList(restaurantId));
        mRequestObservables.add(mModel.downModifier(restaurantId));
        mRequestObservables.add(mModel.downFoodCombo(restaurantId));
        mRequestObservables.add(mModel.downComboItem(restaurantId));
        mRequestObservables.add(mModel.downFoodModifier(restaurantId));
        mRequestObservables.add(mModel.downDiscount(restaurantId));
        mRequestObservables.add(mModel.downExpandInfo(restaurantId));
        mRequestObservables.add(mModel.downPrinterList(restaurantId));
        mRequestObservables.add(mModel.downColorList());
    }

    private void excuteDownRequests() {
        if (mRequestObservables != null && mRequestObservables.size() > 0) {
            for (int i = 0; i < mRequestObservables.size(); i++) {
                downDataRequest(mRequestObservables.get(i), i);
            }
        }
    }


    private void downDataRequest(Observable observable, int downIndex) {
        observable
                .subscribeOn(Schedulers.io())
                .subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(RxLifecycleUtils.bindToLifecycle(mRootView))
                .subscribe(new ErrorHandleSubscriber<BaseResult>(mErrorHandler) {

                    @Override
                    public void onSubscribe(Disposable d) {
                        super.onSubscribe(d);
                        mDownDisposable.add(d);
                    }

                    @Override
                    public void onComplete() {
                        if (responseSuccessCount == requestCount) {
                            saveToDatabase(0, new OnSaveDownDataListener() {
                                @Override
                                public void onComplete(int nextIndex) {
                                    boolean end = saveToDatabase(nextIndex, this);
                                    if (end && saveSuccessCount == requestCount) {
                                        endDownReturn();
                                    }
                                }
                            });
                        }
                    }

                    @Override
                    public void onNext(@NonNull BaseResult result) {
                        if (result.isSuccess()) {

                            responseDataMaps.put(downIndex, result.getData());

                            DataDownLoadState loadState = mList.get(downIndex);
                            loadState.setState(2);
                            mAapter.notifyItemChanged(downIndex);

                            currentProgress += loadState.getRatio();
                            IActivity.setTvProgress(currentProgress);

                            responseSuccessCount++;
                        }
                    }

                    @Override
                    public void onError(Throwable t) {
                        super.onError(t);
                        DataDownLoadState loadState = mList.get(downIndex);
                        loadState.setState(0);
                        mRootView.showMessage("部分下載失敗，點擊刷新按鈕重新下載");
                    }
                });
    }

    private boolean saveToDatabase(int downIndex, OnSaveDownDataListener saveDownDataListener) {
        Object data = responseDataMaps.get(downIndex);
        if (data == null) {
            return true;
        }
        int nextIndex = downIndex + 1;
        switch (downIndex) {
            case 0:
                List<Function> functionList = GsonUtils.jsonToList(data, Function.class);
                if (functionList != null) {
                    Observable.just(0)
                            .observeOn(Schedulers.io())
                            .subscribe(new Consumer<Integer>() {
                                @Override
                                public void accept(@NonNull Integer integer) throws Exception {
                                    FunctionDaoUtils functionDao = new FunctionDaoUtils(IActivity);
                                    functionDao.deleteAll();
                                    functionDao.insertMultFunction(functionList);
                                    saveSuccessCount++;
                                    if (saveDownDataListener != null) {
                                        saveDownDataListener.onComplete(nextIndex);
                                    }
                                }
                            });
                    XFunctionManager.newInstance().updateFunctions(functionList);
                }
                break;
            case 1:
                List<Food> foodList = GsonUtils.jsonToList(data, Food.class);
                if (foodList != null) {
                    Observable.just(0)
                            .observeOn(Schedulers.io())
                            .subscribe(new Consumer<Integer>() {
                                @Override
                                public void accept(@NonNull Integer integer) throws Exception {
                                    FoodDaoUtils foodDao = new FoodDaoUtils(IActivity);
                                    foodDao.deleteAll();
                                    foodDao.insertMultFood(foodList);
                                    saveSuccessCount++;
                                    if (saveDownDataListener != null) {
                                        saveDownDataListener.onComplete(nextIndex);
                                    }
                                }
                            });
                }
                break;
            case 2:
                List<Modifier> modifierList = GsonUtils.jsonToList(data, Modifier.class);
                if (modifierList != null) {
                    Observable.just(0)
                            .observeOn(Schedulers.io())
                            .subscribe(new Consumer<Integer>() {
                                @Override
                                public void accept(@NonNull Integer integer) throws Exception {
                                    ModifierDaoUtils modifierDao = new ModifierDaoUtils(IActivity);
                                    modifierDao.deleteAll();
                                    modifierDao.insertMultModifier(modifierList);
                                    CommonConfiguration.getInstance().saveModifierTopIdConfiguration(modifierList);
                                    saveSuccessCount++;
                                    if (saveDownDataListener != null) {
                                        saveDownDataListener.onComplete(nextIndex);
                                    }
                                }
                            });
                }
                break;
            case 3:
                List<FoodCombo> foodComboList = GsonUtils.jsonToList(data, FoodCombo.class);
                if (foodComboList != null) {
                    Observable.just(0)
                            .observeOn(Schedulers.io())
                            .subscribe(new Consumer<Integer>() {
                                @Override
                                public void accept(@NonNull Integer integer) throws Exception {
                                    FoodComboDaoUtils comboDao = new FoodComboDaoUtils(IActivity);
                                    comboDao.deleteAll();
                                    comboDao.insertMultCombo(foodComboList);
                                    CommonConfiguration.getInstance().saveComboIdConfiguration(foodComboList);
                                    saveSuccessCount++;
                                    if (saveDownDataListener != null) {
                                        saveDownDataListener.onComplete(nextIndex);
                                    }
                                }
                            });
                }
                break;
            case 4:
                List<ComboItem> comboItemList = GsonUtils.jsonToList(data, ComboItem.class);
                if (comboItemList != null) {
                    Observable.just(0)
                            .observeOn(Schedulers.io())
                            .subscribe(new Consumer<Integer>() {
                                @Override
                                public void accept(@NonNull Integer integer) throws Exception {
                                    ComboItemDaoUtils comboDao = new ComboItemDaoUtils(IActivity);
                                    comboDao.deleteAll();
                                    comboDao.insertMultCombo(comboItemList);
                                    saveSuccessCount++;
                                    if (saveDownDataListener != null) {
                                        saveDownDataListener.onComplete(nextIndex);
                                    }
                                }
                            });
                }
                break;
            case 5:
                List<FoodModifier> foodModifierList = GsonUtils.jsonToList(data, FoodModifier.class);
                if (foodModifierList != null) {
                    Observable.just(0)
                            .observeOn(Schedulers.io())
                            .subscribe(new Consumer<Integer>() {
                                @Override
                                public void accept(@NonNull Integer integer) throws Exception {
                                    FoodModifierDaoUtils foodModifierDao = new FoodModifierDaoUtils(IActivity);
                                    foodModifierDao.deleteAll();
                                    foodModifierDao.insertMultFoodModifier(foodModifierList);
                                    saveSuccessCount++;
                                    if (saveDownDataListener != null) {
                                        saveDownDataListener.onComplete(nextIndex);
                                    }
                                }
                            });
                }
                break;
            case 6:
                List<Discount> discountList = GsonUtils.jsonToList(data, Discount.class);
                if (discountList != null) {
                    Observable.just(0)
                            .observeOn(Schedulers.io())
                            .subscribe(new Consumer<Integer>() {
                                @Override
                                public void accept(@NonNull Integer integer) throws Exception {
                                    DiscountDaoUtils discountDaoUtils = new DiscountDaoUtils(IActivity);
                                    discountDaoUtils.deleteAll();
                                    discountDaoUtils.insertMultDiscount(discountList);
                                    saveSuccessCount++;
                                    if (saveDownDataListener != null) {
                                        saveDownDataListener.onComplete(nextIndex);
                                    }
                                }
                            });
                }
                break;
            case 7:
                List<ExpandInfo> expandInfoList = GsonUtils.jsonToList(data, ExpandInfo.class);
                if (expandInfoList != null) {
                    Observable.just(0)
                            .observeOn(Schedulers.io())
                            .subscribe(new Consumer<Integer>() {
                                @Override
                                public void accept(@NonNull Integer integer) throws Exception {
                                    ExpandInfoDaoUtils expandInfoDaoUtils = new ExpandInfoDaoUtils(IActivity);
                                    expandInfoDaoUtils.deleteAll();
                                    expandInfoDaoUtils.insertMultExpandInfo(expandInfoList);
                                    RestaurantExpandInfoUtils.setCommonExpandInfo(expandInfoList);
                                    saveSuccessCount++;
                                    if (saveDownDataListener != null) {
                                        saveDownDataListener.onComplete(nextIndex);
                                    }
                                }
                            });
                }
                break;
            case 8:
                PrinterListBean printerListBean = JsonUtils.parseObject(data, PrinterListBean.class);
                if (printerListBean != null) {
                    Observable.just(0)
                            .observeOn(Schedulers.io())
                            .subscribe(new Consumer<Integer>() {
                                @Override
                                public void accept(@NonNull Integer integer) throws Exception {
                                    PrinterDeviceDaoUtils printerDeviceDaoUtils = new PrinterDeviceDaoUtils(IActivity);
                                    printerDeviceDaoUtils.deleteAll();
                                    printerDeviceDaoUtils.insertMultPrinterDeviceBean(printerListBean.getList());
                                    saveSuccessCount++;
                                    if (saveDownDataListener != null) {
                                        saveDownDataListener.onComplete(nextIndex);
                                    }
                                }
                            });
                }
                break;
            case 9:
                List<ColorBean> colorBeanList = GsonUtils.jsonToList(data, ColorBean.class);
                if (colorBeanList != null) {
                    Observable.just(0)
                            .observeOn(Schedulers.io())
                            .subscribe(new Consumer<Integer>() {
                                @Override
                                public void accept(@NonNull Integer integer) throws Exception {
                                    ColorDaoUtils colorDaoUtils = new ColorDaoUtils(IActivity);
                                    colorDaoUtils.deleteAll();
                                    colorDaoUtils.insertMultColorBean(colorBeanList);
                                    saveSuccessCount++;
                                    if (saveDownDataListener != null) {
                                        saveDownDataListener.onComplete(nextIndex);
                                    }
                                }
                            });
                }
                break;
        }
        return false;
    }

    private void initDownState() {
        for (int i = 0; i < mList.size(); i++) {
            //初始化下载状态
            mList.get(i).setState(1);
        }
        mAapter.notifyDataSetChanged();
    }

    private String[] getDownloadUrls(int restaurantId) {
        String[] urls = new String[urlList.length];
        for (int i = 0; i < urlList.length; i++) {
            if (i != 0) {
                urls[i] = urlList[i] += "?restaurantId=" + restaurantId;
            }
        }
        return urls;
    }

    private List<DownloadRequest> getDownloadRequests(String[] urls, int restaurantId) {
        List<DownloadRequest> requests = new ArrayList<>();
        for (int i = 0; i < urls.length; i++) {
            DownloadRequest request = new DownloadRequest();
            request.setRestaurantId(restaurantId);
            request.setDownTag(i);
            request.setUrl(urls[i]);
            requests.add(request);
        }
        return requests;
    }

    private void endDownReturn() {
        if (IActivity.getFromPage() == 1) {
            CC.obtainBuilder(ComponentName.COMPONENT_MAIN)
                    .setActionName("showMainActivity")
                    .build()
                    .call();
        }
        mRootView.killMyself();
    }

    public void initDataDownItem() {
        for (int i = 0; i < downTitles.length; i++) {
            DataDownLoadState downLoadState1 = new DataDownLoadState();
            downLoadState1.setState(0);
            downLoadState1.setText(downTitles[i]);
            mList.add(downLoadState1);
        }
    }

    /**
     * 設置下載進度比例
     */
    public void setDownAverageRatio() {
        if (ratios.length != mList.size()) {
            for (int i = 0; i < mList.size(); i++) {
                //初始化下载状态
                mList.get(i).setRatio(100 / mList.size());
            }
            return;
        }
        for (int i = 0; i < mList.size(); i++) {
            //初始化下载状态
            mList.get(i).setRatio(ratios[i]);
        }
    }

    public void stopDownDisposable() {
        for (int i = 0; i < mDownDisposable.size(); i++) {
            if (mDownDisposable.get(i) != null && !mDownDisposable.get(i).isDisposed()) {
                mDownDisposable.get(i).dispose();
            }
        }
        mDownDisposable.clear();
    }

    public interface OnSaveDownDataListener {
        void onComplete(int nextIndex);
    }
}
