/*****************************************************************************
 * Open MCT, Copyright (c) 2014-2024, United States Government
 * as represented by the Administrator of the National Aeronautics and Space
 * Administration. All rights reserved.
 *
 * Open MCT is licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 * Open MCT includes source code licensed under additional open source
 * licenses. See the Open Source Licenses file (LICENSES.md) included with
 * this source code distribution or the Licensing information page available
 * at runtime from the About dialog for additional information.
 *****************************************************************************/

import _ from 'lodash';

const CONTEXT_ACTION = 'contextAction';
const CONTEXT_ACTIONS = Object.freeze({
  ADD_ELEMENT: 'addElement',
  REMOVE_ITEM: 'removeItem',
  DUPLICATE_ITEM: 'duplicateItem',
  ORDER_ITEM: 'orderItem',
  SWITCH_VIEW_TYPE: 'switchViewType',
  MERGE_MULTIPLE_TELEMETRY_VIEWS: 'mergeMultipleTelemetryViews',
  MERGE_MULTIPLE_OVERLAY_PLOTS: 'mergeMultipleOverlayPlots',
  TOGGLE_GRID: 'toggleGrid'
});

const DIALOG_FORM = {
  text: {
    title: 'Text Element Properties',
    sections: [
      {
        rows: [
          {
            key: 'text',
            control: 'textfield',
            name: 'Text',
            required: true,
            cssClass: 'l-input-lg'
          }
        ]
      }
    ]
  },
  image: {
    title: 'Image Properties',
    sections: [
      {
        rows: [
          {
            key: 'url',
            control: 'textfield',
            name: 'Image URL',
            cssClass: 'l-input-lg',
            required: true
          }
        ]
      }
    ]
  }
};
const VIEW_TYPES = {
  'telemetry-view': {
    value: 'telemetry-view',
    name: 'Alphanumeric',
    class: 'icon-alphanumeric'
  },
  'telemetry.plot.overlay': {
    value: 'telemetry.plot.overlay',
    name: 'Overlay Plot',
    class: 'icon-plot-overlay'
  },
  'telemetry.plot.stacked': {
    value: 'telemetry.plot.stacked',
    name: 'Stacked Plot',
    class: 'icon-plot-stacked'
  },
  table: {
    value: 'table',
    name: 'Table',
    class: 'icon-tabular-scrolling'
  }
};
const APPLICABLE_VIEWS = {
  'telemetry-view': [
    VIEW_TYPES['telemetry.plot.overlay'],
    VIEW_TYPES['telemetry.plot.stacked'],
    VIEW_TYPES.table
  ],
  'telemetry.plot.overlay': [
    VIEW_TYPES['telemetry.plot.stacked'],
    VIEW_TYPES.table,
    VIEW_TYPES['telemetry-view']
  ],
  'telemetry.plot.stacked': [
    VIEW_TYPES['telemetry.plot.overlay'],
    VIEW_TYPES.table,
    VIEW_TYPES['telemetry-view']
  ],
  table: [
    VIEW_TYPES['telemetry.plot.overlay'],
    VIEW_TYPES['telemetry.plot.stacked'],
    VIEW_TYPES['telemetry-view']
  ],
  'telemetry-view-multi': [
    VIEW_TYPES['telemetry.plot.overlay'],
    VIEW_TYPES['telemetry.plot.stacked'],
    VIEW_TYPES.table
  ],
  'telemetry.plot.overlay-multi': [VIEW_TYPES['telemetry.plot.stacked']]
};

export default class DisplayLayoutToolbar {
  #openmct;

  constructor(openmct) {
    this.#openmct = openmct;
    this.name = 'Display Layout Toolbar';
    this.key = 'layout';
    this.description = 'A toolbar for objects inside a display layout.';
  }

  forSelection(selection) {
    if (!selection || selection.length === 0) {
      return false;
    }

    let selectionPath = selection[0];
    let selectedObject = selectionPath[0];
    let selectedParent = selectionPath[1];

    // Apply the layout toolbar if the selected object is inside a layout, or the main layout is selected.
    return (
      (selectedParent &&
        selectedParent.context.item &&
        selectedParent.context.item.type === 'layout') ||
      (selectedObject.context.item && selectedObject.context.item.type === 'layout')
    );
  }

  #getPath(selectionPath) {
    return `configuration.items[${selectionPath[0].context.index}]`;
  }

  #getAllOfType(selection, specificType) {
    return selection.filter((selectionPath) => {
      let type = selectionPath[0].context.layoutItem.type;

      return type === specificType;
    });
  }

  #getAllTypes(selection) {
    return selection.filter((selectionPath) => {
      let type = selectionPath[0].context.layoutItem.type;

      return (
        type === 'text-view' ||
        type === 'telemetry-view' ||
        type === 'box-view' ||
        type === 'ellipse-view' ||
        type === 'image-view' ||
        type === 'line-view' ||
        type === 'subobject-view'
      );
    });
  }

  #getAddButton(selection, selectionPath) {
    if (selection.length === 1) {
      selectionPath = selectionPath || selection[0];
      const domainObject = selectionPath[0].context.item;
      const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);

      return {
        control: 'menu',
        domainObject,
        method: (option) => {
          let name = option.name.toLowerCase();
          let form = DIALOG_FORM[name];
          if (form) {
            this.#showForm(form, name, selection);
          } else {
            this.#openmct.objectViews.emit(
              `${CONTEXT_ACTION}:${keyString}`,
              CONTEXT_ACTIONS.ADD_ELEMENT,
              name,
              selection
            );
          }
        },
        key: 'add',
        icon: 'icon-plus',
        label: 'Add Drawing Object',
        options: [
          {
            name: 'Box',
            class: 'icon-box-round-corners'
          },
          {
            name: 'Ellipse',
            class: 'icon-circle'
          },
          {
            name: 'Line',
            class: 'icon-line-horz'
          },
          {
            name: 'Text',
            class: 'icon-font'
          },
          {
            name: 'Image',
            class: 'icon-image'
          }
        ]
      };
    }
  }

  #getToggleFrameButton(selectedParent, selection) {
    return {
      control: 'toggle-button',
      domainObject: selectedParent,
      applicableSelectedItems: selection.filter(
        (selectionPath) => selectionPath[0].context.layoutItem.type === 'subobject-view'
      ),
      property: (selectionPath) => {
        return this.#getPath(selectionPath) + '.hasFrame';
      },
      options: [
        {
          value: false,
          icon: 'icon-frame-hide',
          title: 'Frame visible',
          label: 'Hide frame'
        },
        {
          value: true,
          icon: 'icon-frame-show',
          title: 'Frame hidden',
          label: 'Show frame'
        }
      ]
    };
  }

  #getRemoveButton(selectedParent, selectionPath, selection) {
    const domainObject = selectedParent;
    const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
    return {
      control: 'button',
      domainObject,
      icon: 'icon-trash',
      title: 'Delete the selected object',
      method: () => {
        let prompt = this.#openmct.overlays.dialog({
          iconClass: 'alert',
          message: `Warning! This action will remove this item from the Display Layout. Do you want to continue?`,
          buttons: [
            {
              label: 'Ok',
              emphasis: 'true',
              callback: () => {
                this.#openmct.objectViews.emit(
                  `${CONTEXT_ACTION}:${keyString}`,
                  CONTEXT_ACTIONS.REMOVE_ITEM,
                  this.#getAllTypes(selection),
                  selection
                );
                prompt.dismiss();
              }
            },
            {
              label: 'Cancel',
              callback: function () {
                prompt.dismiss();
              }
            }
          ]
        });
      }
    };
  }

  #getStackOrder(selectedParent, selectionPath, selectedObjects) {
    const domainObject = selectedParent;
    const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
    return {
      control: 'menu',
      domainObject,
      icon: 'icon-layers',
      title: 'Move the selected object above or below other objects',
      options: [
        {
          name: 'Move to Top',
          value: 'top',
          class: 'icon-arrow-double-up'
        },
        {
          name: 'Move Up',
          value: 'up',
          class: 'icon-arrow-up'
        },
        {
          name: 'Move Down',
          value: 'down',
          class: 'icon-arrow-down'
        },
        {
          name: 'Move to Bottom',
          value: 'bottom',
          class: 'icon-arrow-double-down'
        }
      ],
      method: (option) => {
        this.#openmct.objectViews.emit(
          `${CONTEXT_ACTION}:${keyString}`,
          CONTEXT_ACTIONS.ORDER_ITEM,
          option.value,
          this.#getAllTypes(selectedObjects)
        );
      }
    };
  }

  #getXInput(selectedParent, selection) {
    if (selection.length === 1) {
      return {
        control: 'input',
        type: 'number',
        domainObject: selectedParent,
        applicableSelectedItems: this.#getAllTypes(selection),
        property: (selectionPath) => {
          return this.#getPath(selectionPath) + '.x';
        },
        label: 'X:',
        title: 'X position'
      };
    }
  }

  #getYInput(selectedParent, selection) {
    if (selection.length === 1) {
      return {
        control: 'input',
        type: 'number',
        domainObject: selectedParent,
        applicableSelectedItems: this.#getAllTypes(selection),
        property: (selectionPath) => {
          return this.#getPath(selectionPath) + '.y';
        },
        label: 'Y:',
        title: 'Y position'
      };
    }
  }

  #getWidthInput(selectedParent, selection) {
    if (selection.length === 1) {
      return {
        control: 'input',
        type: 'number',
        domainObject: selectedParent,
        applicableSelectedItems: this.#getAllTypes(selection),
        property: (selectionPath) => {
          return this.#getPath(selectionPath) + '.width';
        },
        label: 'W:',
        title: 'Resize object width'
      };
    }
  }

  #getHeightInput(selectedParent, selection) {
    if (selection.length === 1) {
      return {
        control: 'input',
        type: 'number',
        domainObject: selectedParent,
        applicableSelectedItems: this.#getAllTypes(selection),
        property: (selectionPath) => {
          return this.#getPath(selectionPath) + '.height';
        },
        label: 'H:',
        title: 'Resize object height'
      };
    }
  }

  #getX2Input(selectedParent, selection) {
    if (selection.length === 1) {
      return {
        control: 'input',
        type: 'number',
        domainObject: selectedParent,
        applicableSelectedItems: selection.filter((selectionPath) => {
          return selectionPath[0].context.layoutItem.type === 'line-view';
        }),
        property: (selectionPath) => {
          return this.#getPath(selectionPath) + '.x2';
        },
        label: 'X2:',
        title: 'X2 position'
      };
    }
  }

  #getY2Input(selectedParent, selection) {
    if (selection.length === 1) {
      return {
        control: 'input',
        type: 'number',
        domainObject: selectedParent,
        applicableSelectedItems: selection.filter((selectionPath) => {
          return selectionPath[0].context.layoutItem.type === 'line-view';
        }),
        property: (selectionPath) => {
          return this.#getPath(selectionPath) + '.y2';
        },
        label: 'Y2:',
        title: 'Y2 position'
      };
    }
  }

  #getTextButton(selectedParent, selection) {
    return {
      control: 'button',
      domainObject: selectedParent,
      applicableSelectedItems: selection.filter((selectionPath) => {
        return selectionPath[0].context.layoutItem.type === 'text-view';
      }),
      property: (selectionPath) => {
        return this.#getPath(selectionPath);
      },
      icon: 'icon-pencil',
      title: 'Edit text properties',
      label: 'Edit text',
      dialog: DIALOG_FORM.text
    };
  }

  #getTelemetryValueMenu(selectionPath, selection) {
    if (selection.length === 1) {
      return {
        control: 'select-menu',
        domainObject: selectionPath[1].context.item,
        applicableSelectedItems: selection.filter((path) => {
          return path[0].context.layoutItem.type === 'telemetry-view';
        }),
        property: (path) => {
          return this.#getPath(path) + '.value';
        },
        title: 'Set value',
        options: this.#openmct.telemetry
          .getMetadata(selectionPath[0].context.item)
          .values()
          .map((value) => {
            return {
              name: value.name,
              value: value.key
            };
          })
      };
    }
  }

  #getDisplayModeMenu(selectedParent, selection) {
    if (selection.length === 1) {
      return {
        control: 'select-menu',
        domainObject: selectedParent,
        applicableSelectedItems: selection.filter((selectionPath) => {
          return selectionPath[0].context.layoutItem.type === 'telemetry-view';
        }),
        property: (selectionPath) => {
          return this.#getPath(selectionPath) + '.displayMode';
        },
        title: 'Set display mode',
        options: [
          {
            name: 'Label + Value',
            value: 'all'
          },
          {
            name: 'Label only',
            value: 'label'
          },
          {
            name: 'Value only',
            value: 'value'
          }
        ]
      };
    }
  }

  #getDuplicateButton(selectedParent, selectionPath, selection) {
    const domainObject = selectedParent;
    const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
    return {
      control: 'button',
      domainObject,
      icon: 'icon-duplicate',
      title: 'Duplicate the selected object',
      method: () => {
        this.#openmct.objectViews.emit(
          `${CONTEXT_ACTION}:${keyString}`,
          CONTEXT_ACTIONS.DUPLICATE_ITEM,
          selection
        );
      }
    };
  }

  #getPropertyFromPath(object, path) {
    let splitPath = path.split('.');
    let property = Object.assign({}, object);

    while (splitPath.length && property) {
      property = property[splitPath.shift()];
    }

    return property;
  }

  #areAllViews(type, path, selection) {
    let allTelemetry = true;

    selection.forEach((selectedItem) => {
      let selectedItemContext = selectedItem[0].context;

      if (this.#getPropertyFromPath(selectedItemContext, path) !== type) {
        allTelemetry = false;
      }
    });

    return allTelemetry;
  }

  #getToggleUnitsButton(selectedParent, selection) {
    let applicableItems = this.#getAllOfType(selection, 'telemetry-view');
    applicableItems = this.#unitsOnly(applicableItems);
    if (!applicableItems.length) {
      return;
    }

    return {
      control: 'toggle-button',
      domainObject: selectedParent,
      applicableSelectedItems: applicableItems,
      property: (selectionPath) => {
        return this.#getPath(selectionPath) + '.showUnits';
      },
      options: [
        {
          value: true,
          icon: 'icon-eye-open',
          title: 'Show units',
          label: 'Show units'
        },
        {
          value: false,
          icon: 'icon-eye-disabled',
          title: 'Hide units',
          label: 'Hide units'
        }
      ]
    };
  }

  #unitsOnly(items) {
    let results = items.filter((item) => {
      let currentItem = item[0];
      let metadata = this.#openmct.telemetry.getMetadata(currentItem.context.item);
      if (!metadata) {
        return false;
      }

      let hasUnits = metadata.valueMetadatas.filter((metadatum) => metadatum.unit).length;

      return hasUnits > 0;
    });

    return results;
  }

  #getViewSwitcherMenu(selectedParent, selectionPath, selection) {
    if (selection.length === 1) {
      let selectedItemContext = selectionPath[0].context;
      let selectedItemType = selectedItemContext.item.type;

      if (selectedItemContext.layoutItem.type === 'telemetry-view') {
        selectedItemType = 'telemetry-view';
      }

      let viewOptions = APPLICABLE_VIEWS[selectedItemType];

      if (viewOptions) {
        const domainObject = selectedParent;
        const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
        return {
          control: 'menu',
          domainObject,
          icon: 'icon-object',
          title: 'Switch the way this telemetry is displayed',
          label: 'View type',
          options: viewOptions,
          method: (option) => {
            this.#openmct.objectViews.emit(
              `${CONTEXT_ACTION}:${keyString}`,
              CONTEXT_ACTIONS.SWITCH_VIEW_TYPE,
              selectedItemContext,
              option.value,
              selection
            );
          }
        };
      }
    } else if (selection.length > 1) {
      const domainObject = selectedParent;
      const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
      if (this.#areAllViews('telemetry-view', 'layoutItem.type', selection)) {
        return {
          control: 'menu',
          domainObject,
          icon: 'icon-object',
          title: 'Merge into a telemetry table or plot',
          label: 'View type',
          options: APPLICABLE_VIEWS['telemetry-view-multi'],
          method: (option) => {
            this.#openmct.objectViews.emit(
              `${CONTEXT_ACTION}:${keyString}`,
              CONTEXT_ACTIONS.MERGE_MULTIPLE_TELEMETRY_VIEWS,
              selection,
              option.value
            );
          }
        };
      } else if (this.#areAllViews('telemetry.plot.overlay', 'item.type', selection)) {
        return {
          control: 'menu',
          domainObject,
          icon: 'icon-object',
          title: 'Merge into a stacked plot',
          options: APPLICABLE_VIEWS['telemetry.plot.overlay-multi'],
          method: (option) => {
            this.#openmct.objectViews.emit(
              `${CONTEXT_ACTION}:${keyString}`,
              CONTEXT_ACTIONS.MERGE_MULTIPLE_OVERLAY_PLOTS,
              selection,
              option.value
            );
          }
        };
      }
    }
  }

  #getToggleGridButton(selection, selectionPath) {
    const ICON_GRID_SHOW = 'icon-grid-on';
    const ICON_GRID_HIDE = 'icon-grid-off';

    let displayLayoutContext;

    if (selection.length === 1 && selectionPath === undefined) {
      displayLayoutContext = selection[0][0].context;
    } else {
      displayLayoutContext = selectionPath[1].context;
    }

    const domainObject = displayLayoutContext.item;
    const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);

    return {
      control: 'button',
      domainObject,
      icon: ICON_GRID_SHOW,
      method: () => {
        this.#openmct.objectViews.emit(
          `${CONTEXT_ACTION}:${keyString}`,
          CONTEXT_ACTIONS.TOGGLE_GRID
        );

        this.icon = this.icon === ICON_GRID_SHOW ? ICON_GRID_HIDE : ICON_GRID_SHOW;
      },
      secondary: true
    };
  }

  #getSeparator() {
    return {
      control: 'separator'
    };
  }

  #isMainLayoutSelected(selectionPath) {
    let selectedObject = selectionPath[0].context.item;

    return (
      selectedObject && selectedObject.type === 'layout' && !selectionPath[0].context.layoutItem
    );
  }

  #showForm(formStructure, name, selection) {
    const domainObject = selection[0][0].context.item;
    const keyString = this.#openmct.objects.makeKeyString(domainObject.identifier);
    this.#openmct.forms.showForm(formStructure).then((changes) => {
      this.#openmct.objectViews.emit(
        `${CONTEXT_ACTION}:${keyString}`,
        CONTEXT_ACTIONS.ADD_ELEMENT,
        name,
        changes,
        selection
      );
    });
  }

  toolbar(selectedObjects) {
    if (this.#isMainLayoutSelected(selectedObjects[0])) {
      return [this.#getToggleGridButton(selectedObjects), this.#getAddButton(selectedObjects)];
    }

    let toolbar = {
      'add-menu': [],
      text: [],
      url: [],
      viewSwitcher: [],
      'toggle-frame': [],
      'display-mode': [],
      'telemetry-value': [],
      style: [],
      'unit-toggle': [],
      position: [],
      duplicate: [],
      remove: [],
      'toggle-grid': []
    };

    selectedObjects.forEach((selectionPath) => {
      let selectedParent = selectionPath[1].context.item;
      let layoutItem = selectionPath[0].context.layoutItem;

      if (!layoutItem || selectedParent.locked) {
        return;
      }

      if (layoutItem.type === 'subobject-view') {
        if (toolbar['add-menu'].length === 0 && selectionPath[0].context.item.type === 'layout') {
          toolbar['add-menu'] = [this.#getAddButton(selectedObjects, selectionPath)];
        }

        if (toolbar['toggle-frame'].length === 0) {
          toolbar['toggle-frame'] = [this.#getToggleFrameButton(selectedParent, selectedObjects)];
        }

        if (toolbar.position.length === 0) {
          toolbar.position = [
            this.#getStackOrder(selectedParent, selectionPath, selectedObjects),
            this.#getSeparator(),
            this.#getXInput(selectedParent, selectedObjects),
            this.#getYInput(selectedParent, selectedObjects),
            this.#getHeightInput(selectedParent, selectedObjects),
            this.#getWidthInput(selectedParent, selectedObjects)
          ];
        }

        if (toolbar.remove.length === 0) {
          toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
        }

        if (toolbar.viewSwitcher.length === 0) {
          toolbar.viewSwitcher = [
            this.#getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)
          ];
        }
      } else if (layoutItem.type === 'telemetry-view') {
        if (toolbar['display-mode'].length === 0) {
          toolbar['display-mode'] = [this.#getDisplayModeMenu(selectedParent, selectedObjects)];
        }

        if (toolbar['telemetry-value'].length === 0) {
          toolbar['telemetry-value'] = [
            this.#getTelemetryValueMenu(selectionPath, selectedObjects)
          ];
        }

        if (toolbar['unit-toggle'].length === 0) {
          let toggleUnitsButton = this.#getToggleUnitsButton(selectedParent, selectedObjects);
          if (toggleUnitsButton) {
            toolbar['unit-toggle'] = [toggleUnitsButton];
          }
        }

        if (toolbar.position.length === 0) {
          toolbar.position = [
            this.#getStackOrder(selectedParent, selectionPath, selectedObjects),
            this.#getSeparator(),
            this.#getXInput(selectedParent, selectedObjects),
            this.#getYInput(selectedParent, selectedObjects),
            this.#getHeightInput(selectedParent, selectedObjects),
            this.#getWidthInput(selectedParent, selectedObjects)
          ];
        }

        if (toolbar.remove.length === 0) {
          toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
        }

        if (toolbar.viewSwitcher.length === 0) {
          toolbar.viewSwitcher = [
            this.#getViewSwitcherMenu(selectedParent, selectionPath, selectedObjects)
          ];
        }
      } else if (layoutItem.type === 'text-view') {
        if (toolbar.position.length === 0) {
          toolbar.position = [
            this.#getStackOrder(selectedParent, selectionPath, selectedObjects),
            this.#getSeparator(),
            this.#getXInput(selectedParent, selectedObjects),
            this.#getYInput(selectedParent, selectedObjects),
            this.#getHeightInput(selectedParent, selectedObjects),
            this.#getWidthInput(selectedParent, selectedObjects)
          ];
        }

        if (toolbar.text.length === 0) {
          toolbar.text = [this.#getTextButton(selectedParent, selectedObjects)];
        }

        if (toolbar.remove.length === 0) {
          toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
        }
      } else if (layoutItem.type === 'box-view' || layoutItem.type === 'ellipse-view') {
        if (toolbar.position.length === 0) {
          toolbar.position = [
            this.#getStackOrder(selectedParent, selectionPath, selectedObjects),
            this.#getSeparator(),
            this.#getXInput(selectedParent, selectedObjects),
            this.#getYInput(selectedParent, selectedObjects),
            this.#getHeightInput(selectedParent, selectedObjects),
            this.#getWidthInput(selectedParent, selectedObjects)
          ];
        }

        if (toolbar.remove.length === 0) {
          toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
        }
      } else if (layoutItem.type === 'image-view') {
        if (toolbar.position.length === 0) {
          toolbar.position = [
            this.#getStackOrder(selectedParent, selectionPath, selectedObjects),
            this.#getSeparator(),
            this.#getXInput(selectedParent, selectedObjects),
            this.#getYInput(selectedParent, selectedObjects),
            this.#getHeightInput(selectedParent, selectedObjects),
            this.#getWidthInput(selectedParent, selectedObjects)
          ];
        }

        if (toolbar.remove.length === 0) {
          toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
        }
      } else if (layoutItem.type === 'line-view') {
        if (toolbar.position.length === 0) {
          toolbar.position = [
            this.#getStackOrder(selectedParent, selectionPath),
            this.#getSeparator(),
            this.#getXInput(selectedParent, selectedObjects),
            this.#getYInput(selectedParent, selectedObjects),
            this.#getX2Input(selectedParent, selectedObjects),
            this.#getY2Input(selectedParent, selectedObjects)
          ];
        }

        if (toolbar.remove.length === 0) {
          toolbar.remove = [this.#getRemoveButton(selectedParent, selectionPath, selectedObjects)];
        }
      }

      if (toolbar.duplicate.length === 0) {
        toolbar.duplicate = [
          this.#getDuplicateButton(selectedParent, selectionPath, selectedObjects)
        ];
      }

      if (toolbar['toggle-grid'].length === 0) {
        toolbar['toggle-grid'] = [this.#getToggleGridButton(selectedObjects, selectionPath)];
      }
    });

    let toolbarArray = Object.values(toolbar);

    return _.flatten(
      toolbarArray.reduce((accumulator, group, index) => {
        group = group.filter((control) => control !== undefined);

        if (group.length > 0) {
          accumulator.push(group);

          if (index < toolbarArray.length - 1) {
            accumulator.push(this.#getSeparator());
          }
        }

        return accumulator;
      }, [])
    );
  }
}
