<template>
	<div class="input-group">
		<span v-if="label" class="input-group-text">{{label}}</span>
		<select v-if="selectable_dgnm" class="form-select" style="flex: 0.15 1 auto;" v-model="x_dgnm">
			<option v-for="item in ORDERABLE_DGNM" :value="item.dgnm">{{item.name}}</option>
		</select>
		<input class="form-control" placeholder="Filter..." v-model="x_query" />
	</div>
	<div class="scroller" ref="scroller">
		<table class="table table-striped">
			<tbody>
				<tr v-for="item in resultset" :class="{ 'table-active': item.IEN == x_modelValue }" @click="x_modelValue = item.IEN">
					<td>{{item.IEN}}</td>
					<td>{{item.description}}</td>
				</tr>
				<tr ref="bottom" />
			</tbody>
		</table>
	</div>
</template>

<style scoped>
	div.scroller {
		max-height: 25vh;
		overflow-y: auto;
	}
	td {
		cursor: default;
	}
	.table-active, .table-active:nth-of-type(odd) > * {
		color: #fff;
		background-color: #0d6efd;
	}
</style>

<script>
	import { debounce } from './util.mjs';

	import { ORDERABLE_DGNM } from './constants.mjs';

	const SZ_WINDOW = 100;

	export default {
		props: {
			client: Object,
			dgnm: String,
			label: String,
			query: String,
			modelValue: String
		},
		emits: {
			'update:dgnm': String,
			'update:query': String,
			'update:modelValue': String
		},
		data() {
			return {
				ORDERABLE_DGNM,
				selectable_dgnm: !this.dgnm,
				x_dgnm: this.dgnm || 'O RX',
				IEN: null,
				count: null,
				x_query: this.query,
				resultset: [],
				end: null,
				has_more: false,
				is_loading: true,
				observer_bottom: null,
				x_modelValue: this.modelValue
			};
		},
		computed: {
			query_view() {
				return this.x_query ? this.x_query.replace(/^\s+$/g, '').replace(/\s+/g, ' ').toUpperCase() : '';	// allow trailing space
			}
		},
		watch: {
			dgnm(value) { this.x_dgnm = value; },
			x_dgnm(value) {
				this.$emit('update:dgnm', value);
				this.$emit('update:query', this.x_query = '');
			},
			modelValue(value) { this.x_modelValue = value; },
			x_modelValue(value) { this.$emit('update:modelValue', value); },
			query(value) { this.x_query = value; },
			x_query(value) {
				this.$emit('update:query', value);
			}
		},
		methods: {
			async handle_bottom([entry]) {
				if((entry.isIntersecting) && (this.has_more) && (this.end < this.count) && (!this.is_loading)) {
					this.is_loading = true;
					this.has_more = false;
					try {
						var batch = await this.client.ORWUL_FVSUB(this.IEN, this.end + 1, this.end = Math.min(this.end + SZ_WINDOW, this.count));
						if(this.query_view.length >= 3) batch = batch.filter(x => x.description.startsWith(this.query_view));
						if(batch.length > 0) {
							Array.prototype.push.apply(this.resultset, batch);
							this.has_more = true;
						} else this.has_more = false;
					} catch(ex) {
						this.has_more = false;
					} finally {
						this.is_loading = false;
					}
				}
			}
		},
		created() {
			this.$watch(
				() => (this.client, this.dgnm, {}),
				async () => {
					if((this.client) && (this.dgnm)) {
						var res = await this.client.ORWUL_FV4DG(this.dgnm);
						this.IEN = res.IEN;
						this.count = res.count;
					} else this.IEN = this.count = null;
				},
				{ immediate: true }
			);
			this.$watch(
				() => (this.client, this.IEN, this.query_view, {}),
				debounce(async function() {
					if((this.client) && (this.IEN)) {
						this.is_loading = true;
						this.has_more = false;
						try {
							var query = this.query_view;
							if(query.length >= 3) {
								var row = await this.client.ORWUL_FVIDX(this.IEN, query.slice(0, -1) + String.fromCharCode(query.charCodeAt(query.length - 1) - 1) + '~');
								var pos = Math.floor((row.index - 1)/SZ_WINDOW)*SZ_WINDOW;
								var batch = await this.client.ORWUL_FVSUB(this.IEN, pos + 1, this.end = Math.min(pos + SZ_WINDOW, this.count));
								this.resultset = batch.filter(x => x.description.startsWith(query));
							} else this.resultset = await this.client.ORWUL_FVSUB(this.IEN, 1, this.end = Math.min(SZ_WINDOW, this.count));
							this.has_more = this.resultset.length > 0;
						} catch(ex) {
							this.resultset = [];
							this.has_more = false;
						} finally {
							this.is_loading = false;
							this.$refs.scroller.scrollTo(0, 0);
						}
					}
				}, 500),
				{ immediate: true }
			);
		},
		mounted() {
			this.observer_bottom = new IntersectionObserver(this.handle_bottom, { root: this.$refs.scroller, rootMargin: '25%' });
			this.observer_bottom.observe(this.$refs.bottom);
		},
		destroyed() {
			if(this.observer_bottom) this.observer_bottom.disconnect();
		}
	};
</script>
