<template>
<v-card style="min-height:400px;" outlined>
  <v-card-title class="headline">
    Gun pressures
    <v-spacer></v-spacer>
    <!--
    <v-switch v-model="shotpoint" label="Shotpoint"></v-switch>
    <v-switch class="ml-4" v-model="violinplot" label="Violin plot"></v-switch>
    -->
  </v-card-title>

  <v-container fluid fill-height>
    <v-row>
      <v-col>
        <div class="graph-container" ref="graphSeries"></div>
      </v-col>
    </v-row>
    <v-row v-show="shotpoint">
      <v-col>
        <div class="graph-container" ref="graphBar"></div>
      </v-col>
    </v-row>
    <v-row v-show="violinplot">
      <v-col>
        <div class="graph-container" ref="graphViolin"></div>
      </v-col>
    </v-row>
  </v-container>

  <v-overlay :value="busy" absolute z-index="1">
    <v-progress-circular indeterminate></v-progress-circular>
  </v-overlay>
</v-card>

</template>

<style scoped>
.graph-container {
  width: 100%;
  height: 100%;
}
</style>

<script>

import * as d3a from 'd3-array';
import Plotly from 'plotly.js-dist';
import { mapActions, mapGetters } from 'vuex';
import unpack from '@/lib/unpack.js';
import * as aes from '@/lib/graphs/aesthetics.js';

export default {
  name: 'DougalGraphGunsPressure',

  props: [ "data", "settings" ],

  data () {
    return {
      graph: null,
      graphHover: null,
      busy: false,
      resizeObserver: null,
      shotpoint: true,
      violinplot: true
    };
  },

  computed: {
    //...mapGetters(['apiUrl'])
  },

  watch: {

    data (newVal, oldVal) {
      console.log("data changed");

      if (newVal === null) {
        this.busy = true;
      } else {
        this.busy = false;
        this.plot();
      }
    },

    settings () {
      for (const key in this.settings) {
        this[key] = this.settings[key];
      }
    },

    shotpoint () {
      if (this.shotpoint) {
        this.replot();
      }
      this.$emit("update:settings", {[`${this.$options.name}.shotpoint`]: this.shotpoint});
    },

    violinplot () {
      if (this.violinplot) {
        this.plotViolin();
      }
      this.$emit("update:settings", {[`${this.$options.name}.violinplot`]: this.violinplot});
    },

    "$vuetify.theme.isDark" () {
      this.plot();
    }

  },

  methods: {

    plot () {
      this.plotSeries();
      if (this.violinplot) {
        this.plotViolin();
      }
    },

    async plotSeries () {

      function transformSeries (d, src_number, otherParams={}) {

        const meta = src_number
          ? unpack(d, "meta").filter( s => s.src_number == src_number )
          : unpack(d, "meta");
        const guns = unpack(meta, "guns").map(s => s.filter(g => g[2] == src_number));;
        const gunPressures = guns.map(s => s.map(g => g[11]));
        const gunPressuresSorted = gunPressures.map(s => d3a.sort(s));
        const gunVolumes = guns.map(s => s.map(g => g[12]));
        const gunPressureWeights = gunVolumes.map( (s, sidx) => s.map( v => v/meta[sidx].volume ));
        const gunsWeightedAvgPressure = gunPressures.map( (s, sidx) =>
          d3a.sum(s.map( (pressure, gidx) => pressure * gunPressureWeights[sidx][gidx] )) / d3a.sum(gunPressureWeights[sidx])
        );

        const manifold = unpack(meta, "manifold");
        const x = src_number
          ? unpack(d.filter(s => s.meta.src_number == src_number), "point")
          : unpack(d, "point");

        const traceManifold = {
          name: "Manifold",
          type: "scatter",
          mode: "lines",
          line: { ...aes.gunArrays[src_number || 1].avg.line, dash: "dot", width: 1 },
          x,
          y: manifold,
        };

        const tracesGunPressures = [{
          type: "scatter",
          mode: "lines",
          x,
          y: gunPressuresSorted.map(s => d3a.quantileSorted(s, 0.25)),
          ...aes.gunArrays[src_number || 1].min
        },
        {
          type: "scatter",
          mode: "lines",
          fill: "tonexty",
          x,
          y: gunsWeightedAvgPressure,
          ...aes.gunArrays[src_number || 1].avg
        },
        {
          type: "scatter",
          mode: "lines",
          fill: "tonexty",
          x,
          y: gunPressuresSorted.map(s => d3a.quantileSorted(s, 0.75)),
          ...aes.gunArrays[src_number || 1].max
        }];

        const tracesGunsPressuresIndividual = {
          //name: `Array ${src_number} outliers`,
          type: "scatter",
          mode: "markers",
          marker: {size: 2 },
          hoverinfo: "skip",
          x: gunPressuresSorted.map( (s, idx) =>
            s.filter( g => g < d3a.quantileSorted(s, 0.05) || g > d3a.quantileSorted(s, 0.95))
            .map( f => Array(f.length).fill(x[idx]) ).flat()
          ).flat(),
          y: gunPressuresSorted.map( (s, idx) =>
            s.filter( g =>  g < d3a.quantileSorted(s, 0.05) || g > d3a.quantileSorted(s, 0.95))
          ).flat(),
          ...aes.gunArrays[src_number || 1].out
        };

        const data = [ traceManifold, ...tracesGunPressures, tracesGunsPressuresIndividual ]
        return data;
      }

      if (!this.data) {
        console.log("missing data");
        return;
      }

      const sources = [ ...new Set(unpack(this.data.items, "meta").map( s => s.src_number ))];
      const data = sources.map( src_number => transformSeries(this.data.items, src_number) ).flat();
      console.log("Sources", sources);
      console.log(data);
      this.busy = false;

      const layout = {
        //autosize: true,
        title: {text: "Gun pressures – sequence %{meta.sequence}"},
        autocolorscale: true,
        // 	colorscale: "sequential",
        hovermode: "x",
        yaxis: {
          title: "Pressure (psi)",
          //zeroline: false
        },
        xaxis: {
          title: "Shotpoint",
          showspikes: true
        },
        font: {
          color: this.$vuetify.theme.isDark ? "#fff" : undefined
        },
        plot_bgcolor:"rgba(0,0,0,0)",
        paper_bgcolor:"rgba(0,0,0,0)",
        meta: this.data.meta
      };

      const config = {
        editable: false,
        displaylogo: false
      };

      this.graph = Plotly.newPlot(this.$refs.graphSeries, data, layout, config);
      this.$refs.graphSeries.on('plotly_hover', (d) => {
        const point = d.points[0].x;
        const item = this.data.items.find(s => s.point == point);
        const guns = item.meta.guns.filter( g =>  g[2] == item.meta.src_number );
        const gunIds = guns.map( g => "G"+g[1] );
        const pressures = unpack(guns, 11);
        const volumes = unpack(guns, 12);
        const maxVolume = d3a.max(volumes);
        const data = [{
          type: "bar",
          x: gunIds,
          y: pressures,
          width: volumes.map( v => v/maxVolume ),
          transforms: [{
            type: "groupby",
            groups: unpack(guns, 0)
          }],
        }];

        const layout = {
          title: {text: "Gun pressures – shot %{meta.point}"},
          height: 300,
          yaxis: {
            title: "Pressure (psi)",
            range: [ Math.min(d3a.min(pressures), 1950), Math.max(d3a.max(pressures), 2050) ]
          },
          xaxis: {
            title: "Gun number",
            type: 'category'
          },
          font: {
            color: this.$vuetify.theme.isDark ? "#fff" : undefined
          },
          plot_bgcolor:"rgba(0,0,0,0)",
          paper_bgcolor:"rgba(0,0,0,0)",
          meta: {
            point
          }
        };

        const config = { displaylogo: false };

        Plotly.react(this.$refs.graphBar, data, layout, config);
      });
    },

    async plotViolin () {

      function transformViolin (d, opts = {}) {

        const styles = [];

        unpack(unpack(d, "meta"), "guns").flat().forEach(i => {
          const gunId = i[1];
          const arrayId = i[2];
          if (!styles[gunId]) {
            styles[gunId] = Object.assign({target: gunId}, aes.gunArrayViolins[arrayId]);
          }
        });

        const data = {
          type: 'violin',
          x: unpack(unpack(unpack(d, "meta"), "guns").flat(), 1),   // Gun number
          y: unpack(unpack(unpack(d, "meta"), "guns").flat(), 11),  // Gun pressure
          points: 'none',
          box: {
            visible: true
          },
          line: {
            color: 'green',
          },
          meanline: {
            visible: true
          },
          transforms: [{
            type: 'groupby',
            groups: unpack(unpack(unpack(d, "meta"), "guns").flat(), 1),
            styles: styles.filter(i => !!i)
          }]
        }

        return data;
      }


      console.log("plot violin");
      if (!this.data) {
        console.log("missing data");
        return;
      }
      console.log("Will plot sequence", this.data.meta.project, this.data.meta.sequence);

      const data = [ transformViolin(this.data.items) ];
      this.busy = false;

      const layout = {
        //autosize: true,
        showlegend: false,
		title: {text: "Individual gun pressures – sequence %{meta.sequence}"},
		autocolorscale: true,
	// 	colorscale: "sequential",
		yaxis: {
			title: "Pressure (psi)",
			zeroline: false
		},
		xaxis: {
			title: "Gun number"
		},
        font: {
          color: this.$vuetify.theme.isDark ? "#fff" : undefined
        },
        plot_bgcolor:"rgba(0,0,0,0)",
        paper_bgcolor:"rgba(0,0,0,0)",
        meta: this.data.meta
      };

      const config = {
        editable: false,
        displaylogo: false
      };

      this.graph = Plotly.newPlot(this.$refs.graphViolin, data, layout, config);
    },


    replot () {
      if (!this.graph) {
        return;
      }

      console.log("Replotting");
      Object.values(this.$refs).forEach( ref => {
        if (ref.data) {
          console.log("Replotting", ref, ref.clientWidth, ref.clientHeight);
          Plotly.relayout(ref, {
            width: ref.clientWidth,
            height: ref.clientHeight
          });
        }
      });
    },

    ...mapActions(["api"])

  },

  mounted () {

    if (this.data) {
      this.plot();
    } else {
      this.busy = true;
    }

    this.resizeObserver = new ResizeObserver(this.replot)
    this.resizeObserver.observe(this.$refs.graphSeries);
    this.resizeObserver.observe(this.$refs.graphViolin);
    this.resizeObserver.observe(this.$refs.graphBar);
  },

  beforeDestroy () {
    if (this.resizeObserver) {
      this.resizeObserver.unobserve(this.$refs.graphBar);
      this.resizeObserver.unobserve(this.$refs.graphViolin);
      this.resizeObserver.unobserve(this.$refs.graphSeries);
    }
  }

};
</script>
