<script setup lang="ts">
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
import { computed, getCurrentInstance, onBeforeUnmount, onMounted } from "vue";
import CvRangeSlider from "@/components/common/cv-range-slider.vue";
import CvSwitch from "@/components/common/cv-switch.vue";
import { useStateStore } from "@/stores/StateStore";
import { ColorPicker, type HSV } from "@/lib/ColorPicker";

const averageHue = computed<number>(() => {
  const isHueInverted = useCameraSettingsStore().currentPipelineSettings.hueInverted;
  let val = Object.values(useCameraSettingsStore().currentPipelineSettings.hsvHue).reduce((a, b) => a + b, 0);

  if(isHueInverted) val += 180;
  if (val > 360) val -= 360;

  return val;
});

// TODO fix cv-range-slider so that store access doesn't need to be deferred
const hsvHue = computed<[number, number]>({
  get: () => Object.values(useCameraSettingsStore().currentPipelineSettings.hsvHue) as [number, number],
  set: v => useCameraSettingsStore().currentPipelineSettings.hsvHue = v
});
const hsvSaturation = computed<[number, number]>({
  get: () => Object.values(useCameraSettingsStore().currentPipelineSettings.hsvSaturation) as [number, number],
  set: v => useCameraSettingsStore().currentPipelineSettings.hsvSaturation = v
});
const hsvValue = computed<[number, number]>({
  get: () => Object.values(useCameraSettingsStore().currentPipelineSettings.hsvValue) as [number, number],
  set: v => useCameraSettingsStore().currentPipelineSettings.hsvValue = v
});

let selectedEventMode: 0 | 1 | 2 | 3 = 0;
const handleStreamClick = (event: MouseEvent) => {
  if(!useStateStore().colorPickingMode || selectedEventMode === 0) return;

  const cameraStream = document.getElementById("input-camera-stream");
  if(cameraStream === null) return;

  const canvas = document.createElement("canvas");
  canvas.width = cameraStream.clientWidth;
  canvas.height = cameraStream.clientHeight;

  // Get the (x, y) position of the click with (0, 0) in the top left corner
  const rect = cameraStream.getBoundingClientRect();
  const x = Math.round((event.clientX - rect.left) / rect.width * cameraStream.clientWidth);
  const y = Math.round((event.clientY - rect.top) / rect.height * cameraStream.clientHeight);

  const context = canvas.getContext("2d");
  if(context === null) return;

  context.drawImage(cameraStream as CanvasImageSource, 0, 0, cameraStream.clientWidth, cameraStream.clientHeight);
  const colorPicker = new ColorPicker(context.getImageData(x, y, 1, 1).data);

  // Calculate HSV values based on the mode
  let selectedHSVData: [HSV, HSV] = [[0, 0, 0], [0, 0, 0]];
  if(selectedEventMode === 1) {
    selectedHSVData = colorPicker.selectedColorRange();
  } else {
    const currentHue = Object.values(useCameraSettingsStore().currentPipelineSettings.hsvHue);
    const currentSaturation = Object.values(useCameraSettingsStore().currentPipelineSettings.hsvSaturation);
    const currentValue = Object.values(useCameraSettingsStore().currentPipelineSettings.hsvValue);

    const currentData: [HSV, HSV] = [
      [currentHue[0], currentSaturation[0], currentValue[0]],
      [currentHue[1], currentSaturation[1], currentValue[1]]
    ];

    if(selectedEventMode === 2) {
      selectedHSVData = colorPicker.expandColorRange(currentData);
    } else if(selectedEventMode === 3) {
      selectedHSVData = colorPicker.shrinkColorRange(currentData);
    }
  }

  // Update the store and backend with the new HSV values
  useCameraSettingsStore().changeCurrentPipelineSetting({
    hsvHue: [selectedHSVData[0][0], selectedHSVData[1][0]],
    hsvSaturation: [selectedHSVData[0][1], selectedHSVData[1][1]],
    hsvValue: [selectedHSVData[0][2], selectedHSVData[1][2]]
  }, true);

  disableColorPicking();
};

// Put some default values in case color picking was enabled before the enableColorPicking method is called
let inputShowing = true;
let outputShowing = false;
const enableColorPicking = (mode: 1 | 2 | 3) => {
  useStateStore().colorPickingMode = true;
  inputShowing = useCameraSettingsStore().currentPipelineSettings.inputShouldShow;
  outputShowing = useCameraSettingsStore().currentPipelineSettings.outputShouldShow;
  useCameraSettingsStore().changeCurrentPipelineSetting({ outputShouldDraw: false, inputShouldShow: true, outputShouldShow: false }, true);
  selectedEventMode = mode;
};
const disableColorPicking = () => {
  useStateStore().colorPickingMode = false;
  useCameraSettingsStore().changeCurrentPipelineSetting({ outputShouldDraw: true, inputShouldShow: inputShowing, outputShouldShow: outputShowing }, true);
  selectedEventMode = 0;
};

onMounted(() => {
  const cameraStream = document.getElementById("input-camera-stream");
  if(cameraStream === null) return;

  cameraStream.addEventListener("click", handleStreamClick);
});
onBeforeUnmount(() => {
  const cameraStream = document.getElementById("input-camera-stream");
  if(cameraStream === null) return;

  cameraStream.removeEventListener("click", handleStreamClick);
});

const interactiveCols = computed(() => (getCurrentInstance()?.proxy.$vuetify.breakpoint.mdAndDown || false) && (!useStateStore().sidebarFolded || useCameraSettingsStore().isDriverMode)) ? 9 : 8;
</script>

<template>
  <div
    class="threshold-modifiers"
    :style="{'--averageHue': averageHue}"
  >
    <cv-range-slider
      id="hue-slider"
      v-model="hsvHue"
      :class="useCameraSettingsStore().currentPipelineSettings.hueInverted ? 'inverted-slider' : 'normal-slider'"
      label="Hue"
      tooltip="Describes color"
      :min="0"
      :max="180"
      :slider-cols="interactiveCols"
      :inverted="useCameraSettingsStore().currentPipelineSettings.hueInverted"
      @input="value => useCameraSettingsStore().changeCurrentPipelineSetting({hsvHue: value}, false)"
    />
    <cv-range-slider
      id="sat-slider"
      v-model="hsvSaturation"
      class="normal-slider"
      label="Saturation"
      tooltip="Describes colorfulness; the smaller this value the 'whiter' the color becomes"
      :min="0"
      :max="255"
      :slider-cols="interactiveCols"
      @input="value => useCameraSettingsStore().changeCurrentPipelineSetting({hsvSaturation: value}, false)"
    />
    <cv-range-slider
      id="value-slider"
      v-model="hsvValue"
      class="normal-slider"
      label="Value"
      tooltip="Describes lightness; the smaller this value the 'blacker' the color becomes"
      :min="0"
      :max="255"
      :slider-cols="interactiveCols"
      @input="value => useCameraSettingsStore().changeCurrentPipelineSetting({hsvValue: value}, false)"
    />
    <cv-switch
      v-model="useCameraSettingsStore().currentPipelineSettings.hueInverted"
      label="Invert Hue"
      :switch-cols="interactiveCols"
      tooltip="Selects the hue range outside of the hue slider bounds instead of inside"
      @input="value => useCameraSettingsStore().changeCurrentPipelineSetting({hueInverted: value}, false)"
    />
    <v-divider
      class="mt-3"
    />
    <div>
      <div class="pt-3 white--text">
        Color Picker
      </div>
      <v-row
        justify="center"
        class="mt-3 mb-3"
      >
        <template v-if="!useStateStore().colorPickingMode">
          <v-btn
            color="accent"
            class="ma-2 black--text"
            small
            @click="enableColorPicking(useCameraSettingsStore().currentPipelineSettings.hueInverted ? 2 : 3)"
          >
            <v-icon left>
              mdi-minus
            </v-icon>
            Shrink Range
          </v-btn>
          <v-btn
            color="accent"
            class="ma-2 black--text"
            small
            @click="enableColorPicking(1)"
          >
            <v-icon left>
              mdi-plus-minus
            </v-icon>
            {{ useCameraSettingsStore().currentPipelineSettings.hueInverted ? "Exclude" : "Set to" }} Average
          </v-btn>
          <v-btn
            color="accent"
            class="ma-2 black--text"
            small
            @click="enableColorPicking(useCameraSettingsStore().currentPipelineSettings.hueInverted ? 3: 2)"
          >
            <v-icon left>
              mdi-plus
            </v-icon>
            Expand Range
          </v-btn>
        </template>
        <template v-else>
          <v-btn
            color="accent"
            class="ma-2 black--text"
            style="width: 30%;"
            small
            @click="disableColorPicking"
          >
            Cancel
          </v-btn>
        </template>
      </v-row>
    </div>
  </div>
</template>

<style scoped lang="css">
.threshold-modifiers {
  --averageHue: 0;
}
#hue-slider >>> .v-slider {
  background: linear-gradient( to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100% );
  border-radius: 10px;
  box-shadow: 0 0 5px #333, inset 0 0 3px #333;
}
#sat-slider >>> .v-slider {
  background: linear-gradient( to right, #fff 0%, hsl(var(--averageHue), 100%, 50%) 100% );
  border-radius: 10px;
  box-shadow: 0 0 5px #333, inset 0 0 3px #333;
}
#value-slider >>> .v-slider {
  background: linear-gradient( to right, #000 0%, hsl(var(--averageHue), 100%, 50%) 100% );
  border-radius: 10px;
  box-shadow: 0 0 5px #333, inset 0 0 3px #333;
}
>>> .v-slider__thumb {
  outline: black solid thin;
}
.normal-slider >>> .v-slider__track-fill {
  outline: black solid thin;
}

.inverted-slider >>> .v-slider__track-background {
  outline: black solid thin;
}
</style>
