package kz.arta.synergy.workflow.mobile.android.forms.general.activities;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.view.animation.TranslateAnimation;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.Button;

import com.devspark.appmsg.AppMsg;
import com.github.lzyzsd.jsbridge.BridgeHandler;
import com.github.lzyzsd.jsbridge.BridgeWebView;
import com.github.lzyzsd.jsbridge.CallBackFunction;
import com.github.lzyzsd.jsbridge.DefaultHandler;
import com.google.gson.reflect.TypeToken;
import com.pnikosis.materialishprogress.ProgressWheel;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

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.components.CustomImageView;
import kz.arta.synergy.workflow.mobile.android.common.components.OpenFileDialog;
import kz.arta.synergy.workflow.mobile.android.common.models.ErrorCode;
import kz.arta.synergy.workflow.mobile.android.common.models.Person;
import kz.arta.synergy.workflow.mobile.android.common.requests.RestClient;
import kz.arta.synergy.workflow.mobile.android.common.utils.ActionUtils;
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.Wrapper;
import kz.arta.synergy.workflow.mobile.android.documents.DocumentConst;
import kz.arta.synergy.workflow.mobile.android.documents.common.activities.DocumentInfoActivity;
import kz.arta.synergy.workflow.mobile.android.files.FilesConst;
import kz.arta.synergy.workflow.mobile.android.files.activities.FilesTreeActivity;
import kz.arta.synergy.workflow.mobile.android.files.manager.DownloadFile;
import kz.arta.synergy.workflow.mobile.android.files.manager.FileUpload;
import kz.arta.synergy.workflow.mobile.android.files.manager.FilesManager;
import kz.arta.synergy.workflow.mobile.android.files.models.Node;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.addressbook.AddressBookActivity;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.addressbook.AddressBookConst;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.addressbook.AddressBookModel;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.date.FormDatePicker;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.link.LinkActivity;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.listbox.ListBox;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.projectchooser.ProjectChooserActivity;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.projectchooser.ProjectModel;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.registrychooser.RegistryChooserActivity;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.repeat.RepeatPeriodActivity;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.repeat.RepeatPeriodConst;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.time.FormTimePicker;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.users.UsersChooserConst;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.users.activity.DepartmentsChooserActivity;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.users.activity.PositionsChooserActivity;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.users.activity.UsersChooserActivity;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.users.model.DepartmentModel;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.users.model.PositionModel;
import kz.arta.synergy.workflow.mobile.android.forms.general.components.users.model.UserModel;
import kz.arta.synergy.workflow.mobile.android.forms.mobile.activities.FormPlayerNative;
import kz.arta.synergy.workflow.mobile.android.forms.mobile.manager.SaveFormData;

/**
 * Created by neox on 04.04.16.
 * Просмторщик форм
 */
public class FormPlayer extends FragmentActivity {

    public static final int USER_CHOOSER = 1;
    public static final int DEPARTMENT_CHOOSER = 2;
    public static final int POSITIONS_CHOOSER = 3;
    public static final int REPEAT_PERIOD = 4;
    public static final int LINK = 5;
    public static final int ADDRESS_BOOK = 6;
    public static final int REGISTRY_LINK = 7;
    public static final int PROJECT_LINK = 8;
    public static final int RESULT_LOAD_IMAGE = 9;
    /**
     * Сохранение через кнопку "Сохранить"
     */
    private static final int SAVE_BUTTON = 1;
    /**
     * Сохранение через нажатие на кнопку "Назад"
     */
    private static final int BACK_PRESSED = 2;
    /**
     * Сохранение через кнопку "Готово"
     */
    private static final int FINISH_BUTTON = 3;
    /**
     * Изменение режима
     */
    private static final int CHANGE_MODE = 4;
    private CallBackFunction usersCallback;
    private CallBackFunction departmentsCallback;
    private CallBackFunction positionsCallback;
    private CallBackFunction repeatCallback;
    private CallBackFunction linkCallBack;
    private CallBackFunction fileLinkCallBack;
    private CallBackFunction fileImageCallBack;
    private CallBackFunction registryLinkCallBack;
    private CallBackFunction addressBookCallBack;
    private CallBackFunction projectLinkCallBack;
    /**
     * Активити
     */
    private Activity activity;

    /**
     * Прогрессбар
     */
    private ProgressWheel progressBar;

    /**
     * Пользователь
     */
    private Person person;

    /**
     * Кнопка "Сохранить"
     */
    private CustomImageView saveButton;

    /**
     * Бразуер (просмотрщик форм)
     */
    private BridgeWebView webView;

    /**
     * Кнопка "Готово"
     */
    private Button finishButton;

    /**
     * Режим просмотра/редактирования
     */
    private boolean editMode;

    /**
     * Идентификатор данных по форме
     */
    private String dataUUID;

    /**
     * Идентификатор формы
     */
    private String formUUID;

    /**
     * Идентификатор файла по форме
     */
    private String fileUUID;

    /**
     * Вертикальный скролл
     */
    private int yPosition;

    /**
     * Имеется ли мобильное представление у текущей формы
     */
    private boolean hasMobileDef;

    /**
     * Доступные элементы меню
     */
    private List<String> menuItems;

    /**
     * Тип сохранения формы
     *
     * @see #SAVE_BUTTON
     * @see #BACK_PRESSED
     * @see #FINISH_BUTTON
     */
    private int saveType;

    /**
     * Можно ли редактировать текущую форму
     */
    private boolean canEdit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        setContentView(R.layout.form_player_activity);
        super.onCreate(savedInstanceState);

        activity = this;

        progressBar = (ProgressWheel) findViewById(R.id.form_player_activity_progress_bar);

        person = (Person) getIntent().getSerializableExtra(ActivityConst.PERSON);
        dataUUID = getIntent().getStringExtra(ActivityConst.DATA_UUID);
        fileUUID = getIntent().getStringExtra(ActivityConst.FILE_IDENTIFIER);
        editMode = getIntent().getBooleanExtra(ActivityConst.EDIT, false);
        canEdit = Boolean.parseBoolean(getIntent().getStringExtra(ActivityConst.CAN_EDIT));

        if (getActionBar() != null) {
            getActionBar().hide();
        }

        if (dataUUID != null) {
            initWebView();
            initButtons();
        } else {
            AppMsg.makeText(activity, "Не найден идентификатор данных по форме", AppMsg.STYLE_ALERT).show();
            progressBar.setVisibility(View.GONE);
        }
    }

    /**
     * Инициализация кнопок
     */
    private void initButtons() {

        // кнопка "Сохранить"
        saveButton = (CustomImageView) findViewById(R.id.form_player_activity_button_save);
        saveButton.setEnabled(false);
        saveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                saveType = SAVE_BUTTON;
                webView.loadUrl("javascript:isValid();");
            }
        });

        if (editMode) {
            saveButton.setVisibility(View.VISIBLE);
        }

        // кнопка "Меню"
        CustomImageView menuButton = (CustomImageView) findViewById(R.id.form_player_activity_button_menu);
        menuButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                webView.loadUrl("javascript:hasMobileDef();");
            }
        });

        // кнопка "Готово"
        finishButton = (Button) findViewById(R.id.form_player_activity_finish_button);
        finishButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                saveType = FINISH_BUTTON;
                webView.loadUrl("javascript:isValid();");
            }
        });
    }

    /**
     * Метод вызывается при нажатии на кнопку меню
     *
     * @param formats доступные форматы
     */
    private void showMenuDialog(List<String> formats) {
        menuItems = new ArrayList<>();
        // переключение режимов
        if (hasMobileDef) {
            menuItems.add(getString(R.string.mobile_version));
        }
        if (!editMode) {
            if (canEdit) {
                menuItems.add(getString(R.string.edit_mode));
            }
            if (formats.contains("pdf")) {
                menuItems.add(getString(R.string.download_pdf));
            }
            if (formats.contains("odt")) {
                menuItems.add(getString(R.string.download_odt));
            }
            if (formats.contains("docx")) {
                menuItems.add(getString(R.string.download_docx));
            }
            if (formats.contains("doc")) {
                menuItems.add(getString(R.string.download_doc));
            }
        } else {
            menuItems.add(getString(R.string.view_mode));
        }
        menuItems.add(getString(R.string.information));
        AlertDialog.Builder dialog = new AlertDialog.Builder(activity);
        dialog.setTitle(getResources().getString(R.string.menu));
        dialog.setItems(menuItems.toArray(new String[menuItems.size()]), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int index) {
                if (menuItems.get(index).equals(getString(R.string.edit_mode))) {
                    editMode = true;
                    setEditable(true);
                } else if (menuItems.get(index).equals(getString(R.string.view_mode))) {
                    saveType = CHANGE_MODE;
                    webView.loadUrl("javascript:isValid();");
                } else if (menuItems.get(index).equals(getString(R.string.mobile_version))) {
                    changePlayer();
                }
                if (menuItems.get(index).equals(getString(R.string.download_pdf))) {
                    getFile("pdf");
                }
                if (menuItems.get(index).equals(getString(R.string.download_odt))) {
                    getFile("odt");
                }
                if (menuItems.get(index).equals(getString(R.string.download_docx))) {
                    getFile("docx");
                }
                if (menuItems.get(index).equals(getString(R.string.download_doc))) {
                    getFile("doc");
                }
                if (menuItems.get(index).equals(getString(R.string.information))) {
                    getFormInfo();
                }
            }
        });
        dialog.show();
    }

    /**
     * Метод открывает нативный просмотрщик форм
     */
    private void changePlayer() {
        Intent intent = new Intent(activity, FormPlayerNative.class);
        intent.putExtra(ActivityConst.DATA_UUID, dataUUID);
        intent.putExtra(ActivityConst.CAN_EDIT, canEdit);
        intent.putExtra(ActivityConst.PERSON, person);
        activity.startActivityForResult(intent, ActionUtils.SAVE_FORM);
        finish();
    }

    /**
     * Инициализация webView
     */
    private void initWebView() {
        webView = (BridgeWebView) findViewById(R.id.form_view_activity_web_view);

        webView.setDefaultHandler(new DefaultHandler());
        webView.getSettings().setDisplayZoomControls(false);
        webView.getSettings().setBuiltInZoomControls(true);
        webView.getSettings().setLoadWithOverviewMode(true);
        webView.getSettings().setUseWideViewPort(true);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.loadUrl(getUrl());
        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                // когда форма полностью загрузилась - выключаем прогрессбар
                if (newProgress == 100) {
                    progressBar.setVisibility(View.GONE);
                    webView.setInitialScale(0);
                }
            }
        });
        webView.setOnScrollChangedCallback(new BridgeWebView.OnScrollChangedCallback() {
            @Override
            public void onScroll(int horizontalPosition, int y) {
                if (y <= yPosition) {
                    if (finishButton.getVisibility() == View.GONE) {
                        TranslateAnimation animate = new TranslateAnimation(0, 0, 300, 0);
                        animate.setDuration(300);
                        animate.setFillAfter(true);
                        finishButton.startAnimation(animate);
                        finishButton.setVisibility(View.VISIBLE);
                    }
                } else {
                    if (finishButton.getVisibility() == View.VISIBLE) {
                        TranslateAnimation animate = new TranslateAnimation(0, 0, 0, 300);
                        animate.setDuration(300);
                        animate.setFillAfter(true);
                        finishButton.startAnimation(animate);
                        finishButton.setVisibility(View.GONE);
                    }
                }
                yPosition = y;
            }
        });
        // регистрация хендлеров
        registerHandlers();
    }

    /**
     * Регистрация хендлеров
     */
    private void registerHandlers() {
        // валидация формы
        webView.registerHandler("isValid", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                isValidHandler(data);
            }
        });
        // имя формы
        webView.registerHandler("getFormName", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                getFormNameHandler(data);
            }
        });
        // имеется ли мобильное представление
        webView.registerHandler("hasMobileDef", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                hasMobileDefHandler(data);
            }
        });
        // форматы печатного представления
        webView.registerHandler("getPrintableFormats", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                getPrintableFormatsHandler(data);
            }
        });
        // получение данных по форме
        webView.registerHandler("getAsfData", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                getAsfDataHandler(data);
            }
        });
        // есть ли несохраненные изменения
        webView.registerHandler("hasChanges", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                hasChangesHandler(data);
            }
        });
        // выбор пользователей
        webView.registerHandler("showUserChooserDialog", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showUserChooserDialogHandler(data, function);
            }
        });
        // выбор департаментов
        webView.registerHandler("showDepartmentChooserDialog", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showDepartmentChooserDialogHandler(data, function);
            }
        });
        // выбор должностей
        webView.registerHandler("showPositionChooserDialog", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showPositionChooserDialogHandler(data, function);
            }
        });
        // вызоыв контекстного меню
        webView.registerHandler("showDropDown", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showDropDownHandler(data, function);
            }
        });
        // выбор даты
        webView.registerHandler("showDatePickerDialog", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showDatePickerDialogHandler(data, function);
            }
        });
        // выбор времени
        webView.registerHandler("showTimePickerDialog", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showTimePickerDialogHandler(data, function);
            }
        });
        // период повторения
        webView.registerHandler("showDaysChooser", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showDaysChooserHandler(data, function);
            }
        });
        // настройки ссылки
        webView.registerHandler("showEditLinkDialog", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                editLink(data, function);
            }
        });
        // ссылка
        webView.registerHandler("openLink", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                openLink(data);
            }
        });
        // открытие файла
        webView.registerHandler("openFile", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                openFile(data);
            }
        });
        // с хранилища
        webView.registerHandler("showUploadFileDialog", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                uploadFile(data, function);
            }
        });
        // новый файл
        webView.registerHandler("showFileLinkDialog", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showFileLinkDialog(data, function);
            }
        });
        // ссылка на реестр
        webView.registerHandler("showRegisterLinkDialog", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showRegistryLinkDialog(data, function);
            }
        });
        // ссылка на адресную книгу
        webView.registerHandler("showAddressBookDialog", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showAddressBookDialog(function);
            }
        });
        // тап по адресной книге в режиме просмотра
        webView.registerHandler("showAddressBookItem", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                AppMsg.makeText(activity, getString(R.string.mobile_form_player_address_book_error), AppMsg.STYLE_ALERT).show();
            }
        });
        // тап по портфелю в режиме просмотра
        webView.registerHandler("showPlan", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                AppMsg.makeText(activity, getString(R.string.mobile_form_player_project_error), AppMsg.STYLE_ALERT).show();
            }
        });
        // тап по проекту в режиме просмотра
        webView.registerHandler("showPortfolio", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                AppMsg.makeText(activity, getString(R.string.mobile_form_player_project_error), AppMsg.STYLE_ALERT).show();
            }
        });
        // информация о документе
        webView.registerHandler("showDocument", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showDocumentInfo(data);
            }
        });
        // ссылка на проект/портфель
        webView.registerHandler("showProjectLinkDialog", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showProjectLinkDialog(function);
            }
        });
        // информация о документе
        webView.registerHandler("showDocument", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                showDocumentInfo(data);
            }
        });
        // изменил ли данные пользователь
        webView.registerHandler("valueChanged", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                saveButton.setEnabled(true);
            }
        });
        // отображение прогрессбара
        webView.registerHandler("showWaitWindow", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                progressBar.setVisibility(View.VISIBLE);
            }
        });
        // скрытие прогрессбара
        webView.registerHandler("hideWaitWindow", new BridgeHandler() {
            @Override
            public void handler(String data, CallBackFunction function) {
                progressBar.setVisibility(View.GONE);
            }
        });
    }

    /**
     * Метод открывет экран с информацией о документе
     *
     * @param data идентификатор документа
     */
    private void showDocumentInfo(String data) {
        try {
            JSONObject jsonObject = new JSONObject(data);
            String documentID = jsonObject.getString("elementId");
            Intent intent = new Intent(activity, DocumentInfoActivity.class);
            intent.putExtra(ActivityConst.DOCUMENT_ID, documentID);
            intent.putExtra(ActivityConst.PERSON, person);
            intent.putExtra(DocumentConst.TYPE_ITEM_DOCUMENTS, DocumentConst.ALL);
            startActivity(intent);
        } catch (Exception exc) {
            // handling error ...
        }
    }

    /**
     * Метод используется для открытия экрана выбора записи портфеля/проекта
     *
     * @param function callback
     */
    private void showProjectLinkDialog(CallBackFunction function) {
        this.projectLinkCallBack = function;
        Intent intent = new Intent(FormPlayer.this, ProjectChooserActivity.class);
        intent.putExtra(ActivityConst.PERSON, person);
        startActivityForResult(intent, PROJECT_LINK);
    }

    /**
     * Метод используется для открытия экрана выбора записи адресной книги
     *
     * @param function callback
     */
    private void showAddressBookDialog(CallBackFunction function) {
        this.addressBookCallBack = function;
        Intent intent = new Intent(FormPlayer.this, AddressBookActivity.class);
        intent.putExtra(ActivityConst.PERSON, person);
        startActivityForResult(intent, ADDRESS_BOOK);
    }

    /**
     * Метод используется для открытия экрана выбора записи реестра
     *
     * @param data     данные
     * @param function callback
     */
    private void showRegistryLinkDialog(String data, CallBackFunction function) {
        this.registryLinkCallBack = function;
        try {
            JSONObject jsonObject = new JSONObject(data);
            String registryID = jsonObject.getString("registryID");
            Intent intent = new Intent(FormPlayer.this, RegistryChooserActivity.class);
            intent.putExtra(ActivityConst.PERSON, person);
            intent.putExtra(ActivityConst.REGISTRY, registryID);
            startActivityForResult(intent, REGISTRY_LINK);
        } catch (Exception exc) {
            // handling error ...
        }
    }

    /**
     * Метод вызывается при вызове js функции для отображения диалога выбора пользователей
     *
     * @param data     данные js скрипта
     * @param callBack callback
     */
    private void showUserChooserDialogHandler(String data, CallBackFunction callBack) {
        usersCallback = callBack;
        try {
            JSONObject json = new JSONObject(data);
            boolean isGroupSelectable = json.getBoolean("isGroupSelectable");
            boolean multiSelectable = json.getBoolean("multiSelectable");
            boolean showWithoutPosition = json.getBoolean("showWithoutPosition");
            String filterDepartmentID = json.getString("filterDepartmentID");
            String filterPositionID = json.getString("filterPositionID");
            String str = json.getJSONArray("selectedValues").toString();
            List<UserModel> list = RestClient.getGson().fromJson(str, new TypeToken<List<UserModel>>() {
            }.getType());
            showUserChooserDialog(isGroupSelectable, multiSelectable, showWithoutPosition, filterDepartmentID, filterPositionID, list);
        } catch (Exception exc) {
            // handling error ...
        }
    }

    /**
     * Метод вызывается при вызове js функции для отображения диалога выбора департаментов
     *
     * @param data     данные js скрипта
     * @param callBack callback
     */
    private void showDepartmentChooserDialogHandler(String data, CallBackFunction callBack) {
        departmentsCallback = callBack;
        try {
            JSONObject json = new JSONObject(data);
            boolean multiSelectable = json.getBoolean("multiSelectable");
            String filterUserID = json.getString("filterUserID");
            String filterPositionID = json.getString("filterPositionID");
            String filterDepartmentID = json.getString("filterDepartmentID");
            String str = json.getJSONArray("selectedValues").toString().replace("departmentID", "departmentId");
            List<DepartmentModel> list = RestClient.getGson().fromJson(str, new TypeToken<List<DepartmentModel>>() {
            }.getType());
            showDepartmentChooserDialog(multiSelectable, filterUserID, filterPositionID, filterDepartmentID, list);
        } catch (Exception exc) {
            // handling error ...
        }
    }

    /**
     * Метод вызывается при вызове js функции для отображения диалога выбора подразделений
     *
     * @param data     данные js скрипта
     * @param callBack callback
     */
    private void showPositionChooserDialogHandler(String data, CallBackFunction callBack) {
        positionsCallback = callBack;
        try {
            JSONObject json = new JSONObject(data);
            String str = json.getJSONArray("selectedValues").toString();
            boolean showVacant = json.getBoolean("showVacant");
            String filterUserId = json.getString("filterUserId");
            String filterDepartmentId = json.getString("filterDepartmentId");
            List<PositionModel> list = RestClient.getGson().fromJson(str, new TypeToken<List<PositionModel>>() {
            }.getType());
            showPositionChooserDialog(list, filterUserId, filterDepartmentId, showVacant);
        } catch (Exception exc) {
            // handling error ...
        }
    }

    /**
     * Метод отображает экран для выбора файла
     *
     * @param function callback
     */
    private void showFileLinkDialog(String data, CallBackFunction function) {
        this.fileLinkCallBack = function;
        try {
            JSONObject jsonObject = new JSONObject(data);
            JSONArray array = jsonObject.getJSONArray("mimeTypes");
            Intent intent = new Intent(activity, FilesTreeActivity.class);
            intent.putExtra(FilesConst.SHOW_FILES, true);
            intent.putExtra(ActivityConst.PERSON, person);
            intent.putExtra(FilesConst.ONLY_IMAGES, array.length() > 0);
            startActivityForResult(intent, ActionUtils.UPLOAD_FILE_FROM_STORAGE);
        } catch (Exception exc) {
            // handling error
        }
    }

    /**
     * Метод используется для добавления файла в форму
     *
     * @param function callback
     */
    private void uploadFile(String data, final CallBackFunction function) {
        try {
            fileImageCallBack = function;
            JSONObject jsonObject = new JSONObject(data);
            JSONArray array = jsonObject.getJSONArray("mimeTypes");
            if (array.length() == 0) {
                final OpenFileDialog fileDialog = new OpenFileDialog(activity);
                fileDialog.setOpenDialogListener(new OpenFileDialog.OpenDialogListener() {
                    @Override
                    public void OnSelectedFile(String path) {
                        uploadFile(new File(path).getName(), path, function);
                    }
                });
                fileDialog.show();
            } else {
                Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                startActivityForResult(i, RESULT_LOAD_IMAGE);
            }
        } catch (Exception exc) {
            // handling error ...
        }
    }

    /**
     * Метод вызывается при добавлении файла с устройства
     *
     * @param name имя файла
     * @param path путь до файла
     */
    public void uploadFile(final String name, final String path, final CallBackFunction function) {
        new FileUpload(activity, person, name, path) {
            @Override
            protected void onPostExecute(final String filePath) {
                if (filePath == null) {
                    return;
                }
                new AsyncTask<Void, Void, String>() {
                    @Override
                    protected String doInBackground(Void... voids) {
                        return new FilesManager(person).addFileToForm(filePath, dataUUID, name);
                    }

                    @Override
                    protected void onPostExecute(String result) {
                        try {
                            JSONObject jsonObject = new JSONObject(result);
                            String fileID = jsonObject.getString("fileId");
                            function.onCallBack(fileID);
                        } catch (Exception exc) {
                            // handling error ...
                        }
                        super.onPostExecute(result);
                    }
                }.execute();
                super.onPostExecute(filePath);
            }
        }.execute();
    }

    /**
     * Открытие файла
     *
     * @param data данные с js скрипта
     */
    private void openFile(String data) {
        try {
            JSONObject jsonObject = new JSONObject(data);
            String uuid = jsonObject.getString("id");
            DownloadFile downloadFile = new DownloadFile(activity, person);
            downloadFile.setUrl(person.getHost() + "rest/api/storage/pdf/get?identifier=" + uuid);
            downloadFile.execute();
        } catch (Exception exc) {
            // handling error ...
        }
    }

    /**
     * Закрытие экрана
     */
    private void finishActivity() {
        Intent intent = new Intent();
        intent.putExtra(ActivityConst.FORM_ID, formUUID);
        intent.putExtra(ActivityConst.DATA_UUID, dataUUID);
        intent.putExtra(ActivityConst.FILE_IDENTIFIER, fileUUID);
        setResult(Activity.RESULT_OK, intent);
        finish();
    }

    /**
     * Метод вызывается при вызове js функции для отображения выпадающего списка
     *
     * @param data     данные js скрипта
     * @param callBack callback
     */
    private void showDropDownHandler(String data, CallBackFunction callBack) {
        new ListBox(activity).showDialog(data, callBack);
    }

    /**
     * Метод вызывается при вызове js функции для отображения диалога выбора даты
     *
     * @param data     данные js скрипта
     * @param callBack callback
     */
    private void showDatePickerDialogHandler(String data, CallBackFunction callBack) {
        FormDatePicker picker = new FormDatePicker();
        picker.setDate(data);
        picker.setFunction(callBack);
        picker.show(getSupportFragmentManager());
    }

    /**
     * Метод вызывается при вызове js функции для отображения диалога выбора времени
     *
     * @param data     данные js скрипта
     * @param callBack callback
     */
    private void showTimePickerDialogHandler(String data, CallBackFunction callBack) {
        FormTimePicker picker = new FormTimePicker();
        picker.setDate(data);
        picker.setFunction(callBack);
        picker.show(getSupportFragmentManager());
    }

    /**
     * Метод вызывается при вызове js функции для отображения диалога периода повторения
     *
     * @param data     данные js скрипта
     * @param callBack callback
     */
    private void showDaysChooserHandler(String data, CallBackFunction callBack) {
        repeatCallback = callBack;
        try {
            JSONObject jsonObject = new JSONObject(data);
            int type = jsonObject.getInt("type");
            JSONArray selected = jsonObject.getJSONArray("values");
            String[] selectedArray = new String[selected.length()];
            for (int i = 0; i < selected.length(); i++) {
                selectedArray[i] = selected.getString(i);
            }
            Intent intent = new Intent(activity, RepeatPeriodActivity.class);
            if (type == 1) {
                intent.putExtra(RepeatPeriodConst.PERIOD_REPEAT_TYPE, RepeatPeriodConst.WEEKS);
            } else if (type == 2) {
                intent.putExtra(RepeatPeriodConst.PERIOD_REPEAT_TYPE, RepeatPeriodConst.DAYS);
            } else if (type == 4) {
                intent.putExtra(RepeatPeriodConst.PERIOD_REPEAT_TYPE, RepeatPeriodConst.YEARS);
            }
            intent.putExtra(ActivityConst.SELECTED, selectedArray);
            startActivityForResult(intent, REPEAT_PERIOD);
        } catch (Exception exc) {
            // handling error ...
        }
    }

    /**
     * Метод используется для открытия экрана ретактирования ссылки
     *
     * @param data данные, которые приходят с js функции
     */
    private void editLink(String data, CallBackFunction callBackFunction) {
        try {
            linkCallBack = callBackFunction;
            JSONObject jsonObject = new JSONObject(data);
            String url = jsonObject.getString("url");
            String title = jsonObject.getString("title");
            boolean openNewWindow = jsonObject.getBoolean("openInNew");
            Intent intent = new Intent(FormPlayer.this, LinkActivity.class);
            intent.putExtra(LinkActivity.TITLE, (title == null || title.equals("null") ? "" : title));
            intent.putExtra(LinkActivity.URL, (url == null || url.equals("null") ? "" : url));
            intent.putExtra(LinkActivity.NEW, openNewWindow);
            startActivityForResult(intent, LINK);
        } catch (Exception exc) {
            // handling error ...
        }
    }

    /**
     * Метод используется для открытия ссылки
     *
     * @param data данные, которые приходят с js функции
     */
    private void openLink(String data) {
        try {
            JSONObject jsonObject = new JSONObject(data);
            String url = jsonObject.getString("url");
            Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            startActivity(browserIntent);
        } catch (Exception exc) {
            // handling error ...
        }
    }

    /**
     * Метод открывает экран для выбора пользователей
     *
     * @param isGroupSelectable отображать ли группы
     * @param multiSelectable   позволять ли мультивыбор
     * @param list              список пользователей
     */
    private void showUserChooserDialog(boolean isGroupSelectable, boolean multiSelectable, boolean showWithoutPosition,
                                       String filterDepartmentID, String filterPositionID, List<UserModel> list) {
        Intent intent = new Intent(activity, UsersChooserActivity.class);
        intent.putExtra(UsersChooserConst.SHOW_GROUPS, isGroupSelectable);
        intent.putExtra(UsersChooserConst.MULTI_SELECT, multiSelectable);
        intent.putExtra(UsersChooserConst.SHOW_REMOVED, showWithoutPosition);
        intent.putExtra(UsersChooserConst.FILTER_DEPARTMENT_ID, filterDepartmentID);
        intent.putExtra(UsersChooserConst.FILTER_POSITION_ID, filterPositionID);
        intent.putExtra(UsersChooserConst.SELECTED_ITEMS, RestClient.getGson().toJson(list));
        intent.putExtra(ActivityConst.PERSON, person);
        startActivityForResult(intent, USER_CHOOSER);
    }

    /**
     * Метод открывает экран для выбора департаментов
     *
     * @param multiSelectable позволять ли мультивыбор
     * @param list            список департаментов
     */
    private void showDepartmentChooserDialog(boolean multiSelectable, String filterUserID,
                                             String filterPositionID, String filterDepartmentID, List<DepartmentModel> list) {
        Intent intent = new Intent(activity, DepartmentsChooserActivity.class);
        intent.putExtra(UsersChooserConst.MULTI_SELECT, multiSelectable);
        intent.putExtra(UsersChooserConst.FILTER_USER_ID, filterUserID);
        intent.putExtra(UsersChooserConst.FILTER_DEPARTMENT_ID, filterDepartmentID);
        intent.putExtra(UsersChooserConst.FILTER_POSITION_ID, filterPositionID);
        intent.putExtra(UsersChooserConst.SELECTED_ITEMS, RestClient.getGson().toJson(list));
        intent.putExtra(ActivityConst.PERSON, person);
        startActivityForResult(intent, DEPARTMENT_CHOOSER);
    }

    /**
     * Метод открывает экран для выбора должностей
     *
     * @param list список должностей
     */
    private void showPositionChooserDialog(List<PositionModel> list, String filterUserId,
                                           String filterDepartmentId, boolean showVacant) {
        Intent intent = new Intent(activity, PositionsChooserActivity.class);
        intent.putExtra(UsersChooserConst.SELECTED_ITEMS, RestClient.getGson().toJson(list));
        intent.putExtra(UsersChooserConst.SHOW_VACANT, showVacant);
        intent.putExtra(UsersChooserConst.FILTER_USER_ID, filterUserId);
        intent.putExtra(UsersChooserConst.FILTER_DEPARTMENT_ID, filterDepartmentId);
        intent.putExtra(ActivityConst.PERSON, person);
        startActivityForResult(intent, POSITIONS_CHOOSER);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode != RESULT_OK) {
            return;
        }
        if (requestCode == USER_CHOOSER) {
            String list = data.getStringExtra(UsersChooserConst.SELECTED_ITEMS);
            usersCallback.onCallBack(list);
        } else if (requestCode == DEPARTMENT_CHOOSER) {
            String list = data.getStringExtra(UsersChooserConst.SELECTED_ITEMS);
            departmentsCallback.onCallBack(list);
        } else if (requestCode == POSITIONS_CHOOSER) {
            String list = data.getStringExtra(UsersChooserConst.SELECTED_ITEMS);
            positionsCallback.onCallBack(list);
        } else if (requestCode == REPEAT_PERIOD) {
            String list = data.getStringExtra(UsersChooserConst.SELECTED_ITEMS);
            repeatCallback.onCallBack(list);
        } else if (requestCode == LINK) {
            String title = data.getStringExtra(LinkActivity.TITLE);
            String url = data.getStringExtra(LinkActivity.URL);
            boolean newWindow = data.getBooleanExtra(LinkActivity.NEW, false);
            String result = "{\"title\":\"" + title + "\",\"url\":\"" + url + "\",\"openInNew\":" + newWindow + "}";
            linkCallBack.onCallBack(result);
        } else if (requestCode == ActionUtils.UPLOAD_FILE_FROM_STORAGE) {
            Node node = (Node) data.getSerializableExtra(FilesConst.NODE);
            String nodeJson = RestClient.getGson().toJson(node);
            fileLinkCallBack.onCallBack(nodeJson);
        } else if (requestCode == ADDRESS_BOOK) {
            AddressBookModel addressBookItem = (AddressBookModel) data.getSerializableExtra(ActivityConst.SELECTED);
            String itemId = addressBookItem.getItemID();
            String type = addressBookItem.getItemType();
            String value;
            if (type.equals(AddressBookConst.ITEM_TYPE_PERSON)) {
                value = addressBookItem.getLastName() + " " + addressBookItem.getFirstName();
            } else {
                value = addressBookItem.getName();
            }
            String result = "{\"itemID\":\"" + itemId + "\",\"value\":\"" + value + "\",\"type\":" + (type.equals(AddressBookConst.ITEM_TYPE_PERSON) ? "0" : "1") + "}";
            addressBookCallBack.onCallBack(result);
        } else if (requestCode == REGISTRY_LINK) {
            String dataUUID = data.getStringExtra(ActivityConst.SELECTED);
            registryLinkCallBack.onCallBack(dataUUID);
        } else if (requestCode == PROJECT_LINK) {
            ProjectModel.ProjectItem projectItem = (ProjectModel.ProjectItem) data.getSerializableExtra(ActivityConst.SELECTED);
            String json = RestClient.getGson().toJson(projectItem);
            projectLinkCallBack.onCallBack(json);
        } else if (requestCode == RESULT_LOAD_IMAGE) {
            Uri selectedImage = data.getData();
            String[] filePathColumn = {MediaStore.Images.Media.DATA};
            Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
            if (cursor == null || cursor.getCount() < 1) {
                return;
            }
            cursor.moveToFirst();
            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            if (columnIndex < 0) {
                return;
            }
            String picturePath = cursor.getString(columnIndex);
            uploadFile(new File(picturePath).getName(), picturePath, fileImageCallBack);
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    /**
     * Метод загружает файл на устройство
     *
     * @param format формат файла
     */
    private void getFile(String format) {
        DownloadFile downloadFile = new DownloadFile(activity, person);
        downloadFile.setUrl(person.getHost() + "rest/api/asforms/template/print/form?dataUUID=" + dataUUID + "&format=" + format);
        downloadFile.execute();
    }

    /**
     * Метод переключает просмотрщик (режим редактирования/обычный)
     *
     * @param enabled true/false
     */
    private void setEditable(boolean enabled) {
        if (enabled) {
            saveButton.setVisibility(View.VISIBLE);
            webView.loadUrl("javascript:setEditable(true);");
        } else {
            saveButton.setVisibility(View.GONE);
            webView.loadUrl("javascript:setEditable(false);");
        }
    }

    /**
     * Метод возвращает данные по форме в том виде который будет отправляться на сервер
     *
     * @param formData исходные данные по форме
     * @return измененные данные по форме
     */
    private String getFormData(String formData) {
        try {
            JSONObject data = new JSONObject(formData);
            JSONArray resultData = data.getJSONArray("data");
            formUUID = data.getString("form");
            return "\"data\":" + resultData.toString();
        } catch (Exception exc) {
            return null;
        }
    }

    /**
     * Метод возвращает url для загрузки формы
     *
     * @return url
     */
    private String getUrl() {
        Logger.v("http://" + person.getShortHost() + "/Synergy/mobileFormPlayer.html" +
                "?asfDataId=" + dataUUID +
                "&login=" + person.getLogin() +
                "&password=" + person.getPassword() +
                "&userid=" + person.getUuid() +
                "&locale=" + MyApplication.getLang() +
                "&editable=" + editMode);
        return "http://" + person.getShortHost() + "/Synergy/mobileFormPlayer.html" +
                "?asfDataId=" + dataUUID +
                "&login=" + person.getLogin() +
                "&password=" + person.getPassword() +
                "&userid=" + person.getUuid() +
                "&locale=" + MyApplication.getLang() +
                "&editable=" + editMode;
    }

    /**
     * Метод используется для сохранения данных по форме
     *
     * @param formData данные по форме
     */
    private void saveForm(final String formData) {
        if (formUUID == null) {
            AppMsg.makeText(activity, getString(R.string.form_uuid_not_found), AppMsg.STYLE_ALERT).show();
            return;
        }
        if (formData == null) {
            AppMsg.makeText(activity, getString(R.string.form_data_not_found), AppMsg.STYLE_ALERT).show();
        }
        if (dataUUID == null) {
            AppMsg.makeText(activity, getString(R.string.data_uuid_not_found), AppMsg.STYLE_ALERT).show();
            return;
        }
        new AsyncTask<Void, Void, String>() {
            @Override
            protected void onPreExecute() {
                progressBar.setVisibility(View.VISIBLE);
                super.onPreExecute();
            }

            @Override
            protected String doInBackground(Void... params) {
                return new SaveFormData(person, formUUID, dataUUID, formData).saveForm(false);
            }

            @Override
            protected void onPostExecute(String result) {
                progressBar.setVisibility(View.GONE);
                if (result == null) {
                    AppMsg.makeText(activity, getString(R.string.error_to_get_save_form_data), AppMsg.STYLE_ALERT).show();
                    return;
                }
                try {
                    ErrorCode errorCode = RestClient.getGson().fromJson(result, ErrorCode.class);
                    if (errorCode.getErrorCode().equals(Wrapper.NO_ERROR)) {
                        webView.loadUrl("javascript:forgetChanges();");
                        saveButton.setEnabled(false);
                        AppMsg.makeText(activity, getString(R.string.data_saved), AppMsg.STYLE_INFO).show();
                    } else {
                        AppMsg.makeText(activity, errorCode.getErrorMessage(), AppMsg.STYLE_ALERT).show();
                    }
                    if (saveType == BACK_PRESSED || saveType == FINISH_BUTTON) {
                        finishActivity();
                    }
                } catch (Exception exc) {
                    AppMsg.makeText(activity, getString(R.string.error_to_get_save_form_data), AppMsg.STYLE_ALERT).show();
                }
                super.onPostExecute(result);
            }
        }.execute();
    }

    @Override
    public void onBackPressed() {
        if (webView == null) {
            super.onBackPressed();
        } else {
            saveType = BACK_PRESSED;
            webView.loadUrl("javascript:hasChanges();");
        }
    }

    /**
     * Получение информации о форме
     */
    private void getFormInfo() {
        webView.loadUrl("javascript:getFormName();");
    }


    /**
     * Метод вызывается при вызове js функции для валидации формы
     *
     * @param data данные с js скрипта
     */
    private void isValidHandler(String data) {
        if (Boolean.parseBoolean(data)) {
            if (saveType == CHANGE_MODE) {
                editMode = false;
                setEditable(false);
            } else {
                webView.loadUrl("javascript:hasChanges();");
            }
        } else {
            AppMsg.makeText(activity, getString(R.string.mandatory_fields), AppMsg.STYLE_ALERT).show();
        }
    }

    /**
     * Метод вызывается при вызове js функции для получения имя формы
     *
     * @param data данные с js скрипта
     */
    private void getFormNameHandler(String data) {
        AlertDialog.Builder alert = new AlertDialog.Builder(activity);
        alert.setTitle(R.string.information);
        alert.setMessage(data);
        alert.setPositiveButton(R.string.ok, null);
        alert.show();
    }

    /**
     * Метод вызывается при вызове js функции для получения информации о том имеется ли мобильное представление у формы
     *
     * @param data данные с js скрипта
     */
    private void hasMobileDefHandler(String data) {
        hasMobileDef = Boolean.parseBoolean(data);
        webView.loadUrl("javascript:getPrintableFormats();");
    }

    /**
     * Метод вызывается при вызове js функции для получения доступных форматов печатного представления
     *
     * @param data данные с js скрипта
     */
    private void getPrintableFormatsHandler(String data) {
        List<String> formats = new ArrayList<>();
        if (data.contains("pdf")) {
            formats.add("pdf");
        }
        if (data.contains("odt")) {
            formats.add("odt");
        }
        if (data.contains("docx")) {
            formats.add("docx");
        }
        if (data.contains("doc")) {
            formats.add("doc");
        }
        showMenuDialog(formats);
    }

    /**
     * Метод вызывается при вызове js функции для получения данных по форме
     *
     * @param data данные с js скрипта
     */
    private void getAsfDataHandler(String data) {
        saveForm(getFormData(data));
    }

    /**
     * Метод вызывается при вызове js функции для получения информации о том имеются ли несохраненные изменения в форме
     *
     * @param data true/false
     */
    private void hasChangesHandler(String data) {
        if (saveType == SAVE_BUTTON) {
            if (Boolean.parseBoolean(data)) {
                webView.loadUrl("javascript:getAsfData();");
            }
        } else if (saveType == BACK_PRESSED) {
            if (Boolean.parseBoolean(data)) {
                AlertDialog.Builder dialog = new AlertDialog.Builder(activity);
                dialog.setTitle(getString(R.string.warning));
                dialog.setMessage(R.string.form_has_changed);
                dialog.setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        webView.loadUrl("javascript:getAsfData();");
                    }
                });
                dialog.setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        finish();
                    }
                });
                dialog.setNeutralButton(getString(R.string.cancel), null);
                dialog.show();
            } else {
                finish();
            }
        } else if (saveType == FINISH_BUTTON) {
            if (Boolean.parseBoolean(data)) {
                webView.loadUrl("javascript:getAsfData();");
            } else {
                finishActivity();
            }
        }
    }
}
