/*
    Парус 8 - Панели мониторинга - Редактор панелей
    Редактор панели
*/

//---------------------
//Подключение библиотек
//---------------------

import React, { useState, useContext, useEffect } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Responsive, WidthProvider } from "react-grid-layout"; //Адаптивный макет
import { Box, Grid, Menu, MenuItem, Popover } from "@mui/material"; //Интерфейсные элементы
import { MessagingCtx } from "../../context/messaging"; //Контекст сообщений
import { APP_BAR_HEIGHT } from "../../components/p8p_app_workspace"; //Рабочая область приложения
import { P8PEditorToolBar } from "../../components/editors/p8p_editor_toolbar"; //Панель инструментов редактора
import { LayoutItem } from "./layout_item"; //Элемент макета
import { ComponentView } from "./component_view"; //Представление компонента панели
import { ComponentEditor } from "./component_editor"; //Редактор свойств компонента панели
import { COMPONENTS } from "./components/components"; //Описание доступных компонентов
import { usePanelDesc, useWindowResize } from "./components/components_hooks"; //Вспомогательные хуки
import { toolbarImportRenderer } from "./layouts"; //Дополнительная разметка и вёрстка клиентских элементов
import "react-grid-layout/css/styles.css"; //Стили для адаптивного макета
import "react-resizable/css/styles.css"; //Стили для адаптивного макета
import "./panels_editor.css"; //Стили редактора панелей
import { APP_STYLES } from "../../../app.styles"; //Типовые стили
import { PanelPropsEditor } from "./panel_props_editor"; //Редактор глобальных свойств панели
import { PanelVariableList } from "./panel_variable_list"; //Список переменных панели

//---------
//Константы
//---------

//Стили
const STYLES = {
    CONTAINER: { display: "flex", overflow: "auto", ...APP_STYLES.SCROLL },
    GRID_CONTAINER: { height: `calc(100vh - ${APP_BAR_HEIGHT})` },
    GRID_ITEM_INSPECTOR: { backgroundColor: "#e9ecef", height: "100%", overflow: "auto", ...APP_STYLES.SCROLL },
    POPOVER_CONTAINER: { [`& .MuiPaper-root`]: { maxHeight: "60%", width: "300px", ...APP_STYLES.SCROLL } }
};

//-----------
//Тело модуля
//-----------

//Обёрдка для динамического макета
const ResponsiveGridLayout = WidthProvider(Responsive);

//Редактор панели
const PanelEditor = ({
    panel,
    panelName,
    editMode,
    isPanelChanged,
    isPanelVariablesOpened,
    onPanelChanged,
    onPanelClose,
    onEditModeToggle,
    onPanelsManagerOpen,
    onPanelVariablesClose
}) => {
    //Собственное состояние - ID перемещаемого макета
    //Требуется для разделения onDrag и onClick на макете
    const [draggingID, setDraggingID] = useState("");

    //Состояние описания и функций панели
    const [
        panelDesc,
        isInit,
        addComponent,
        deleteComponent,
        breakpointChange,
        layoutsChange,
        changeValueProviders,
        changeComponentSettings,
        changePanelSettings,
        savePanelDesc,
        importPanelDesc,
        exportPanelDesc
    ] = usePanelDesc(panel);

    //Собственное состояние - признак инициализации макетов
    const [isLayoutsInit, setIsLayoutsInit] = useState(true);
    //Собственное состояние - редактирование компонента
    const [editComponent, setEditComponent] = useState(null);
    //Собственное состояние - элемент открывающий список
    const [addMenuAnchorEl, setAddMenuAnchorEl] = useState(null);
    //Собственное состояние - высота строки ResponsiveGridLayout
    const [rowHeight] = useWindowResize();

    //Подключение к контексту сообщений
    const { showMsgWarn } = useContext(MessagingCtx);

    //При перемещении макета
    const handleOnDrag = layout => {
        //Если ID перемещаемого макета не установлен - устанавливаем
        if (!draggingID) setDraggingID(layout.i);
    };

    //При завершении перемещения макета
    const handleOnDragStop = () => setDraggingID("");

    //Открытие редактора настроек компонента
    const handleComponentSettingsEditorOpen = id => setEditComponent(id);

    //Закрытие реактора настроек компонента
    const handleComponentSettingsEditorClose = () => setEditComponent(null);

    //При нажатии на настройки компонента
    const handleComponentSettingsClick = id => (editComponent === id ? handleComponentSettingsEditorClose() : handleComponentSettingsEditorOpen(id));

    //Открытие/сокрытие меню добавления
    const handleAddMenuToggle = target => setAddMenuAnchorEl(target instanceof Element ? target : null);

    //При изменении размера холста
    const handleBreakpointChange = breakpoint => breakpointChange(breakpoint);

    //При изменении состояния макета
    const handleLayoutChange = (currentLayout, layouts) => {
        //Изменяем только в случае, если панель выбрана, проиницализирована и это режим редактирования
        if (panel && isInit) {
            //ResponsiveGridLayout реагирует на каждое изменение, при первой загрузке с сервера не требуется перезагрузка макетов
            if (isLayoutsInit) {
                setIsLayoutsInit(false);
            } else {
                //Изменяем состояние макета
                layoutsChange(layouts);
                //Указываем, что панель изменилась (только при редактировании)
                onPanelChanged(editMode ? true : false);
            }
        }
    };

    //Добвление компонента в макет
    const handlePanelComponentAdd = component => {
        //Добавляем компонент и его макет
        addComponent(component);
        //Указываем, что панель изменилась
        onPanelChanged(true);
    };

    //Удаление компонента из макета
    const handlePanelComponentDelete = id => {
        //Удаляем компонент
        deleteComponent(id);
        //Если удаляемый компонент редактируется - закрываем редактор
        editComponent === id && handleComponentSettingsEditorClose();
        //Указываем, что панель изменилась
        onPanelChanged(true);
    };

    //При нажатии на кнопку добавления
    const handleAddClick = e => handleAddMenuToggle(e.currentTarget);

    //При выборе элемента меню добавления
    const handleAddMenuItemClick = component => {
        handleAddMenuToggle();
        handlePanelComponentAdd(component);
    };

    //При изменении значений в компоненте
    const handleComponentValuesChange = values => {
        //Изменяем значения проводника
        changeValueProviders(values);
        //Указываем, что панель изменилась (только при редактировании)
        onPanelChanged(editMode ? true : false);
    };

    //При изменении настроек компонента
    const handleComponentSettingsChange = ({ id = null, settings = {}, closeEditor = false } = {}) => {
        if (id && panelDesc.components[id]) {
            //Изменяем настройки компонента
            changeComponentSettings(id, settings, () => {
                if (closeEditor === true) handleComponentSettingsEditorClose();
                //Указываем, что панель изменилась
                onPanelChanged(true);
            });
        }
    };

    //При изменении настроек панели
    const handlePanelSettingsChange = ({ valueProviders }) => {
        //Изменяем настройки панели
        changePanelSettings(valueProviders);
        //Указываем, что панель изменилась
        onPanelChanged(true);
    };

    //При удалении компоненета
    const handleComponentDeleteClick = id => handlePanelComponentDelete(id);

    //Закрытие панели
    const handlePanelClose = () => onPanelClose();

    //При запуске панели
    const handlePanelPlay = () => onEditModeToggle();

    //При сохранении описания панели
    const handlePanelDescSave = () => savePanelDesc(isChanged => onPanelChanged(isChanged));

    //При импорте настройки панели из файла
    const handleImportPanel = (fileData, callback) => importPanelDesc(fileData, callback);

    //При выгрузке настройки панели в файл
    const handleExportPanel = () => {
        //Если панель выбрана и есть изменения
        if (panel && isPanelChanged)
            showMsgWarn(`Панель содержит несохраненные изменения. Выгрузить панель в текущем состоянии?`, () => exportPanelDesc(panelName));
        else exportPanelDesc(panelName);
    };

    //Текущие значения панели
    const values = Object.keys(panelDesc.valueProviders).reduce((res, key) => ({ ...res, ...{ [key]: panelDesc.valueProviders[key].value } }), {});

    //Меню добавления
    const addMenu = (
        <Menu anchorEl={addMenuAnchorEl} open={Boolean(addMenuAnchorEl)} onClose={handleAddMenuToggle}>
            {COMPONENTS.map((comp, i) => (
                <MenuItem key={i} onClick={() => handleAddMenuItemClick(comp)}>
                    {comp.name}
                </MenuItem>
            ))}
        </Menu>
    );

    //Панель инструментов
    const toolBar = (
        <P8PEditorToolBar
            items={[
                { icon: "file_open", title: "Менеджер панелей", onClick: onPanelsManagerOpen },
                { icon: "save", title: "Сохранить", onClick: handlePanelDescSave, disabled: !isPanelChanged },
                { icon: "close", title: "Закрыть панель", onClick: handlePanelClose, disabled: !panel },
                { icon: "play_arrow", title: "Запустить", onClick: handlePanelPlay, disabled: !panel },
                { icon: "add", title: "Добавить элемент", onClick: handleAddClick, disabled: !panel },
                {
                    icon: "file_upload",
                    title: "Загрузить из файла",
                    onClick: fileData => {
                        handleImportPanel(fileData, isLoaded => onPanelChanged(isLoaded));
                    },
                    customRenderer: toolbarImportRenderer,
                    disabled: !panel
                },
                { icon: "file_download", title: "Выгрузить в файл", onClick: handleExportPanel, disabled: !panel },
                { icon: "settings", title: "Параметры панели", onClick: handleComponentSettingsEditorClose, disabled: !panel }
            ]}
        />
    );

    //При изменении панели
    useEffect(() => {
        //Первое изменение состояния макета - инициализация
        setIsLayoutsInit(true);
        //Очищаем редактируемый компонент
        setEditComponent(null);
    }, [panel]);

    //Генерация содержимого
    return (
        <Box sx={STYLES.CONTAINER}>
            {addMenu}
            <Grid container sx={STYLES.GRID_CONTAINER} columns={25}>
                <Grid item xs={editMode ? 20 : 25}>
                    <ResponsiveGridLayout
                        autoSize={true}
                        rowHeight={rowHeight}
                        className={"layout"}
                        layouts={panelDesc.layouts}
                        breakpoints={{ lg: 1200 }}
                        cols={{ lg: 12 }}
                        onBreakpointChange={handleBreakpointChange}
                        onLayoutChange={handleLayoutChange}
                        useCSSTransforms={true}
                        compactType={"vertical"}
                        isDraggable={editMode}
                        isResizable={editMode}
                        onDrag={(layouts, oldItem) => handleOnDrag(oldItem)}
                        onDragStop={handleOnDragStop}
                    >
                        {panelDesc.layouts[panelDesc.breakpoint].map(item => (
                            <LayoutItem
                                key={item.i}
                                onSettingsClick={handleComponentSettingsClick}
                                onDeleteClick={handleComponentDeleteClick}
                                item={item}
                                editMode={editMode}
                                selected={editMode && editComponent === item.i}
                            >
                                <ComponentView
                                    id={item.i}
                                    isDragging={item.i === draggingID}
                                    path={panelDesc.components[item.i]?.path}
                                    settings={panelDesc.components[item.i]?.settings}
                                    values={values}
                                    onValuesChange={handleComponentValuesChange}
                                />
                            </LayoutItem>
                        ))}
                    </ResponsiveGridLayout>
                </Grid>
                {editMode && (
                    <Grid item xs={5} sx={STYLES.GRID_ITEM_INSPECTOR}>
                        {toolBar}
                        {panel ? (
                            editComponent ? (
                                <ComponentEditor
                                    id={editComponent}
                                    path={panelDesc.components[editComponent].path}
                                    settings={panelDesc.components[editComponent].settings}
                                    valueProviders={panelDesc.valueProviders}
                                    onSettingsChange={handleComponentSettingsChange}
                                />
                            ) : (
                                <PanelPropsEditor
                                    valueProviders={panelDesc.valueProviders}
                                    onSettingsChange={handlePanelSettingsChange}
                                    onDependencyClick={handleComponentSettingsClick}
                                />
                            )
                        ) : null}
                    </Grid>
                )}
                <Popover
                    id={"variables-popover"}
                    sx={STYLES.POPOVER_CONTAINER}
                    open={!editMode && isPanelVariablesOpened}
                    marginThreshold={10}
                    onClose={onPanelVariablesClose}
                    anchorReference={"anchorPosition"}
                    anchorPosition={{ top: 60, left: window.innerWidth }}
                    transitionDuration={{ appear: 0, enter: 200, exit: 200 }}
                >
                    <PanelVariableList valueProviders={panelDesc.valueProviders} onValuesChange={handleComponentValuesChange} />
                </Popover>
            </Grid>
        </Box>
    );
};

//Контроль свойств компонента - Редактор панели
PanelEditor.propTypes = {
    panel: PropTypes.number.isRequired,
    panelName: PropTypes.string.isRequired,
    editMode: PropTypes.bool.isRequired,
    isPanelChanged: PropTypes.bool.isRequired,
    isPanelVariablesOpened: PropTypes.bool.isRequired,
    onPanelChanged: PropTypes.func.isRequired,
    onPanelClose: PropTypes.func.isRequired,
    onEditModeToggle: PropTypes.func.isRequired,
    onPanelsManagerOpen: PropTypes.func.isRequired,
    onPanelVariablesClose: PropTypes.func.isRequired
};

//----------------
//Интерфейс модуля
//----------------

export { PanelEditor };
