<template>
  <f-modal :showModal="true" :fullscreen="true" :cardPadding="false" :fullWidth="true" :showButtons="false"
    :title="modalTitle" :modalBackground="platformConfig ? platformConfig.BACKGROUND_COLOR : null"
    :cardTextScrollable="false" @closeModal="$emit('closeModal')">
    <template v-slot:toolbarItems>
      <!-- Componente de intervalo de refresco -->
      <f-intervalComponent v-if="selectedPlatform" :intervalSeconds="intervalTime" @intervalFinished="refreshData"
        minimal paddingRight centerComponent :activeColor="fieldeasDocksOptions.isToolbarDark ? '#fff' : '#000'"
        ref="intervalComponent" />
    </template>
    <!-- Spinner async -->
    <f-spinnerAsync :showSpinner="showFetchSpinner" :text="spinnerText" :successful="spinnerSuccesful"
      :error="spinnerError" left bottom />
    <div ref="tools" class="platformImage" style="position: absolute; height: calc(100% - 64px); width: 100%">
      <!-- Botón para recargar los datos -->
      <v-tooltip left>
        <template v-slot:activator="{ on }">
          <button v-on="on" type="button" class="gm-control-active absoluteButton" :style="`top: 0px; right: 0px`"
            @click="refreshData" :disabled="showFetchSpinner">
            <v-icon small color="#000">fas fa-redo-alt</v-icon>
          </button>
        </template>
        <span>{{ $t("reload") }}</span>
      </v-tooltip>

      <!-- Botón para resetear zoom -->
      <v-tooltip left>
        <template v-slot:activator="{ on }">
          <button v-on="on" type="button" class="gm-control-active absoluteButton" :style="`top: 60px; right: 0px`"
            @click="resetZoom">
            <v-icon small color="#000">fas fa-compass</v-icon>
          </button>
        </template>
        <span>{{ $t("center_map") }}</span>
      </v-tooltip>

      <v-tooltip left>
        <template v-slot:activator="{ on }">
          <button v-on="on" type="button" :class="{
            'gm-control-active': true,
            absoluteButton: true,
            absoluteButtonActive: showArrivals,
          }" :style="`top: 120px; right: 0px`" @click="showArrivals = !showArrivals">
            <v-icon small :color="showArrivals ? '#fff' : '#000'">fas fa-truck</v-icon>
          </button>
        </template>
        <span>{{ $t("next_arrivals") }}</span>
      </v-tooltip>

      <v-card v-if="showArrivals" ref="arrivals" :class="{
        arrivals: true,
      }" :style="`width: ${screenSize.arrivalsWidth}px`">
        <v-card-title>
          <span class="mr-10">
            {{ $t("next_arrivals") }}
          </span>
        </v-card-title>
        <v-card-text style="padding: 0px !important">
          <vueper-slides progress autoplay :arrows="false" :duration="15000" lazy :touchable="false" pauseOnTouch
            :fixed-height="`${screenSize.arrivalsHeight - 64}px`" ref="vueperSlides">
            <vueper-slide v-for="page in arrivalsPages" :key="`page_arrivals_${page}`">
              <template v-slot:content>
                <v-list>
                  <v-list-item v-for="(arrival, index) in arrivals.slice(
                    (page - 1) * arrivalsPerPage,
                    page * arrivalsPerPage
                  )" :key="`arrival_${arrival.ID_PARADA}_${index}`" class="px-0">
                    <v-list-item-avatar>
                      <v-icon v-if="arrival.TIPO_RETRASO" :color="arrival.TIPO_RETRASO.COLOR"
                        style="position: absolute">fas fa-circle</v-icon>
                      <v-icon color="#000" style="position: absolute">fal fa-circle</v-icon>
                    </v-list-item-avatar>
                    <v-list-item-content>
                      <v-list-item-title>
                        {{ arrival.CODIGO_RUTA }} - {{ arrival.MATRICULA_1 }}
                      </v-list-item-title>
                      <v-list-item-subtitle class="mt-1">
                        <div v-if="arrival.FH_ESTIMADA_LLEGADA">
                          <v-icon color="primary" small class="pr-2">fal fa-clock</v-icon>
                          <span>{{
                            getDateToShow(arrival.FH_ESTIMADA_LLEGADA)
                          }}</span>
                        </div>
                        <div>
                          <v-icon color="primary" small class="pr-2">fal fa-calendar</v-icon>
                          <span class="plannedDate">{{
                            getDateToShow(arrival.FH_PLANIFICADA_CERCANA)
                          }}</span>
                        </div>
                      </v-list-item-subtitle>
                    </v-list-item-content>
                  </v-list-item>
                </v-list>
              </template>
            </vueper-slide>
          </vueper-slides>
        </v-card-text>
      </v-card>

      <!-- Menú de ayuda -->
      <v-card scrollable :class="{
        legendMenu: true,
        closed: !showStatusLegend,
      }" v-on="!showStatusLegend
            ? {
              click: () => (showStatusLegend = true),
            }
            : {}
          " :style="`bottom: ${showPlatformButton ? 60 : 10}px`">
        <v-card-title class="text-center" :style="!showStatusLegend ? `height: 45px !important` : ``">
          <template v-if="showStatusLegend">
            <span class="mr-10">
              {{ $t("status_legend") }}
            </span>
            <v-btn icon @click.stop="showStatusLegend = false" style="position: absolute; right: 4px">
              <v-icon small color="#000">fas fa-chevron-double-left</v-icon>
            </v-btn>
          </template>
          <template v-else>
            <v-icon small class="center" color="#000">fas fa-question</v-icon>
          </template>
        </v-card-title>
        <v-card-text :class="{
          legendMenuButton: true,
          closed: !showStatusLegend,
        }">
          <v-expansion-panels accordion multiple>
            <v-expansion-panel>
              <v-expansion-panel-header>
                <v-row no-gutters>
                  <v-col>{{ $t("docks_status") }}</v-col>
                </v-row>
              </v-expansion-panel-header>
              <v-expansion-panel-content>
                <v-list light class="overflow-y-auto" max-height="228px">
                  <template v-for="(item, i) in statusDocks">
                    <v-divider v-if="i > 0" :key="`dock_status_${i}d`"></v-divider>
                    <v-list-item :key="`dock_status_${i}`">
                      <v-list-item-action>
                        <v-chip disabled small :color="item.COLOR" :outlined="item.OUTLINED">
                        </v-chip>
                      </v-list-item-action>

                      <v-list-item-content>
                        <v-list-item-title>{{
                          $t(item.DESC)
                        }}</v-list-item-title>
                      </v-list-item-content>
                    </v-list-item>
                  </template>
                </v-list>
              </v-expansion-panel-content>
            </v-expansion-panel>
            <v-expansion-panel>
              <v-expansion-panel-header>
                <v-row no-gutters>
                  <v-col>{{ $t("vehicles_to_dock_status") }}</v-col>
                </v-row>
              </v-expansion-panel-header>
              <v-expansion-panel-content>
                <v-list light class="overflow-y-auto" max-height="228px">
                  <template v-for="(item, i) in statusVehiclesToDock">
                    <v-divider v-if="i > 0" :key="`vehicle_to_dock_status_${i}d`"></v-divider>
                    <v-list-item :key="`vehicle_to_dock_status_${i}`">
                      <v-list-item-action>
                        <v-chip disabled small :color="item.COLOR" :outlined="item.OUTLINED">
                        </v-chip>
                      </v-list-item-action>

                      <v-list-item-content>
                        <v-list-item-title>{{
                          $t(item.DESC)
                        }}</v-list-item-title>
                      </v-list-item-content>
                    </v-list-item>
                  </template>
                </v-list>
              </v-expansion-panel-content>
            </v-expansion-panel>
            <v-expansion-panel>
              <v-expansion-panel-header>
                <v-row no-gutters>
                  <v-col>{{ $t("estimations") }}</v-col>
                </v-row>
              </v-expansion-panel-header>
              <v-expansion-panel-content>
                <v-list light class="overflow-y-auto" max-height="228px">
                  <template v-for="(item, i) in estimationsConfig">
                    <v-divider v-if="i > 0" :key="`estimation_${i}d`"></v-divider>
                    <v-list-item :key="`estimation_${i}`">
                      <v-list-item-action>
                        <v-chip disabled small :color="item.COLOR" :outlined="item.OUTLINED">
                        </v-chip>
                      </v-list-item-action>

                      <v-list-item-content>
                        <v-list-item-title>{{
                          $t(item.TAG)
                        }}</v-list-item-title>
                      </v-list-item-content>
                    </v-list-item>
                  </template>
                </v-list>
              </v-expansion-panel-content>
            </v-expansion-panel>
          </v-expansion-panels>
        </v-card-text>
      </v-card>

      <!-- Botón para seleccionar la plataforma -->
      <v-card v-if="showPlatformButton" :class="{
        platformMenu: true,
        closed: !showPlatformMenu,
      }" v-on="!showPlatformMenu
            ? {
              click: () => (showPlatformMenu = true),
            }
            : {}
          ">
        <v-card-title :class="{
          'text-center': true,
          primary: !showPlatformMenu,
        }" :style="!showPlatformMenu ? `height: 45px !important` : ``">
          <template v-if="showPlatformMenu">
            <span class="mr-10">
              {{ $t("selectPlatform") }}
            </span>
            <v-btn icon @click.stop="showPlatformMenu = false" style="position: absolute; right: 4px">
              <v-icon small color="#000">fas fa-chevron-double-left</v-icon>
            </v-btn>
          </template>
          <template v-else>
            <v-icon small :class="`center ${fieldeasDocksOptions.textClass}`">fas fa-store-alt</v-icon>
          </template>
        </v-card-title>
        <v-card-text :class="{
          platformMenuButton: true,
          closed: !showPlatformMenu,
        }">
          <v-row class="pa-0 ma-0">
            <v-col cols="12" no-gutters class="pa-0">
              <v-select v-model="platformSelected" item-value="CODIGO_INTERNO" :item-text="(platform) =>
                  `${platform.CODIGO_INTERNO.replaceAll('%', '')} (${$t(
                    platform.NOMBRE
                  )})`
                " :items="availablePlatforms" :label="`${$t('platform')}*`" required></v-select>
            </v-col>
            <v-col cols="12" no-gutters class="pa-0 text-center">
              <v-btn :class="`primary ${fieldeasDocksOptions.textClass}`" @click="assignPlatform">{{ $t("ok") }}</v-btn>
            </v-col>
          </v-row>
        </v-card-text>
      </v-card>
    </div>

    <panZoom v-if="selectedPlatform && platformConfig" ref="panZoom" :options="{ minZoom: 0.5, maxZoom: 2 }"
      v-resize="onResize">
      <div v-if="selectedPlatform && platformConfig">
        <!-- Imagen de la plataforma -->
        <img v-if="platformConfig && platformConfig.file_content" :src="platformConfig.file_content" ref="platformImage"
          @load="
            onResize(true);
            appLoaded = true;
          " />

        <!-- Muelles -->
        <DockComponent v-for="dock in docksToShow" :key="`dock_${dock.ID_MUELLE}`" :dock="dock" />

        <!-- Vehículos en muelles -->
        <Vehicle v-for="vehicle in vehiclesDocked" :key="`vehicle_docked_${vehicle.ID_PARADA}`" :vehicle="vehicle" />

        <!-- Parkings -->
        <Parking v-for="(parking, index) in parkings" :key="`parking_${index}`" :parking="parking" />

        <!-- Llegadas -->
        <!-- <Arrivals
          v-for="(arrival, index) in arrivals"
          :key="`arrivals_${index}`"
          :arrivalsParking="arrival"
        /> -->
      </div>
    </panZoom>

    <v-container v-else fill-height fluid>
      <v-row align="center" justify="center">
        <v-col class="text-center text-h3">
          {{ $t("selectPlatformWarning") }}
        </v-col>
      </v-row>
    </v-container>
  </f-modal>
</template>

<script>
// Componentes necesarios
import DockComponent from "../dockView/dock/index.vue";
import Vehicle from "../dockView/vehicle/index.vue";
import Parking from "../dockView/parking/index.vue";
import modal from "../../../modal/index.vue";
import intervalComponent from "../../../intervalComponent.vue";
import spinner_async from "../spinnerAsync.vue";
import { VueperSlides, VueperSlide } from "vueperslides";
import "vueperslides/dist/vueperslides.css";

import "@interactjs/auto-start";
import "@interactjs/actions/drag";
import "@interactjs/actions/resize";
import "@interactjs/modifiers";
import interact from "@interactjs/interact";

import global from "../../../mixins/Global";

export default {
  name: "DocksView",
  components: {
    DockComponent,
    Vehicle,
    Parking,
    "f-modal": modal,
    "f-intervalComponent": intervalComponent,
    "f-spinnerAsync": spinner_async,
    "f-spinnerAsync": spinner_async,
    VueperSlides,
    VueperSlide,
  },
  props: {
    // Intervalo (en segundos) del intervalo de refresco
    intervalTime: {
      default: 59,
    },
  },
  mixins: [global],
  async created() {
    await this.initPlatform();

    await this.initDragResize();
  },
  data() {
    return {
      interactable: null,
      // Tamaño de la pantalla
      screenSize: {
        w: null,
        h: null,
        initialZoom: 0,
        initialPosition: {
          x: 0,
          y: 0,
        },
        arrivalsHeight: 788,
        arrivalsWidth: 375,
        arrivalsX: 0,
        arrivalsY: 0,
      },
      // Plataforma seleccionada
      platformSelected: null,
      // Mostrar la selección de plataforma
      showPlatformMenu: true,
      // Mostrar el botón de la plataforma
      showPlatformButton: true,
      // Mostrar el spinner async
      showFetchSpinner: false,
      // Consulta de datos correcta
      spinnerSuccesful: false,
      // Texto del spinner
      spinnerText: null,
      // Consulta de datos erroneas
      spinnerError: false,
      // Cola de mensajes del spinner
      waitingSpinners: [],
      // Mostrar el menú de la leyenda de estados
      showStatusLegend: false,
      // Mostrar las próximas llegadas
      showArrivals: false,
      // Estados de los muelles
      statusDocks: [
        {
          COLOR: "success",
          DESC: "occupied",
        },
        {
          COLOR: "error",
          DESC: "discrepancy",
        },
        {
          COLOR: "ababab",
          DESC: "inactive",
        },
        {
          COLOR: "#000",
          OUTLINED: true,
          DESC: "free",
        },
      ],
      // Estados de los vehículos del parking
      statusVehiclesToDock: [
        {
          COLOR: "#000",
          OUTLINED: true,
          DESC: "notAssigned",
        },
        {
          COLOR: "warning",
          DESC: "dock_pending",
        },
        {
          COLOR: "error",
          DESC: "waiting",
        },
      ],
      // Variable para saber si la aplicación ya se ha cargado
      appLoaded: false,
    };
  },
  beforeDestroy() {
    this.resetData();
  },
  watch: {
    showArrivals(newVal) {
      if (newVal) {
        setTimeout(() => {
          this.setEvents();
        }, 250);
      }
    },
  },
  computed: {
    fieldeasDocksOptions() {
      return this.$store.getters.getFieldeasDocksOptions;
    },
    arrivalsPages() {
      if (this.arrivals.length == 0 && this.arrivalsPerPage == 0) {
        return 0;
      } else {
        return Math.ceil(this.arrivals.length / this.arrivalsPerPage);
      }
    },
    arrivalsPerPage() {
      return Math.floor((this.screenSize.arrivalsHeight - 64) / 66);
    },
    // Título de la ventana modal
    modalTitle() {
      const platform = this.selectedPlatform
        ? this.availablePlatforms.find(
            (platform) => platform.CODIGO_INTERNO == this.selectedPlatform
          )
        : null;

      return platform
        ? `${platform.CODIGO_INTERNO} (${platform.NOMBRE})`
        : this.$t("selectPlatform");
    },
    /**
     * Obtener las plataformas disponibles
     */
    availablePlatforms() {
      return this.$store.getters.getDocksViewAvailablePlatforms;
    },
    /**
     * Obtener la plataforma seleccionada
     */
    selectedPlatform() {
      return this.$store.getters.getDocksViewPlatform;
    },
    /**
     * Obtener los muelles de la plataforma seleccionada
     */
    docks() {
      return this.$store.getters.getDocksViewDocks;
    },
    /**
     * Configuración de la plataforma
     */
    platformConfig() {
      return this.$store.getters.getDocksViewConfig;
    },
    /**
     * Obtener los estados de las paradas
     */
    stopsStatus() {
      return this.$store.getters.getDocksStopsStatus;
    },
    /**
     * Obtener los estados de las estimaciones
     */
    estimationsConfig() {
      return [
        {
          COLOR: "#000",
          TAG: "no_estimacion",
          OUTLINED: true,
        },
        ...this.$store.getters.getEstimationsConfig,
      ];
    },
    /**
     * Muelles que se ven en la pantalla
     */
    docksToShow() {
      return this.$store.getters.getDocksViewConfigDocks
        ? this.$store.getters.getDocksViewConfigDocks
            .filter((config) => config.TIPO == "MUELLE")
            .map((config) => {
              // Las coordenadas X e Y se calculan como la posición + tamaño de borde (2) + la mitad de la altura, para que quede centrado
              const coordinateX =
                parseInt(config.POSICION_X) + 2 - parseInt(config.HEIGHT) / 2;
              const coordinateY =
                parseInt(config.POSICION_Y) +
                2 -
                (parseInt(config.HEIGHT) + 2) / 2;

              // Las posiciones y tamaños reales se calculan haciendo una regla de 3 entre el tamaño teórico y el real de la imagen
              return {
                ...config,
                positionY:
                  config.ORIENTACION == "VERTICAL"
                    ? config.POSICION_Y
                    : coordinateY,
                positionX:
                  config.ORIENTACION == "VERTICAL"
                    ? coordinateX
                    : config.POSICION_X,
              };
            })
        : [];
    },
    /**
     * Obtener la configuración de los vehículos
     */
    vehicleConfig() {
      return this.$store.getters.getDocksViewConfigDocks
        ? this.$store.getters.getDocksViewConfigDocks.find(
            (dockConfig) => dockConfig.TIPO == "VEHICULO"
          )
        : null;
    },
    /**
     * Vehículos que se encuentran en los muelles
     */
    vehiclesDocked() {
      return this.$store.getters.getVehiclesDocked.map((vehicle) => {
        const dockConfig = this.docksToShow.find(
          (dock) => dock.ID_MUELLE == vehicle.ID_MUELLE
        );
        return {
          ...vehicle,
          realHeight: this.vehicleConfig
            ? dockConfig && dockConfig.EXTRA_WIDE == 1
              ? dockConfig.HEIGHT
              : this.vehicleConfig.HEIGHT
            : null,
          dockConfig,
        };
      });
    },
    /**
     * Configuración de los parkings
     */
    parkings() {
      const totalVehicles = this.$store.getters.getVehiclesToDock.length;
      // let vehiclesIndex = 0;

      return this.$store.getters.getDocksViewConfigDocks
        ? this.$store.getters.getDocksViewConfigDocks
            .filter((config) => config.TIPO == "PARKING")
            .map((config) => {
              // La configuración de los vehículos se guarda en EXTRA_CONFIG
              const vehicleConfig =
                config.EXTRA_CONFIG && config.EXTRA_CONFIG.length > 0
                  ? JSON.parse(config.EXTRA_CONFIG)
                  : null;

              // La altura de un vehículo es la altura configurada en el parking + el margen
              const vehicleHeight = vehicleConfig
                ? vehicleConfig.VEHICLE_HEIGHT + 16
                : 0;

              // El ancho de un vehículo es el ancho configurado en el parking + ancho del muelle + el margen
              const vehicleWidth = vehicleConfig
                ? vehicleConfig.DOCK_WIDTH
                  ? vehicleConfig.VEHICLE_WIDTH + vehicleConfig.DOCK_WIDTH + 24
                  : vehicleConfig.VEHICLE_WIDTH + 16
                : 0;

              // Se calculan las filas y columnas disponibles para repartir los vehículos
              const availableRows = vehicleConfig
                ? Math.floor(parseInt(config.HEIGHT) / parseInt(vehicleHeight))
                : 0;

              const availableColumns = vehicleConfig
                ? Math.floor(parseInt(config.WIDTH) / parseInt(vehicleWidth))
                : 0;

              // const startIndex = vehiclesIndex;

              const vehiclesIndex =
                availableRows * availableColumns > totalVehicles
                  ? totalVehicles
                  : availableRows * availableColumns;

              return {
                ...config,
                availableRows,
                availableColumns,
                vehiclesToDock: this.$store.getters.getVehiclesToDock,
                vehiclesPerPage: availableRows * availableColumns,
                // vehiclesToDock: [
                //   ...this.$store.getters.getVehiclesToDock,
                // ].slice(startIndex, vehiclesIndex),
                vehiclesDimensions: {
                  height: vehicleConfig.VEHICLE_HEIGHT,
                  width: vehicleConfig.DOCK_WIDTH
                    ? vehicleConfig.VEHICLE_WIDTH + vehicleConfig.DOCK_WIDTH + 8
                    : vehicleConfig.VEHICLE_WIDTH,
                  vehicleWidth: vehicleConfig.VEHICLE_WIDTH,
                  dockWidth: vehicleConfig.DOCK_WIDTH,
                  showDock: !!vehicleConfig.DOCK_WIDTH,
                },
              };
            })
        : [];
    },
    /**
     * Próximas llegadas de vehículos
     */
    arrivals() {
      return this.$store.getters.getVehiclesToArrive;
    },
  },
  methods: {
    /**
     * Se inicia el drag&resize
     */
    async initDragResize() {
      // Si no existe registro en la localStorage para showArrivals, se pregunta al usuario
      // Si existe, se actualizan los valores por defecto
      if (this.getFromLocalStorage("showArrivals") == null) {
        let resDialog = await this.$confirm({
          title: this.$t("next_arrivals"),
          message: this.$t("next_arrivals_msg"),
        });

        this.saveArrivalsParams({ showArrivals: resDialog });

        this.initArrivalsParams();

        this.showArrivals = resDialog;
      } else {
        if (
          +this.getFromLocalStorage("screenWidth") != window.screen.width ||
          +this.getFromLocalStorage("screenHeight") != window.screen.height
        ) {
          this.initArrivalsParams();
        } else {
          this.screenSize.arrivalsHeight =
            +this.getFromLocalStorage("arrivalsHeight");
          this.screenSize.arrivalsWidth =
            +this.getFromLocalStorage("arrivalsWidth");
          this.screenSize.arrivalsX = +this.getFromLocalStorage("arrivalsX");
          this.screenSize.arrivalsY = +this.getFromLocalStorage("arrivalsY");
        }
        this.showArrivals = this.getFromLocalStorage("showArrivals") == 'true';
      }
    },
    initArrivalsParams() {
      const savedHeight = +this.getFromLocalStorage("arrivalsHeight");
      const savedWidth = +this.getFromLocalStorage("arrivalsWidth");
      const savedX = +this.getFromLocalStorage("arrivalsX");
      const savedY = +this.getFromLocalStorage("arrivalsY");

      this.screenSize.arrivalsHeight = savedHeight || 788;
      this.screenSize.arrivalsWidth = savedWidth || 375;
      this.screenSize.arrivalsX =
        savedX || window.screen.width - this.screenSize.arrivalsWidth - 62;
      this.screenSize.arrivalsY = savedY || 0;
    },
    saveArrivalsParams({
      showArrivals,
      arrivalsHeight,
      arrivalsWidth,
      arrivalsX,
      arrivalsY,
    }) {
      if (showArrivals != null)
        this.saveToLocalStorage("showArrivals", showArrivals);
      if (arrivalsHeight != null)
        this.saveToLocalStorage("arrivalsHeight", arrivalsHeight);
      if (arrivalsWidth != null)
        this.saveToLocalStorage("arrivalsWidth", arrivalsWidth);
      if (arrivalsX != null) this.saveToLocalStorage("arrivalsX", arrivalsX);
      if (arrivalsY != null) this.saveToLocalStorage("arrivalsY", arrivalsY);
    },
    dragMoveListener(event) {
      var target = event.target;
      // keep the dragged position in the data-x/data-y attributes
      var x = (parseFloat(target.getAttribute("data-x")) || 0) + event.dx;
      var y = (parseFloat(target.getAttribute("data-y")) || 0) + event.dy;

      // translate the element
      target.style.webkitTransform = target.style.transform =
        "translate(" + x + "px, " + y + "px)";

      // update the posiion attributes
      target.setAttribute("data-x", x);
      target.setAttribute("data-y", y);

      this.screenSize.arrivalsX = x;
      this.screenSize.arrivalsY = y;

      this.saveArrivalsParams({
        arrivalsX: x,
        arrivalsY: y,
      });
    },
    resizeListener(event) {
      var target = event.target;
      var x = parseFloat(target.getAttribute("data-x")) || 0;
      var y = parseFloat(target.getAttribute("data-y")) || 0;

      // update the element's style
      target.style.width = event.rect.width + "px";
      target.style.height = event.rect.height + "px";

      // translate when resizing from top or left edges
      x += event.deltaRect.left;
      y += event.deltaRect.top;

      target.style.webkitTransform = target.style.transform =
        "translate(" + x + "px," + y + "px)";

      target.setAttribute("data-x", x);
      target.setAttribute("data-y", y);

      this.screenSize.arrivalsWidth = event.rect.width;
      this.screenSize.arrivalsHeight = event.rect.height;
      this.screenSize.arrivalsX = x;
      this.screenSize.arrivalsY = y;

      this.saveArrivalsParams({
        arrivalsWidth: event.rect.width,
        arrivalsHeight: event.rect.height,
        arrivalsX: x,
        arrivalsY: y,
      });
    },
    setupInteractable(item) {
      item.resizable({
        // resize from all edges and corners
        edges: {
          left: true,
          right: true,
          bottom: true,
          top: true,
        },

        listeners: {
          move: this.resizeListener,
        },
        modifiers: [
          // keep the edges inside the parent
          interact.modifiers.restrictEdges({
            outer: "parent",
          }),

          // minimum size
          interact.modifiers.restrictSize({
            min: {
              width: 100,
              height: 50,
            },
          }),
        ],
      });

      item.draggable({
        allowFrom: ".v-card__title",
        autoScroll: true,
        listeners: {
          move: this.dragMoveListener,
        },
        modifiers: [
          interact.modifiers.restrictRect({
            restriction: "parent",
            endOnly: true,
          }),
        ],
      });
    },
    /**
     * Eventos para cambiar el ancho de la v-card de llegadas haciendo drag
     */
    setEvents() {
      const el = this.$refs.arrivals.$el;
      el.style.width = this.screenSize.arrivalsWidth;
      el.style.height = this.screenSize.arrivalsHeight;

      // translate the element
      el.style.webkitTransform = el.style.transform =
        "translate(" +
        this.screenSize.arrivalsX +
        "px, " +
        this.screenSize.arrivalsY +
        "px)";

      // update the posiion attributes
      el.setAttribute("data-x", this.screenSize.arrivalsX);
      el.setAttribute("data-y", this.screenSize.arrivalsY);

      this.setupInteractable(interact(el));
    },
    /**
     * Calcula y modifica la escala y zoom necesarios para que la imagen de la plataforma complete la pantalla
     */
    async onResize(ignoreCondition = false) {
      if (!ignoreCondition && !this.appLoaded) {
        return;
      }

      while (
        !this.$refs.panZoom ||
        !this.$refs.platformImage ||
        !this.$refs.tools
      ) {
        new Promise((res) => {
          setTimeout(() => {
            res();
          }, 100);
        });
      }

      this.screenSize.w = this.$refs.tools.clientWidth;

      this.screenSize.h = this.$refs.tools.clientHeight;

      const element = this.$refs.platformImage;

      if (element.width == 0 || element.height == 0) return;

      this.screenSize.initialZoom =
        this.screenSize.w / element.width < this.screenSize.h / element.height
          ? this.screenSize.w / element.width
          : this.screenSize.h / element.height;

      this.screenSize.initialPosition = {
        x:
          (this.screenSize.w - element.width * this.screenSize.initialZoom) / 2,
        y:
          (this.screenSize.h - element.height * this.screenSize.initialZoom) /
          2,
      };

      this.resetZoom();
    },
    /**
     * Inicia la plataforma del usuario
     */
    async initPlatform() {
      // Consulta las plataformas
      await this.$store.dispatch(
        "FETCH_DOCKSVIEW_PLATFORMS",
        this.$store.getters.getDockViewUser
      );

      if (this.selectedPlatform) {
        this.platformSelected = this.selectedPlatform;
      }

      // Si el usuario solo tiene una plataforma, se selecciona automaticamente
      if (this.availablePlatforms.length == 1)
        this.platformSelected = this.availablePlatforms[0].CODIGO_INTERNO;

      // Si ya se ha seleccionado la plataforma, no se le mostrará al usuario la modal de selección
      const askPlatform = this.platformSelected ? false : true;

      // Si el usuario tiene más de 1 plataforma, se mostrará el botón de selección de plataforma
      this.showPlatformButton = this.availablePlatforms.length !== 1;

      // Si es necesario pedirle al usuario la plataforma, se mostrará la modal
      this.showPlatformMenu = askPlatform;

      // Si se ha seleccionado la plataforma, se cambia la plataforma actual en la store
      if (this.platformSelected) {
        this.$store.commit(
          "_setCurrentPlatformDockView",
          this.platformSelected
        );
        this.$store.dispatch("FETCH_PLATFORM_DATA").then(() => {
          this.refreshData();
        });
      }
    },
    /**
     * Resetea el zoom del plano
     */
    resetZoom() {
      if (!this.$refs.panZoom) return;

      this.$refs.panZoom.$panZoomInstance.zoomAbs(
        0,
        0,
        this.screenSize.initialZoom
      );
      this.$refs.panZoom.$panZoomInstance.moveTo(
        this.screenSize.initialPosition.x,
        this.screenSize.initialPosition.y
      );
    },
    /**
     * Cambiar la plataforma seleccionada
     */
    async assignPlatform() {
      this.resetData();

      if (!this.platformSelected) {
        this.$store.commit("SET_SNACK", {
          open: true,
          color: "red darken-1",
          text: this.$t("alertSelectPlatform"),
        });
        return;
      }

      this.$store.commit("_setCurrentPlatformDockView", this.platformSelected);

      this.$store.dispatch("FETCH_PLATFORM_DATA");

      this.refreshData();

      this.showPlatformMenu = false;
    },
    /**
     * Muestra el spinner del mapa con el
     * texto indicado
     * @param {String} spinnerText
     */
    showSpinner(spinnerText) {
      if (!this.showFetchSpinner) {
        this.showFetchSpinner = true;
        this.spinnerText = this.$t(spinnerText);
      } else {
        this.waitingSpinners.push(spinnerText);
      }
    },
    /**
     * Oculta el spinner
     * @param {String} spinnerText
     */
    hideSpinner(spinnerText, isSuccesful) {
      setTimeout(() => {
        if (this.spinnerText == this.$t(spinnerText)) {
          this.spinnerSuccesful = isSuccesful;
          this.spinnerError = !isSuccesful;
          setTimeout(() => {
            this.showFetchSpinner = false;
            this.spinnerSuccesful = false;
            this.spinnerError = false;
            if (this.waitingSpinners.length) {
              this.showSpinner(this.waitingSpinners.pop());
            }
          }, 250);
        } else {
          this.waitingSpinners.pop();
        }
      }, 250);
    },
    /**
     * Consulta de los datos que se ven en la vista
     */
    async refreshData() {
      if (!this.$refs.intervalComponent) {
        this.$nextTick(() => {
          // Si el intervalo está activo, se reinician
          if (
            this.$refs.intervalComponent &&
            this.$refs.intervalComponent.intervalActive
          ) {
            this.$refs.intervalComponent.clearIntervals();
            this.$refs.intervalComponent.initIntervals();
          }
        });
      } else {
        // Si el intervalo está activo, se reinician
        if (
          this.$refs.intervalComponent &&
          this.$refs.intervalComponent.intervalActive
        ) {
          this.$refs.intervalComponent.clearIntervals();
          this.$refs.intervalComponent.initIntervals();
        }
      }

      this.showSpinner("map_reload");

      try {
        await this.$store.dispatch("FETCH_DOCKS_DATA");
        this.hideSpinner("map_reload", true);
      } catch (error) {
        console.error(error);
        this.hideSpinner("map_reload", false);
      } finally {
        // Si no se dispone de configuración de plataforma se para el contador
        if (
          this.$refs.intervalComponent &&
          this.$refs.intervalComponent.intervalActive &&
          !this.platformConfig
        ) {
          this.$refs.intervalComponent.clearIntervals();
        }
      }
    },
    /**
     * Reset de los datos cuando se cambia de plataforma y cuando se destruye la vista
     */
    resetData() {
      this.$store.commit("_setDocksDockView", []);
      this.$store.commit("_setVehiclesDocked", []);
      this.$store.commit("_setVehiclesToDock", []);
      this.$store.commit("_setPlatformConfig", null);
      this.$store.commit("_setDocksConfig", null);
      this.$store.commit("_setDocksStatus", []);
    },
  },
};
</script>

<style lang="scss" scoped>
.platformImage {
  display: block;
  margin: auto;
  overflow: auto;
  position: relative;
}

.absoluteButton {
  background: none rgb(255, 255, 255);
  border: 0px;
  margin: 10px;
  padding: 0px;
  position: absolute;
  cursor: pointer;
  user-select: none;
  border-radius: 4px;
  height: 40px;
  width: 40px;
  box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 4px -1px;
  overflow: hidden;
  right: 0px;
  z-index: 1;
  color: #000;
}

.absoluteButtonActive {
  background: var(--v-primary-base);
}

::v-deep .legendMenu {
  transition: max-width 0.3s ease, max-height 0.3s ease;
  position: absolute;
  z-index: 99;
  right: 10px;
  bottom: 60px;
  max-width: 500px;
  max-height: 500px;
  white-space: nowrap;
  overflow: hidden;
  &.closed {
    max-width: 40px !important;
    max-height: 40px !important;
    right: 10px;
    bottom: 60px;
  }
}

::v-deep .legendMenuButton {
  transition: max-height 0.3s ease;
  overflow-y: auto;
  max-height: 436px;
  &.closed {
    max-height: 0px !important;
    padding: 0;
    overflow: hidden !important;
  }
}

::v-deep .platformMenu {
  transition: max-width 0.3s ease, max-height 0.3s ease;
  position: absolute;
  z-index: 999;
  right: 10px;
  bottom: 10px;
  max-width: 313px;
  max-height: 500px;
  white-space: nowrap;
  overflow: hidden;
  &.closed {
    max-width: 40px !important;
    max-height: 40px !important;
    right: 10px;
    bottom: 10px;
  }
}

::v-deep .platformMenuButton {
  transition: max-height 0.3s ease;
  overflow-y: auto;
  max-height: 436px;
  &.closed {
    max-height: 0px !important;
    padding: 0;
    overflow: hidden !important;
  }
}

::v-deep .arrivals {
  position: absolute;
  z-index: 999;
  white-space: nowrap;
}

.center {
  position: relative !important;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.plannedDate {
  display: inline-block;
  margin-left: 2px;
}

::v-deep .vueperslides__progress {
  color: var(--v-primary-base) !important;
}

::v-deep .vueperslides__bullet .default {
  background-color: rgba(0, 0, 0, 0.3);
  border: none;
  box-shadow: none;
  transition: 0.3s;
  width: 12px;
  height: 12px;
}

::v-deep .vueperslides__bullet--active .default {
  background-color: var(--v-primary-base);
}

::v-deep .vueperslides__bullets {
  overflow: hidden;
}
</style>