<template>
<v-card style="min-height:400px;" outlined>
  <v-card-title class="headline">
    Gun details
  </v-card-title>

  <v-container fluid fill-height>
    <v-row>
      <v-col>
        <div class="graph-container" ref="graphHeat"></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: 'DougalGraphGunsDepth',

  props: [ "data" ],

  data () {
    return {
      graph: null,
      graphHover: null,
      busy: false,
      resizeObserver: null,
      // TODO: aspects should be a prop
      aspects: [
          "Mode", "Detect", "Autofire", "Aimpoint", "Firetime", "Delay",
          "Delta",
          "Depth", "Pressure", "Volume", "Filltime"
        ]
    };
  },

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

  watch: {

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

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

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

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

  },

  methods: {

    plot () {
      this.plotHeat();
    },

    async plotHeat () {


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

      function transform (data, aspects=["Depth", "Pressure"]) {

        const facets = [
          // Mode
          {
            params: {
              name: "Mode",
              hovertemplate: "SP%{x}<br>%{y}<br>%{text}",
            },

            text: [ "Off", "Auto", "Manual", "Disabled" ],

            conversion: (gun, shot) => {
              switch (gun[3]) {
                case "A":
                  return 1;
                case "M":
                  return 2;
                case "O":
                  return 0;
                case "D":
                  return 3;
              }
            }
          },

          // Detect
          {
            params: {
              name: "Detect",
              hovertemplate: "SP%{x}<br>%{y}<br>%{text}",
            },

            text: [ "Zero", "Peak", "Level" ],

            conversion: (gun, shot) => {
              switch (gun[4]) {
                case "P":
                  return 1;
                case "Z":
                  return 0;
                case "L":
                  return 2;
              }
            }
          },

          // Autofire
          {
            params: {
              name: "Autofire",
              hovertemplate: "SP%{x}<br>%{y}<br>%{text}",
            },

            text:  [ "False", "True" ],

            conversion: (gun, shot) => {
              return gun[5] ? 1 : 0;
            }
          },

          // Aimpoint
          {
            params: {
              name: "Aimpoint",
              hovertemplate: "SP%{x}<br>%{y}<br>%{z} ms"
            },

            conversion: (gun, shot) => gun[7]
          },

          // Firetime
          {
            params: {
              name: "Firetime",
              hovertemplate: "SP%{x}<br>%{y}<br>%{z} ms"
            },

            conversion: (gun, shot) => gun[2] == shot.meta.src_number ? gun[8] : null
          },

          // Delta
          {
            params: {
              name: "Delta",
              hovertemplate: "SP%{x}<br>%{y}<br>%{z} ms",
              // NOTE: These values are based on
              // Grane + Snorre's ±1.5 ms spec. While a fairly
              // common range, I still consider these min / max
              // numbers to have been chosen semi-arbitrarily.
              zmin: -2,
              zmax: 2
            },

            conversion: (gun, shot) => gun[2] == shot.meta.src_number ? gun[7]-gun[8] : null
          },

          // Delay
          {
            params: {
              name: "Delay",
              hovertemplate: "SP%{x}<br>%{y}<br>%{z} ms"
            },

            conversion: (gun, shot) => gun[9]
          },

          // Depth
          {
            params: {
              name: "Depth",
              hovertemplate: "SP%{x}<br>%{y}<br>%{z} m"
            },

            conversion: (gun, shot) => gun[10]
          },

          // Pressure
          {
            params: {
              name: "Pressure",
              hovertemplate: "SP%{x}<br>%{y}<br>%{z} psi"
            },

            conversion: (gun, shot) => gun[11]
          },

          // Volume
          {
            params: {
              name: "Volume",
              hovertemplate: "SP%{x}<br>%{y}<br>%{z} in³"
            },

            conversion: (gun, shot) => gun[12]
          },

          // Filltime
          {
            params: {
              name: "Filltime",
              hovertemplate: "SP%{x}<br>%{y}<br>%{z} ms"
            },

            // NOTE that filltime is applicable to the *non* firing guns
            conversion: (gun, shot) => gun[2] == shot.meta.src_number ? null : gun[13]
          }


        ];

        // Get gun numbers
        const guns = [...new Set(data.map( s => s.meta.guns.map( g => g[1] ) ).flat())];

        // z eventually will have the structure:
        // z = {
        //       [aspect]: [ // First shotpoint
        //                   [ // Value for gun 0, gun 1, … ],
        //                   …more shotpoints…
        //                 ]
        //     }
        const z = {};

        // x is an array of shotpoints
        const x = [];

        // y is an array of gun numbers
        const y = guns.map( gun => `G${gun}` );

        // Build array of guns (i.e., populate z)
        // We prefer to do this outside the shot-to-shot loop
        // for efficiency
        for (const facet of facets) {
          const label = facet.params.name;
          z[label] = Array(guns.length);
          for (let i=0; i<guns.length; i++) {
            z[label][i] = [];
          }
        }

        // Populate array of guns with shotpoint data
        for (let shot of data) {
          x.push(shot.point);

          for (const facet of facets) {
            const label = facet.params.name;
            const facetGunsArray = z[label];

            for (const gun of shot.meta.guns) {
              const gunIndex = gun[1]-1;
              const facetGun = facetGunsArray[gunIndex];
              facetGun.push(facet.conversion(gun, shot));
            }
          }
        }

        return aspects.map( (aspect, idx) => {
          const facet = facets.find(el => el.params.name == aspect) || {};

          const defaultParams = {
            name: aspect,
            type: "heatmap",
            showscale: false,
            x,
            y,
            z: z[aspect],
            text: facet.text ? z[aspect].map(row => row.map(v => facet.text[v])) : undefined,
            xaxis: "x",
            yaxis: "y" + (idx > 0 ? idx+1 : "")
          }


          return Object.assign({}, defaultParams, facet.params);
        });
      }

      const data = transform(this.data.items, this.aspects);
      this.busy = false;

      const layout = {
        title: {text: "Gun details – sequence %{meta.sequence}"},
        height: 200*this.aspects.length,
        //autocolorscale: true,
        /*
        grid: {
          rows: this.aspects.length,
          columns: 1,
          pattern: "coupled",
          roworder: "bottom to top"
        },
        */
        //autosize: true,
        // 	colorscale: "sequential",

        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
      };

      this.aspects.forEach ( (aspect, idx) => {
        const num = idx+1;
        const key = "yaxis" + num;
        const anchor = "y" + num;
        const segment = (1/this.aspects.length);
        const margin = segment/20;
        const domain = [
          segment*idx + margin,
          segment*num - margin
        ];
        layout[key] = {
          title: aspect,
          anchor,
          domain
        }
      });

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

      this.graph = Plotly.newPlot(this.$refs.graphHeat, 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.graphHeat);
  },

  beforeDestroy () {
    if (this.resizeObserver) {
      this.resizeObserver.unobserve(this.$refs.graphHeat);
    }
  }

};
</script>
