<template>
  <v-container fluid>
    <v-card>
      <v-card-title>
        <v-toolbar flat>
          <v-toolbar-title>Plan</v-toolbar-title>

          <v-menu v-if="items">
            <template v-slot:activator="{on, attrs}">
              <v-btn class="ml-5" small v-on="on" v-bind="attrs">
                <span class="d-none d-lg-inline">Download as…</span>
                <v-icon right small>mdi-cloud-download</v-icon>
              </v-btn>
            </template>

            <v-list>
              <v-list-item
                :href="`/api/project/${$route.params.project}/plan/?mime=text%2Fcsv&download`"
                title="Download as a comma-separated values file."
              >CSV</v-list-item>
              <v-list-item
                :href="`/api/project/${$route.params.project}/plan/?mime=application%2Fgeo%2Bjson&download`"
                title="Download as a QGIS-compatible GeoJSON file"
              >GeoJSON</v-list-item>
              <v-list-item
                :href="`/api/project/${$route.params.project}/plan/?mime=application%2Fjson&download`"
                title="Download as a generic JSON file"
              >JSON</v-list-item>
              <v-list-item
                :href="`/api/project/${$route.params.project}/plan/?mime=text%2Fhtml&download`"
                title="Download as an HTML formatted file"
              >HTML</v-list-item>
              <v-list-item
                :href="`/api/project/${$route.params.project}/plan/?mime=application%2Fpdf&download`"
                title="Download as a Portable Document File"
              >PDF</v-list-item>
            </v-list>
          </v-menu>

          <v-spacer></v-spacer>
          <v-text-field
            v-model="filter"
            append-icon="mdi-magnify"
            label="Filter"
            single-line
            clearable
            hint="Filter by sequence, line, first or last shotpoints, remarks or start/end time"
            ></v-text-field>
        </v-toolbar>
      </v-card-title>
      <v-card-text>

        <v-menu v-if="writeaccess()"
          v-model="contextMenuShow"
          :position-x="contextMenuX"
          :position-y="contextMenuY"
          absolute
          offset-y
        >
          <v-list dense v-if="contextMenuItem">
            <v-list-item @click="deletePlannedSequence">
              <v-list-item-icon><v-icon>mdi-delete</v-icon></v-list-item-icon>
              <v-list-item-title class="warning--text">Delete planned sequence</v-list-item-title>
            </v-list-item>
          </v-list>
        </v-menu>

        <v-card class="mb-5" flat>
          <v-card-title class="text-overline">
            Comments
            <template v-if="writeaccess()">
              <v-btn v-if="!editRemarks"
                class="ml-3"
                small
                icon
                title="Edit comments"
                @click="editRemarks=true"
              >
                <v-icon small>mdi-square-edit-outline</v-icon>
              </v-btn>

              <v-btn v-else
                class="ml-3"
                small
                icon
                title="Save comments"
                @click="saveRemarks"
              >
                <v-icon>mdi-content-save-edit-outline</v-icon>
              </v-btn>
            </template>
          </v-card-title>

          <v-card-text v-if="editRemarks">
            <v-textarea
              v-model="remarks"
              class="markdown"
              placeholder="Plan comments"
              dense
              auto-grow
              rows="1"
            ></v-textarea>
          </v-card-text>

          <v-card-text v-else v-html="$options.filters.markdown(remarks || '*(nil)*')"></v-card-text>

        </v-card>

        <v-data-table
        :headers="headers"
        :items="items"
        :items-per-page.sync="itemsPerPage"
        :server-items-length="sequenceCount"
        item-key="sequence"
        :search="filter"
        :loading="plannedSequencesLoading"
        fixed-header
        no-data-text="No planned lines. Add lines via the context menu from either the Lines or Sequences view."
        :item-class="(item) => (activeItem == item && !edit) ? 'blue accent-1 elevation-3' : ''"
        :footer-props="{showFirstLastPage: true}"
        @click:row="setActiveItem"
        @contextmenu:row="contextMenu"
        >

          <template v-slot:item.srss="{item}">
            <span style="white-space: nowrap;">
              <v-icon small :title="srssInfo(item)">{{srssIcon(item)}}</v-icon>
              /
              <v-icon small :title="wxInfo(item)" v-if="item.meta.wx">{{wxIcon(item)}}</v-icon>
            </span>
          </template>

          <template v-slot:item.sequence="{item, value}">
            <v-edit-dialog v-if="writeaccess()"
              large
              @open="editItem(item, 'sequence')"
              @save="edit = null"
              @cancel="edit.value = item.sequence; edit = null"
            >
              <span>{{ value }}</span>
              <template v-slot:input>
                <v-text-field v-if="edit"
                  type="number"
                  v-model.number="edit.value"
                  single-line
                >
                </v-text-field>
                <v-checkbox
                  v-model="shiftAll"
                  class="mt-0"
                  label="Shift all planned sequences"
                ></v-checkbox>
              </template>
            </v-edit-dialog>
            <span v-else>{{ value }}</span>
          </template>

          <template v-slot:item.name="{item, value}">
            <v-edit-dialog v-if="writeaccess()"
              large
              @open="editItem(item, 'name')"
              @save="edit = null"
              @cancel="edit.value = item.name; edit = null"
            >
              <span>{{ value }}</span>
              <template v-slot:input>
                <v-text-field v-if="edit"
                  v-model="edit.value"
                  single-line
                >
                </v-text-field>
              </template>
            </v-edit-dialog>
            <span v-else>{{ value }}</span>
          </template>

          <template v-slot:item.fsp="{item, value}">
            <v-edit-dialog v-if="writeaccess()"
              large
              @open="editItem(item, 'fsp')"
              @save="edit = null"
              @cancel="edit.value = item.fsp; edit = null"
            >
              <span>{{ value }}</span>
              <template v-slot:input>
                <v-text-field v-if="edit"
                  type="number"
                  v-model.number="edit.value"
                  single-line
                >
                </v-text-field>
              </template>
            </v-edit-dialog>
            <span v-else>{{ value }}</span>
          </template>

          <template v-slot:item.lsp="{item, value}">
            <v-edit-dialog v-if="writeaccess()"
              large
              @open="editItem(item, 'lsp')"
              @save="edit = null"
              @cancel="edit.value = item.lsp; edit = null"
            >
              <span>{{ value }}</span>
              <template v-slot:input>
                <v-text-field v-if="edit"
                  type="number"
                  v-model.number="edit.value"
                  single-line
                >
                </v-text-field>
              </template>
            </v-edit-dialog>
            <span v-else>{{ value }}</span>
          </template>

          <template v-slot:item.ts0="{item, value}">
            <v-edit-dialog v-if="writeaccess()"
              large
              @open="editItem(item, 'ts0', item.ts0.toISOString())"
              @save="edit = null"
              @cancel="edit.value = item.ts0; edit = null"
            >
              <span>{{ value.toISOString ? value.toISOString().slice(0, 16) : "" }}</span>
              <template v-slot:input>
                <v-text-field v-if="edit"
                  type="datetime-local"
                  v-model="edit.value"
                  single-line
                >
                </v-text-field>
              </template>
            </v-edit-dialog>
            <span v-else>{{ value.toISOString ? value.toISOString().slice(0, 16) : "" }}</span>
          </template>

          <template v-slot:item.ts1="{item, value}">
            <v-edit-dialog v-if="writeaccess()"
              large
              @open="editItem(item, 'ts1', item.ts1.toISOString())"
              @save="edit = null"
              @cancel="edit.value = item.ts1; edit = null"
            >
              <span>{{ value.toISOString ? value.toISOString().slice(0, 16) : "" }}</span>
              <template v-slot:input>
                <v-text-field v-if="edit"
                  type="datetime-local"
                  v-model="edit.value"
                  single-line
                >
                </v-text-field>
              </template>
            </v-edit-dialog>
            <span v-else>{{ value.toISOString ? value.toISOString().slice(0, 16) : "" }}</span>
          </template>

          <template v-slot:item.length="props">
            <span style="white-space:nowrap;">{{ Math.round(props.value) }} m</span>
          </template>

          <template v-slot:item.azimuth="props">
            <span style="white-space:nowrap;">{{ props.value.toFixed(2) }} °</span>
          </template>

          <template v-slot:item.remarks="{item}">
            <v-text-field v-if="writeaccess() && edit && edit.sequence == item.sequence && edit.key == 'remarks'"
              type="text"
              v-model="edit.value"
              prepend-icon="mdi-restore"
              append-outer-icon="mdi-content-save-edit-outline"
              clearable
              @click:prepend="edit.value = item.remarks; edit = null"
              @click:append-outer="edit = null"
            >
            </v-text-field>
            <div v-else>
              <span v-html="$options.filters.markdownInline(item.remarks)"></span>
              <v-btn v-if="edit === null && writeaccess()"
                icon
                small
                title="Edit"
                :disabled="plannedSequencesLoading"
                @click="editItem(item, 'remarks')"
              >
                <v-icon small>mdi-square-edit-outline</v-icon>
              </v-btn>
            </div>

          </template>

          <template v-slot:item.speed="{item}">
            <v-edit-dialog v-if="writeaccess()"
              large
              @open="editItem(item, 'speed', knots(item).toFixed(1))"
              @save="edit = null"
              @cancel="edit.value = undefined; edit = null"
            >
              <span style="white-space:nowrap;">{{ knots(item).toFixed(1) }} kt</span>
              <template v-slot:input>
                <v-text-field v-if="edit"
                  type="number"
                  min="0"
                  step="0.1"
                  v-model.number="edit.value"
                  single-line
                >
                </v-text-field>
              </template>
            </v-edit-dialog>
            <span v-else style="white-space:nowrap;">{{ knots(item).toFixed(1) }} kt</span>
          </template>

          <template v-slot:item.lag="{item}">
            <v-edit-dialog v-if="writeaccess()"
              large
              @open="editItem(item, 'lagAfter', Math.round(lagAfter(item)/(60*1000)))"
              @save="edit = null"
              @cancel="edit.value = undefined; edit = null"
            >
              <span>{{ Math.round(lagAfter(item) / (60*1000)) }} min</span>
              <template v-slot:input>
                <v-text-field v-if="edit"
                  type="number"
                  min="0"
                  v-model="edit.value"
                  single-line
                >
                </v-text-field>
              </template>
            </v-edit-dialog>
            <span v-else>{{ Math.round(lagAfter(item) / (60*1000)) }} min</span>
          </template>

        </v-data-table>

      </v-card-text>
    </v-card>
  </v-container>
</template>

<style lang="stylus" scoped>
</style>

<script>
import suncalc from 'suncalc';
import { mapActions, mapGetters } from 'vuex';
import AccessMixin from '@/mixins/access';

export default {
  name: "Plan",

  components: {
  },

  mixins: [
    AccessMixin
  ],

  data () {
    return {
      headers: [
        {
          value: "sequence",
          text: "Sequence"
        },
        {
          value: "srss",
          text: "SR/SS"
        },
        {
          value: "name",
          text: "Name"
        },
        {
          value: "line",
          text: "Line"
        },
        {
          value: "fsp",
          text: "FSP",
          align: "end"
        },
        {
          value: "lsp",
          text: "LSP",
          align: "end"
        },
        {
          value: "ts0",
          text: "Start"
        },
        {
          value: "ts1",
          text: "End"
        },
        {
          value: "num_points",
          text: "Num. points",
          align: "end"
        },
        {
          value: "length",
          text: "Length",
          align: "end"
        },
        {
          value: "azimuth",
          text: "Azimuth",
          align: "end"
        },
        {
          value: "remarks",
          text: "Remarks"
        },
        {
          value: "speed",
          text: "Speed"
        },
        {
          text: "Line change after",
          value: "lag",
          sortable: false
        }
      ],
      items: [],
      remarks: null,
      editRemarks: false,
      filter: null,
      options: {},
      sequenceCount: null,
      activeItem: null,
      edit: null, // {sequence, key, value}
      queuedReload: false,
      itemsPerPage: 25,

      plannerConfig: null,
      shiftAll: false, // Shift all sequences checkbox

      // Weather API
      wxData: null,
      weathercode: {
        0: {
          description: "Clear sky",
          icon: "mdi-weather-sunny"
        },
        1: {
          description: "Mainly clear",
          icon: "mdi-weather-sunny"
        },
        2: {
          description: "Partly cloudy",
          icon: "mdi-weather-partly-cloudy"
        },
        3: {
          description: "Overcast",
          icon: "mdi-weather-cloudy"
        },
        45: {
          description: "Fog",
          icon: "mde-weather-fog"
        },
        48: {
          description: "Depositing rime fog",
          icon: "mdi-weather-fog"
        },
        51: {
          description: "Light drizzle",
          icon: "mdi-weather-partly-rainy"
        },
        53: {
          description: "Moderate drizzle",
          icon: "mdi-weather-partly-rainy"
        },
        55: {
          description: "Dense drizzle",
          icon: "mdi-weather-rainy"
        },
        56: {
          description: "Light freezing drizzle",
          icon: "mdi-weather-partly-snowy-rainy"
        },
        57: {
          description: "Freezing drizzle",
          icon: "mdi-weather-partly-snowy-rainy"
        },
        61: {
          description: "Light rain",
          icon: "mdi-weather-rainy"
        },
        63: {
          description: "Moderate rain",
          icon: "mdi-weather-rainy"
        },
        65: {
          description: "Heavy rain",
          icon: "mdi-weather-pouring"
        },
        66: {
          description: "Light freezing rain",
          icon: "mdi-loading"
        },
        67: {
          description: "Freezing rain",
          icon: "mdi-loading"
        },
        71: {
          description: "Light snow",
          icon: "mdi-loading"
        },
        73: {
          description: "Moderate snow",
          icon: "mdi-loading"
        },
        75: {
          description: "Heavy snow",
          icon: "mdi-loading"
        },
        77: {
          description: "Snow grains",
          icon: "mdi-loading"
        },
        80: {
          description: "Light rain showers",
          icon: "mdi-loading"
        },
        81: {
          description: "Moderate rain showers",
          icon: "mdi-loading"
        },
        82: {
          description: "Violent rain showers",
          icon: "mdi-loading"
        },
        85: {
          description: "Light snow showers",
          icon: "mdi-loading"
        },
        86: {
          description: "Snow showers",
          icon: "mdi-loading"
        },
        95: {
          description: "Thunderstorm",
          icon: "mdi-loading"
        },
        96: {
          description: "Hailstorm",
          icon: "mdi-loading"
        },
        99: {
          description: "Heavy hailstorm",
          icon: "mdi-loading"
        },
      },

      // Context menu stuff
      contextMenuShow: false,
      contextMenuX: 0,
      contextMenuY: 0,
      contextMenuItem: null
    }
  },

  computed: {
    ...mapGetters(['user', 'plannedSequencesLoading', 'plannedSequences', 'planRemarks'])
  },

  watch: {

    options: {
      handler () {
        this.fetchPlannedSequences();
      },
      deep: true
    },

    async plannedSequences () {
      await this.fetchPlannedSequences();
    },

    async edit (newVal, oldVal) {
      if (newVal === null && oldVal !== null) {
        const item = this.items.find(i => i.sequence == oldVal.sequence);

        // Get around this Vuetify ‘feature’
        // https://github.com/vuetifyjs/vuetify/issues/4144
        if (oldVal.value === null) oldVal.value = "";

        if (item) {
          if (item[oldVal.key] != oldVal.value) {
            if (oldVal.key == "lagAfter") {
              // Convert from minutes to seconds
              oldVal.value *= 60;
            } else if (oldVal.key == "speed") {
              // Convert knots to metres per second
              oldVal.value = oldVal.value*(1.852/3.6);
            }

            if (await this.saveItem(oldVal)) {
              item[oldVal.key] = oldVal.value;
            } else {
              this.edit = oldVal;
            }

          }
        }

      }
    },

    filter (newVal, oldVal) {
      if (newVal?.toLowerCase() != oldVal?.toLowerCase()) {
        this.fetchPlannedSequences();
      }
    },

    itemsPerPage (newVal, oldVal) {
      localStorage.setItem(`dougal/prefs/${this.user?.name}/${this.$route.params.project}/${this.$options.name}/items-per-page`, newVal);
    },

    user (newVal, oldVal) {
      this.itemsPerPage = Number(localStorage.getItem(`dougal/prefs/${this.user?.name}/${this.$route.params.project}/${this.$options.name}/items-per-page`)) || 25;
    }

  },

  methods: {

    suntimes (line) {
      const oneday = 86400000;

      function isDay (srss, ts, lat, lng) {
        if (isNaN(srss.sunriseEnd) || isNaN(srss.sunsetStart)) {
          // Between March and September
          ts = new Date(ts);
          if (ts.getMonth() >= 2 && ts.getMonth() <= 8) {
            // Polar day in the Northern hemisphere, night in the South
            return lat > 0;
          } else {
            return lat < 0;
          }
        } else {
          if (srss.sunriseEnd < ts) {
            if (ts < srss.sunsetStart) {
              return true;
            } else {
              return suncalc.getTimes(new Date(ts.valueOf() + oneday), lat, lng).sunriseEnd < ts;
            }
          } else {
            return ts < suncalc.getTimes(new Date(ts.valueOf() - oneday), lat, lng).sunsetStart;
          }
        }
      }

      let {ts0, ts1} = line;
      const [ lng0, lat0 ] = line.geometry.coordinates[0];
      const [ lng1, lat1 ] = line.geometry.coordinates[1];

      if (ts1-ts0 > oneday) {
        console.warn("Cannot provide reliable sunrise / sunset times for lines over 24 hr in this version");
        //return null;
      }

      const srss0 = suncalc.getTimes(ts0, lat0, lng0);
      const srss1 = suncalc.getTimes(ts1, lat1, lng1);

      srss0.prevDay = suncalc.getTimes(new Date(ts0.valueOf()-oneday), lat0, lng0);
      srss1.nextDay = suncalc.getTimes(new Date(ts1.valueOf()+oneday), lat1, lng1);

      srss0.isDay = isDay(srss0, ts0, lat0, lng0);
      srss1.isDay = isDay(srss1, ts1, lat1, lng1);

      return {
        ts0: srss0,
        ts1: srss1
      };
    },

    srssIcon (line) {
      const srss = this.suntimes(line);
      const moon = suncalc.getMoonIllumination(line.ts0);
      return srss.ts0.isDay && srss.ts1.isDay
        ? 'mdi-weather-sunny'
        : !srss.ts0.isDay && !srss.ts1.isDay
          ? moon.phase < 0.05
            ? 'mdi-moon-new'
            : moon.phase < 0.25
              ? 'mdi-moon-waxing-crescent'
              : moon.phase < 0.45
                ? 'mdi-moon-waxing-gibbous'
                : moon.phase < 0.55
                  ? 'mdi-moon-full'
                  : moon.phase < 0.75
                    ? 'mdi-moon-waning-gibbous'
                    : 'mdi-moon-waning-crescent'
          : 'mdi-theme-light-dark';
    },

    srssMoonPhase (line) {
      const ts = new Date((Number(line.ts0)+Number(line.ts1))/2);
      const moon = suncalc.getMoonIllumination(ts);
      return moon.phase < 0.05
        ? 'New moon'
        : moon.phase < 0.25
          ? 'Waxing crescent moon'
          : moon.phase < 0.45
            ? 'Waxing gibbous moon'
            : moon.phase < 0.55
              ? 'Full moon'
              : moon.phase < 0.75
                ? 'Waning gibbous moon'
                : 'Waning crescent moon';
    },

    srssInfo (line) {
      const srss = this.suntimes(line);
      const text = [];

      try {
        text.push(`Sunset at\t${srss.ts0.prevDay.sunset.toISOString().substr(0, 16)}Z (FSP)`);
        text.push(`Sunrise at\t${srss.ts0.sunrise.toISOString().substr(0, 16)}Z (FSP)`);
        text.push(`Sunset at\t${srss.ts0.sunset.toISOString().substr(0, 16)}Z (FSP)`);
        if (line.ts0.getUTCDate() != line.ts1.getUTCDate()) {
          text.push(`Sunrise at\t${srss.ts1.sunrise.toISOString().substr(0, 16)}Z (LSP)`);
          text.push(`Sunset at\t${srss.ts1.sunset.toISOString().substr(0, 16)}Z (LSP)`);
        }
        text.push(`Sunrise at\t${srss.ts1.nextDay.sunrise.toISOString().substr(0, 16)}Z (LSP)`);
      } catch (err) {
        if (err instanceof RangeError) {
          text.push(srss.ts0.isDay ? "Polar day" : "Polar night");
        } else {
          console.log("ERROR", err);
        }
      }

      if (!srss.ts0.isDay || !srss.ts1.isDay) {
        text.push(this.srssMoonPhase(line));
      }

      return text.join("\n");
    },

    wxInfo (line) {

      function atm(key) {
        return line.meta?.wx?.atmospheric?.hourly[key];
      }

      function mar(key) {
        return line.meta?.wx?.marine?.hourly[key];
      }

      const code = atm("weathercode");

      const description = this.weathercode[code]?.description ?? `WMO code ${code}`;
      const wind_speed = Math.round(atm("windspeed_10m"));
      const wind_direction = String(Math.round(atm("winddirection_10m"))).padStart(3, "0");
      const pressure = Math.round(atm("surface_pressure"));
      const temperature = Math.round(atm("temperature_2m"));
      const humidity = atm("relativehumidity_2m");
      const precipitation = atm("precipitation");
      const precipitation_probability = atm("precipitation_probability");
      const precipitation_str = precipitation_probability
        ? `\nPrecipitation ${precipitation} mm (prob. ${precipitation_probability}%)`
        : ""

      const wave_height = mar("wave_height").toFixed(1);
      const wave_direction = mar("wave_direction");
      const wave_period = mar("wave_period");

      return `${description}\n${temperature}° C\n${pressure} hPa\nWind ${wind_speed} kt ${wind_direction}°\nRelative humidity ${humidity}%${precipitation_str}\nWaves ${wave_height} m ${wave_direction}° @ ${wave_period} s`;
    },

    wxIcon (line) {
      const code = line.meta?.wx?.atmospheric?.hourly?.weathercode;

      return this.weathercode[code]?.icon ?? "mdi-help";

    },

    async wxQuery (line) {
      function midpoint(line) {
        // WARNING Fails if across the antimeridian
        const longitude = (line.geometry.coordinates[0][0] + line.geometry.coordinates[1][0])/2;
        const latitude = (line.geometry.coordinates[0][1] + line.geometry.coordinates[1][1])/2;
        return [ longitude, latitude ];
      }

      function extract (fcst) {
        const τ = (line.ts0.valueOf() + line.ts1.valueOf()) / 2000;
        const [idx, ε] = fcst?.hourly?.time?.reduce( (acc, cur, idx) => {
          const δ = Math.abs(cur - τ);
          const retval = acc
            ? acc[1] < δ
              ? acc
              : [ idx, δ ]
            : [ idx, δ ];

          return retval;
        });

        if (idx) {
          const hourly = {};
          for (let key in fcst?.hourly) {
            fcst.hourly[key] = fcst.hourly[key][idx];
          }
        }

        return fcst;
      }

      async function fetch_atmospheric (opts) {
        const { longitude, latitude, dt0, dt1 } = opts;

        const url = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=temperature_2m,relativehumidity_2m,precipitation_probability,precipitation,weathercode,pressure_msl,surface_pressure,windspeed_10m,winddirection_10m&daily=uv_index_max&windspeed_unit=kn&timeformat=unixtime&timezone=GMT&start_date=${dt0}&end_date=${dt1}&format=json`;
        const init = {};
        const res = await fetch (url, init);
        if (res?.ok) {
          const data = await res.json();

          return extract(data);
        }
      }

      async function fetch_marine (opts) {
        const { longitude, latitude, dt0, dt1 } = opts;
        const url = `https://marine-api.open-meteo.com/v1/marine?latitude=${latitude}&longitude=${longitude}&hourly=wave_height,wave_direction,wave_period&timeformat=unixtime&timezone=GMT&start_date=${dt0}&end_date=${dt1}&format=json`;

        const init = {};
        const res = await fetch (url, init);
        if (res?.ok) {
          const data = await res.json();

          return extract(data);
        }
      }

      if (line) {
        const [ longitude, latitude ] = midpoint(line);
        const dt0 = line.ts0.toISOString().substr(0, 10);
        const dt1 = line.ts1.toISOString().substr(0, 10);

        return {
          atmospheric: await fetch_atmospheric({longitude, latitude, dt0, dt1}),
          marine: await fetch_marine({longitude, latitude, dt0, dt1})
        };
      }
    },

    lagAfter (item) {
      const pos = this.items.indexOf(item)+1;
      if (pos != 0) {
        if (pos < this.items.length) {
          const nextItem = this.items[pos];
          return nextItem.ts0 - item.ts1;
        }
      } else {
        console.warn("Item not found in list", item);
      }
      return this.plannerConfig.defaultLineChangeDuration * 60*1000;
    },

    knots (item) {
      const v = item.length / ((item.ts1-item.ts0)/1000); // m/s
      return v*3.6/1.852;
    },

    contextMenu (e, {item}) {
      e.preventDefault();
      this.contextMenuShow = false;
      this.contextMenuX = e.clientX;
      this.contextMenuY = e.clientY;
      this.contextMenuItem = item;
      this.$nextTick( () => this.contextMenuShow = true );
    },

    async deletePlannedSequence () {
      console.log("Delete sequence", this.contextMenuItem.sequence);
      const url = `/project/${this.$route.params.project}/plan/${this.contextMenuItem.sequence}`;
      const init = {method: "DELETE"};
      await this.api([url, init]);
    },

    editItem (item, key, value) {
      this.edit = {
        sequence: item.sequence,
        key,
        value: value === undefined ? item[key] : value
      }
    },

    async saveItem (edit) {
      if (!edit)  return;

      try {
        const url = `/project/${this.$route.params.project}/plan/${edit.sequence}`;
        const init = {
          method: "PATCH",
          body: {
            [edit.key]: edit.value
          }
        };

        let res;
        await this.api([url, init, (e, r) => res = r]);
        return res && res.ok;
      } catch (err) {
        return false;
      }
    },

    async saveRemarks () {
      const url = `/project/${this.$route.params.project}/info/plan/remarks`;
      let res;
      if (this.remarks) {
        const init = {
          method: "PUT",
          headers: { "Content-Type": "text/plain" },
          body: this.remarks
        };
        await this.api([url, init, (e, r) => res = r]);
      } else {
        const init = {
          method: "DELETE"
        };
        await this.api([url, init, (e, r) => res = r]);
      }
      if (res && res.ok) {
        this.editRemarks = false;
      }
    },

    async getPlannerConfig () {
      const url = `/project/${this.$route.params.project}/configuration/planner`;
      this.plannerConfig = await this.api([url]) || {
        "overlapAfter": 0,
        "overlapBefore": 0,
        "defaultAcquisitionSpeed": 5,
        "defaultLineChangeDuration": 36
      }
    },

    async fetchPlannedSequences (opts = {}) {
      const options = {
        text: this.filter,
        ...this.options
      };
      const res = await this.getPlannedSequences([this.$route.params.project, options]);
      this.items = res.sequences;
      this.sequenceCount = res.count;
      this.remarks = this.planRemarks;
    },

    setActiveItem (item) {
      this.activeItem = this.activeItem == item
        ? null
        : item;
    },

    ...mapActions(["api", "showSnack", "getPlannedSequences"])
  },

  async mounted () {
    await this.getPlannerConfig();
    await this.fetchPlannedSequences();
  }

}

</script>
