package kz.arta.synergy.workflow.mobile.android.files.fragments;

import android.annotation.SuppressLint;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.devspark.appmsg.AppMsg;
import com.pnikosis.materialishprogress.ProgressWheel;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import kz.arta.synergy.workflow.mobile.android.R;
import kz.arta.synergy.workflow.mobile.android.common.ActivityConst;
import kz.arta.synergy.workflow.mobile.android.common.SettingsConst;
import kz.arta.synergy.workflow.mobile.android.common.activities.MainActivity;
import kz.arta.synergy.workflow.mobile.android.common.activities.SettingsActivity;
import kz.arta.synergy.workflow.mobile.android.common.components.CustomImageView;
import kz.arta.synergy.workflow.mobile.android.common.models.Person;
import kz.arta.synergy.workflow.mobile.android.common.offline.SyncMain;
import kz.arta.synergy.workflow.mobile.android.common.requests.RestClient;
import kz.arta.synergy.workflow.mobile.android.common.requests.SendGetRequest;
import kz.arta.synergy.workflow.mobile.android.common.utils.Const;
import kz.arta.synergy.workflow.mobile.android.common.utils.Fonts;
import kz.arta.synergy.workflow.mobile.android.common.utils.Logger;
import kz.arta.synergy.workflow.mobile.android.common.utils.MyApplication;
import kz.arta.synergy.workflow.mobile.android.common.utils.Utils;
import kz.arta.synergy.workflow.mobile.android.files.layouts.FavoriteFoldersTree;
import kz.arta.synergy.workflow.mobile.android.files.layouts.FoldersTree;
import kz.arta.synergy.workflow.mobile.android.files.models.FavoriteNode;
import kz.arta.synergy.workflow.mobile.android.files.models.Node;
import kz.arta.synergy.workflow.mobile.android.files.offline.FilesFiltersTable;

import static kz.arta.synergy.workflow.mobile.android.common.utils.Logger.TAG;

/**
 * Created by neox on 01.11.14.
 * Дерево папок
 */
public class FragmentFilesTree extends Fragment {

    /**
     * Выделенный элемент основного дерева
     */
    public static FoldersTree selectedItem;
    /**
     * Выделенный элемент избранных папок
     */
    public static FavoriteFoldersTree selectedFolderItem;
    private ProgressBar progressBar;
    private TextView syncButtonTitle;
    private SharedPreferences settings;
    /**
     * Пользователь
     */
    private Person person;
    /**
     * View
     */
    private View view;
    /**
     * Прогрессбар
     */
    private ProgressWheel treeProgressBar;
    /**
     * Контейнер в котором находится список
     */
    private SwipeRefreshLayout refreshLayout;
    /**
     * Анимация (для отрисовки дерева)
     */
    private Animation startAnim;
    /**
     * Фрагмент, содержащий в себе список файлов
     */
    private FragmentFilesList fragmentList;
    /**
     * Контейнер для дерева
     */
    private LinearLayout treeLayout;
    /**
     * Найденные ноды по идентификатору
     */
    private List<FoldersTree> folders = new ArrayList<>();
    /**
     * Ссылка на текущий фрагмент
     */
    private FragmentFilesTree fragment;
    /**
     * Вызывается когда данные загружены
     */
    private FinishedHandler finishedHandler;

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        settings = getActivity().getSharedPreferences(SettingsConst.DEFAULT_NAME_SETTINGS, Context.MODE_PRIVATE);
        view = inflater.inflate(R.layout.fragment_filters, null);
        treeProgressBar = (ProgressWheel) view.findViewById(R.id.filters_progress_bar);
        person = (Person) getArguments().getSerializable(ActivityConst.PERSON);
        startAnim = AnimationUtils.loadAnimation(getActivity(), R.anim.start_animation_activity);
        fragment = this;
        initFilters();
        initSyncButton();
//        saveState();
        return view;
    }

    @Override
    public void onResume() {
        initSyncButton();
        super.onResume();
    }

    /**
     * Запоминаем свернуто ли приложение кнопкой "Back"
     */
    private void saveState() {
        if (settings.getBoolean(Const.BACK_PRESSED_SYNC, false)) {
            allSync(true);
        }
        SharedPreferences.Editor editor = settings.edit();
        editor.putBoolean(Const.BACK_PRESSED_SYNC, true);
        editor.apply();
    }

    /**
     * Инициализация кнопки синхронизации
     */
    private void initSyncButton() {

        // заголовок прогрессбара
        syncButtonTitle = (TextView) view.findViewById(R.id.sync_button_title);
        syncButtonTitle.setTypeface(Fonts.getThin(getActivity()));

        // если есть сохраненный период синхронизации - выставляем
        if (settings.contains(Const.SYNC_DATA)) {
            syncButtonTitle.setText("Последняя синхронизация: " + settings.getString(Const.SYNC_DATA, ""));
        } else {
            syncButtonTitle.setText("Синхронизация не производилась");
        }

        // иконка синхронизации
        ImageView syncIcon = (ImageView) view.findViewById(R.id.sync_icon);

        // прогрессбар
        progressBar = (ProgressBar) view.findViewById(R.id.sync_button);
        progressBar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // если есть интеннет - запускаем синхронизацию
                allSync(false);
            }
        });

        // если в настройках выставлена галочка синхронизировать и если текущий пользователь запомнил себя
        if (Utils.checkSyncEnables(settings)) {
            progressBar.setVisibility(View.VISIBLE);
            syncIcon.setVisibility(View.VISIBLE);
            syncButtonTitle.setVisibility(View.VISIBLE);
        } else {
            progressBar.setVisibility(View.GONE);
            syncIcon.setVisibility(View.GONE);
            syncButtonTitle.setVisibility(View.GONE);
        }
    }

    /**
     * Метод выполняет полную синхронизацию. Вызывается при клике на кнопку "Синхронизация"
     *-
     * @param send - нужно ли отправлять данные
     */
    private void allSync(final boolean send) {
        if (Utils.isOnline(getActivity(), person.getHost()) && Utils.checkSyncEnables(settings)) {
            if (SyncMain.canSync) {
                new SendGetRequest("about", person) {
                    @Override
                    protected void onPostExecute(String result) {
                        if (result == null) {
                            return;
                        }
                        SyncMain sync = new SyncMain(getActivity().getBaseContext());
                        sync.setPerson(person);
                        sync.setProgressBar(progressBar);
                        sync.setProgressBarTitle(syncButtonTitle);
                        sync.init();
                        sync.setShowProgress(true);
                        sync.setAllSync(true);
                        sync.setSend(send);
                        sync.clearTables(false);
                        sync.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
                        super.onPostExecute(result);
                    }
                }.execute();
            } else {
                AppMsg.makeText(getActivity(), getString(R.string.can_not_sync), AppMsg.STYLE_ALERT).show();
            }
        }
    }

    /**
     * Инициализация кнопки "Настройки" и загрузка дерева
     */
    private void initFilters() {
        // заголовок экрана
        TextView navigatorTitle = (TextView) view.findViewById(R.id.title_action_bar_filters);
        navigatorTitle.setTypeface(Fonts.getMedium(getActivity()));
        // кнопка "Настройка"
        CustomImageView settings = (CustomImageView) view.findViewById(R.id.icon_settings);
        settings.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getActivity(), SettingsActivity.class);
                intent.putExtra(ActivityConst.PERSON, person);
                startActivity(intent);
            }
        });
        // главный контейнер фильтров
        refreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refresh_filters);
        refreshLayout.setColorSchemeResources(R.color.color1_update_list, R.color.color2_update_list,
                R.color.color3_update_list, R.color.color4_update_list);
        refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                refreshLayout.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        getTree();
                    }
                }, 0);
            }
        });
        // контейнер для фильтров
        treeLayout = (LinearLayout) view.findViewById(R.id.filters_list);
        // загрузка дерева
        getTree();
    }

    /**
     * Метод загружает список папок и отображаеет их в дереве
     */
    private void getTree() {
        getFavoriteFolders();
        getMainFolders();
    }

    /**
     * Метод используется для загрузки дерева избранных папок
     */
    private void getFavoriteFolders() {
        new SendGetRequest("rest/api/storage/favorite_folders?locale=" + MyApplication.getLang(), person) {
            @Override
            protected void onPostExecute(String result) {
                try {
                    FavoriteNode[] files = RestClient.getGson().fromJson(result, FavoriteNode[].class);
                    if (files.length == 0) {
                        return;
                    }
                    FavoriteFoldersTree favoriteFolderNode = new FavoriteFoldersTree(getString(R.string.favorites), getActivity(), 1);
                    favoriteFolderNode.setNodes(files);
                    favoriteFolderNode.setFragmentTree(fragment);
                    favoriteFolderNode.setPerson(person);
                    favoriteFolderNode.expand();
                    selectedFolderItem = favoriteFolderNode;
                    treeLayout.addView(favoriteFolderNode);
                    treeLayout.startAnimation(startAnim);
                    View view = new View(getActivity());
                    view.setLayoutParams(new LinearLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, Utils.dpToPx(1, getActivity())));
                    view.setBackgroundColor(getActivity().getResources().getColor(R.color.driver_list_filters));
                    treeLayout.addView(view);
                } catch (Exception exc) {
                    Logger.v(exc.getLocalizedMessage());
                }
                super.onPostExecute(result);
            }
        }.execute();
    }

    /**
     * Метод используется для загрузки основного дерева папок
     */
    @SuppressLint("StaticFieldLeak")
    private void getMainFolders() {
        new SendGetRequest("rest/api/storage/list?getOnlyFolders=true&locale=" + MyApplication.getLang(), person) {
            @Override
            protected void onPreExecute() {
                treeLayout.removeAllViews();
                super.onPreExecute();
            }

            @Override
            protected void onPostExecute(String result) {
                refreshLayout.setRefreshing(false);
                treeProgressBar.setVisibility(View.GONE);
                if (result == null) {
                    result = new FilesFiltersTable(getActivity().getBaseContext()).get(person.getHost()
                            + "rest/api/storage/list?getOnlyFolders=true&locale=" + MyApplication.getLang());
                }
                try {
                    Node[] files = RestClient.getGson().fromJson(result, Node[].class);
                    int count = 0;
                    for (Node node : files) {
                        FoldersTree folderNode = new FoldersTree(node.getName(), getActivity(), 1);
                        folderNode.setNode(node);
                        folderNode.setProgressBar(treeProgressBar);
                        folderNode.setPerson(person);
                        folderNode.setFragmentList(fragmentList);
                        folderNode.setFragmentTree(fragment);
                        folderNode.setParents(new ArrayList<Node>());
                        if (count == 0) {
                            folderNode.getElementName().setTextColor(getResources().getColor(R.color.selected_filter_item));
                            selectedItem = folderNode;
                        }
                        treeLayout.addView(folderNode);
                        treeLayout.startAnimation(startAnim);
                        count++;
                    }
                    if (files.length != 0) {
                        // загружаем список файлов для первого фильтра в списке
                        fragmentList.getFiles(files[0], true, true);
                    }
                    if (finishedHandler != null) {
                        finishedHandler.onFinished();
                    }
                } catch (Exception exc) {
                    AppMsg.makeText(getActivity(), getString(R.string.no_connection), AppMsg.STYLE_ALERT).show();
                    Log.e(TAG, exc.getMessage());
                } finally {
                    //UPD: 2023-11-13 - блок finally будет выполняться в любом случае, поэтому это сообщение тут не уместно.
                    // перенес его в catch по аналогии с остальными activities
                    //AppMsg.makeText(getActivity(), getString(R.string.no_connection), AppMsg.STYLE_ALERT).show();
                    if (fragmentList != null) {
                        fragmentList.hideProgressBar();
                    }
                }
                super.onPostExecute(result);
            }
        }.execute();
    }

    /**
     * Раскрытие дерева по родителям
     *
     * @param parents родители
     */
    public void expandTree(List<String> parents) {
        getFolders(parents);
    }

    /**
     * Метод используется для раскрытия ноды с идентификатором nodeID
     *
     * @param nodeID идентификатор ноды
     */
    public void expandNode(String nodeID) {
        expandNode(treeLayout, nodeID, true);
    }

    /**
     * Метод используется для обновления ноды
     *
     * @param nodeID идентификатор ноды
     */
    public void updateNode(String nodeID) {
        expandNode(treeLayout, nodeID, false);
        expandNode(treeLayout, nodeID, true);
    }

    /**
     * Метод возвращает ноду в дереве по ее идентификатору
     *
     * @param nodeID идентификатор ноды
     */
    public void getNodeById(String nodeID) {
        folders.clear();
        getNodeById(treeLayout, nodeID);
    }

    /**
     * Метод ищет в дереве ноду с идентификатором nodeID и разворачивает ее добавляя к ней список папок nodes
     *
     * @param layout  контейнер с деревом
     * @param parents массив с идентификаторами родителей
     */
    private void expandNode(LinearLayout layout, Map<String, List<Node>> parents) {
        for (int i = 0; i < layout.getChildCount(); i++) {
            if (layout.getChildAt(i) instanceof FoldersTree) {
                FoldersTree node = (FoldersTree) layout.getChildAt(i);
                for (Map.Entry<String, List<Node>> entry : parents.entrySet()) {
                    // если нода нашлась
                    if (node.getNode().getIdentifier().equals(entry.getKey()) && !node.isState()) {
                        // раскрываем ее
                        node.expand(entry.getValue());
                    }
                }
                expandNode(node, parents);
            }
        }
    }

    /**
     * Метод ищет в дереве ноду с идентификатором nodeID и разворает/сворачивает ее (в зависимости от переданного параметра expand)
     *
     * @param layout контейнер, в котором находится список
     * @param nodeID идентификатор ноды
     * @param expand true - раскрыавем дерево, false - сворачиваем
     */
    private void expandNode(LinearLayout layout, String nodeID, boolean expand) {
        for (int i = 0; i < layout.getChildCount(); i++) {
            if (layout.getChildAt(i) instanceof FoldersTree) {
                FoldersTree node = (FoldersTree) layout.getChildAt(i);
                // если нода нашлась
                if (node.getNode().getIdentifier().equals(nodeID)) {
                    // если используется раскрытие
                    if (expand) {
                        // если текущая нода не раскрыта
                        if (!node.isState()) {
                            // раскрываем ее
                            node.expand();
                            node.setSelected(false);
                        }
                    } else {
                        // сворачивание ноды
                        if (node.isState()) {
                            node.collapse(false);
                        }
                    }
                    return;
                }
                expandNode(node, nodeID, expand);
            }
        }
    }

    /**
     * Метод получает список папок для каждой ноды в массиве parents.
     * После этого происходит добавление в дерево каждого полученного списка (соблюдая структуру)
     *
     * @param parents идентификаторы родителей
     */
    private void getFolders(final List<String> parents) {
        new AsyncTask<Void, Void, Map<String, List<Node>>>() {
            @Override
            protected void onPostExecute(Map<String, List<Node>> result) {
                if (result == null) {
                    return;
                }
                // раскрываем дерево
                expandNode(treeLayout, result);
                // после чего получаем последнюю ноду в списке
                getNodeById(parents.get(parents.size() - 1));
                // и выделяем ее в дереве
                getCurrentNode().setSelected(true);
                if (((MainActivity) getActivity()).getFiltersSlidingMenu().isMenuShowing()) {
                    // закрываем боковую навигацию
                    ((MainActivity) getActivity()).getFiltersSlidingMenu().toggle();
                }
                super.onPostExecute(result);
            }

            @Override
            protected Map<String, List<Node>> doInBackground(Void... voids) {
                try {
                    Map<String, List<Node>> result = new HashMap<>();
                    for (String nodeID : parents) {
                        String response = new SendGetRequest(person).requestGET(person.getHost() + "rest/api/storage/list?parentID="
                                + nodeID + "&getOnlyFolders=true&locale=" + MyApplication.getLang());
                        Node[] files = RestClient.getGson().fromJson(response, Node[].class);
                        Arrays.sort(files, new Comparator<Node>() {
                            @Override
                            public int compare(Node node1, Node node2) {
                                return node1.getName().compareTo(node2.getName());
                            }
                        });
                        List<Node> nodes = new ArrayList<>(Arrays.asList(files));
                        result.put(nodeID, nodes);
                    }
                    return result;
                } catch (Exception exc) {
                    return null;
                }
            }
        }.execute();
    }

    /**
     * Метод возвращает ноду в дереве по ее идентификатору
     *
     * @param layout контейнер, в котором находится список
     * @param nodeID идентификатор ноды
     */
    private void getNodeById(LinearLayout layout, String nodeID) {
        for (int i = 0; i < layout.getChildCount(); i++) {
            if (layout.getChildAt(i) instanceof FoldersTree) {
                FoldersTree node = (FoldersTree) layout.getChildAt(i);
                if (node.getNode().getIdentifier().equals(nodeID)) {
                    folders.add(node);
                }
                getNodeById(node, nodeID);
            }
        }
    }

    public void setFragmentList(FragmentFilesList fragmentList) {
        this.fragmentList = fragmentList;
    }

    public FoldersTree getCurrentNode() {
        if (folders.isEmpty()) {
            return null;
        } else {
            return folders.get(0);
        }
    }

    public void setFinishedHandler(FinishedHandler finishedHandler) {
        this.finishedHandler = finishedHandler;
    }

    public interface FinishedHandler {
        void onFinished();
    }
}