<template>
  <v-input
    class="v-text-field"
    :hint="hint"
    persistent-hint
    :value="text"
  >
    <label
      class="v-label"
      :class="[ $vuetify.theme.isDark && 'theme--dark', text && text.length && 'v-label--active' ]"
      style="left: 0px; right: auto; position: absolute;"
    >{{ label }}</label>
    <div class="input" slot="default"
      v-html="html"
    >
    </div>
    <template slot="append">
      <v-menu
        scrollable
        offset-y
        :close-on-content-click="false"
      >

        <template v-slot:activator="{on, attrs}">
          <v-btn
            icon
            v-bind="attrs"
            v-on="on"
          >
            <v-icon title="Configure sample values">mdi-list-box-outline</v-icon>
          </v-btn>
        </template>

        <v-card>
          <v-card-title>Sample values</v-card-title>
          <v-card-subtitle>Enter sample values to test your configuration</v-card-subtitle>

          <v-divider></v-divider>

          <v-card-text>

            <v-container>
              <v-row v-for="(prop, key) in properties" :key="key">
                <template v-if="prop.type == 'boolean'">
                  <v-col cols="6" align-self="center">
                    <v-chip
                      outlined
                      label
                      small
                      :color="getHSLColourFor(key)"
                      :title="prop.description"
                    >{{prop.summary || key}}</v-chip>
                  </v-col>
                  <v-col cols="6" align-self="center">
                    <v-simple-checkbox v-model="values[key]"></v-simple-checkbox>
                  </v-col>
                </template>
                <template v-else-if="key != 'text'">
                  <v-col cols="6" align-self="center">
                    <v-chip
                      outlined
                      label
                      small
                      :color="getHSLColourFor(key)"
                      :title="prop.description"
                    >{{prop.summary || key}}</v-chip>
                  </v-col>
                  <v-col cols="6" align-self="center">
                    <v-text-field v-if="prop.type == 'number'"
                      :type="prop.type"
                      :label="prop.summary || key"
                      :hint="prop.description"
                      v-model.number="values[key]"
                    ></v-text-field>
                    <v-text-field v-else
                      :type="prop.type"
                      :label="prop.summary || key"
                      :hint="prop.description"
                      v-model="values[key]"
                    ></v-text-field>
                  </v-col>
                </template>
              </v-row>
            </v-container>

          </v-card-text>
        </v-card>

      </v-menu>
    </template>
    <v-icon slot="prepend">mdi-list</v-icon>
  </v-input>
</template>

<style scoped>

.input {
  flex: 1 1 auto;
  line-height: 20px;
  padding: 8px 0 8px;
  min-height: 32px;
  max-height: 32px;
  max-width: 100%;
  min-width: 0px;
  width: 100%;
  white-space-collapse: preserve;
}

.multiline {
  font-family: mono;
  white-space: pre;
  overflow-x: auto;
  overflow-y: auto;
}

.multiline >>> .line-number {
  display: inline-block;
  font-size: 75%;
  width: 5ex;
  margin-inline-end: 1ex;
  text-align: right;
  border: none;
  position: relative;
  top: -1px;
}

.input, .multiline >>> .chunk-field {
  padding-inline: 1px;
  border: 1px solid;
}

.input, .multiline >>> .chunk-fixed {
  padding-inline: 1px;
  border: 1px dashed;
}

.input, .multiline >>> .chunk-empty {
  padding-inline: 1px;
}

.input, .multiline >>> .chunk-overlap {
  padding-inline: 1px;
  border: 1px solid grey;
  color: grey;
}

.input >>> .chunk-mismatch {
  padding-inline: 1px;
  border: 2px solid red !important;
}

</style>

<script>
import { getHSLColourFor } from '@/lib/hsl'

export default {
  name: "DougalFixedStringEncoderSample",

  components: {
  },

  mixins: [
    {
      methods: {
        getHSLColourFor
      }
    }
  ],

  props: {
    properties: { type: Object, default: () => ({}) },
    fields: { type: Array, default: () => [] },
    values: { type: Object, default: () => ({}) },
    readonly: Boolean,
    label: String,
    hint: String,
  },

  data () {
    return {
    }
  },

  computed: {

    chunks () {
      const properties = this.properties;
      const fields = this.fields;
      const values = this.values;
      const str = "";
      const chunks = [];

      for (const field of fields) {
        const value = this.fieldValue(properties, field, values);

        if (value != null) {
          const chunk = {
            start: field.offset,
            end: field.offset + field.length - 1,
            colour: this.getHSLColourFor(field.item),
            class: field.item == "text" ? "fixed" : "field",
            text: value
          }
          chunks.push(chunk);
        }
      }

      return chunks;
    },

    text () {
      return this.sample(this.properties, this.fields, this.values);
    },

    html () {
      return this.renderTextLine(this.text);
    }

  },

  watch: {
  },

  methods: {

    fieldValue (properties, field, values) {
      let value;

      if (field.item == "text") {
        value = field.value;
      } else if (properties[field.item]?.type == "boolean") {
        if (values[field.item] === field.when) {
          value = field.value;
        }
      } else {
        value = values[field.item];
      }

      if (value != null) {

        if (properties[field.item]?.type == "number") {
          if (field.scale_multiplier != null) {
            value *= field.scale_multiplier;
          }
          if (field.scale_offset != null) {
            value += field.scale_offset;
          }

          if (field.format == "integer") {
            value = Math.round(value);
          }
        }

        value = String(value);
        if (field.pad_side == "left") {
          value = value.padStart(field.length, field.pad_string ?? " ");
        } else if (field.pad_side == "right") {
          value = value.padEnd(field.length, field.pad_string ?? " ");
        }

        return value;
      }
    },

    sample (properties, fields, values, str = "") {

      const length = fields.reduce( (acc, cur) => (cur.offset + cur.length) > acc ? (cur.offset + cur.length) : acc, str.length )

      str = str.padEnd(length);

      for (const field of fields) {
        //console.log("FIELD", field);
        const value = this.fieldValue(properties, field, values);
        if (value != null) {
          str = str.slice(0, field.offset) + value + str.slice(field.offset + field.length);
        }
      }

      return str;
    },

    /** Return a `<span>` opening tag.
     */
    style (name, colour) {
      return colour
        ? `<span class="${name}" style="color:${colour};border-color:${colour}">`
        : `<span class="${name}">`;
    },

    /** Return an array of the intervals that intersect `pos`.
     * May be empty.
     */
    chunksFor (pos) {
      return this.chunks.filter( chunk =>
        pos >= chunk.start &&
        pos <= chunk.end
      )
    },

    /*
     * Algorithm:
     *
     * Go through every character of one line of text and determine in which
     * part(s) it falls in, if any. Collect adjacent same parts into <span/>
     * elements.
     */
    renderTextLine (text) {
      const parts = [];

      let prevStyle;

      for (const pos in text) {
        const chunks = this.chunksFor(pos);
        const isEmpty = chunks.length == 0;
        const isOverlap = chunks.length > 1;
        const isMismatch = chunks[0]?.text &&
          (text.substring(chunks[0].start, chunks[0].end+1) != chunks[0].text);

        const style = isEmpty
          ? this.style("chunk-empty")
          : isMismatch
            ? this.style("chunk-mismatch", chunks[0].colour)
            : isOverlap
              ? this.style("chunk-overlap")
              : this.style("chunk-"+chunks[0].class, chunks[0].colour);

        if (style != prevStyle) {
          if (prevStyle) {
            parts.push("</span>");
          }
          parts.push(style);
        }
        parts.push(text[pos]);
        prevStyle = style;
      }

      if (parts.length) {
        parts.push("</span>");
      }

      return parts.join("");
    },

  },

  mounted () {
  }

}
</script>
