<script setup>

import "@/lib/array.js"
import {inject, onMounted, ref} from "vue"
import HTTPRequestCondsBox from "@/components/server/HTTPRequestCondsBox.vue";
import URLPatternsBox from "@/components/common/URLPatternsBox.vue";
import SizeCapacityBox from "@/components/common/SizeCapacityBox.vue";
import TCheckboxBinary from "@/components/ui/TCheckboxBinary.vue";
import PriorCheckbox from "@/components/common/PriorCheckbox.vue";
import ValuesBox from "@/components/common/ValuesBox.vue";
import MoreOptionsTbody from "@/components/common/MoreOptionsTbody.vue";
import TDivider from "@/components/ui/TDivider.vue";
import Sortable from "sortablejs";
import DefinitionTable from "@/components/common/DefinitionTable.vue";

const utils = inject("$utils")

const props = defineProps(["v-compression-config", "v-is-location", "v-is-group"])

const {config, moreOptionsVisible, allTypes} = (function () {
	let config = props.vCompressionConfig
	if (config == null) {
		config = {
			isPrior: false,
			isOn: false,
			useDefaultTypes: true,
			types: ["zstd", "brotli", "gzip", "deflate"],
			level: 0,
			decompressData: false,
			gzipRef: null,
			deflateRef: null,
			brotliRef: null,
			minLength: {count: 1, "unit": "kb"},
			maxLength: {count: 32, "unit": "mb"},
			mimeTypes: ["text/*", "application/javascript", "application/json", "application/atom+xml", "application/rss+xml", "application/xhtml+xml", "image/svg+xml"],
			extensions: [".js", ".json", ".html", ".htm", ".xml", ".css", ".woff2", ".txt"],
			exceptExtensions: [".apk", ".ipa"],
			conds: null,
			enablePartialContent: false,
			onlyURLPatterns: [],
			exceptURLPatterns: []
		}
	}

	if (config.types == null) {
		config.types = []
	}
	if (config.mimeTypes == null) {
		config.mimeTypes = []
	}
	if (config.extensions == null) {
		config.extensions = []
	}

	let allTypes = [
		{
			name: "Gzip",
			code: "gzip",
			isOn: true
		},
		{
			name: "Deflate",
			code: "deflate",
			isOn: true
		},
		{
			name: "Brotli",
			code: "brotli",
			isOn: true
		},
		{
			name: "ZSTD",
			code: "zstd",
			isOn: true
		}
	]

	let configTypes = []
	config.types.forEach(function (typeCode) {
		allTypes.forEach(function (t) {
			if (typeCode == t.code) {
				t.isOn = true
				configTypes.push(t)
			}
		})
	})
	allTypes.forEach(function (t) {
		if (!config.types.$contains(t.code)) {
			t.isOn = false
			configTypes.push(t)
		}
	})

	return {
		config: ref(config),
		moreOptionsVisible: ref(false),
		allTypes: ref(configTypes)
	}
})()

const compressionTypesBoxRef = ref()

const isOn = function () {
	return ((!props.vIsLocation && !props.vIsGroup) || config.value.isPrior) && config.value.isOn
}

const changeExtensions = function (values) {
	values.forEach(function (v, k) {
		if (v.length > 0 && v[0] != ".") {
			values[k] = "." + v
		}
	})
	config.value.extensions = values
}

const changeExceptExtensions = function (values) {
	values.forEach(function (v, k) {
		if (v.length > 0 && v[0] != ".") {
			values[k] = "." + v
		}
	})
	config.value.exceptExtensions = values
}

const changeMimeTypes = function (values) {
	config.value.mimeTypes = values
}

const changeAdvancedVisible = function () {
	moreOptionsVisible.value = !moreOptionsVisible.value
}

const changeConds = function (conds) {
	config.value.conds = conds
}

const changeType = function () {
	config.value.types = []
	allTypes.value.forEach(function (v) {
		if (v.isOn) {
			config.value.types.push(v.code)
		}
	})
}

const initSortableTypes = function () {
	Sortable.create(compressionTypesBoxRef.value, {
		draggable: ".checkbox-wrapper",
		handle: ".icon.handle",
		chosenClass: "dragging",
		ghostClass: "placeholder",
		onStart: function () {

		},
		onUpdate: function (event) {
			utils.moveElement(allTypes.value, event.oldDraggableIndex, event.newDraggableIndex)

			const newTypes = []
			allTypes.value.forEach(v => {
				if (v.isOn) {
					newTypes.push(v.code)
				}
			})
			config.value.types = newTypes
		}
	})
}

onMounted(() => {
	initSortableTypes()
})

</script>

<template>
	<div>
		<input type="hidden" name="compressionJSON" :value="JSON.stringify(config)"/>
		<DefinitionTable>
			<PriorCheckbox :v-config="config" v-if="vIsLocation || vIsGroup"></PriorCheckbox>
			<tbody v-show="(!vIsLocation && !vIsGroup) || config.isPrior">
			<tr>
				<td class="title">启用内容压缩</td>
				<td>
					<TCheckboxBinary value="1" v-model="config.isOn"/>
				</td>
			</tr>
			</tbody>
			<tbody v-show="isOn()">
			<tr>
				<td>支持的扩展名</td>
				<td>
					<ValuesBox :values="config.extensions" @change="changeExtensions" placeholder="比如 .html"></ValuesBox>
					<p class="comment">含有这些扩展名的URL将会被压缩，不区分大小写。</p>
				</td>
			</tr>
			<tr>
				<td>例外扩展名</td>
				<td>
					<ValuesBox :values="config.exceptExtensions" @change="changeExceptExtensions" placeholder="比如 .html"></ValuesBox>
					<p class="comment">含有这些扩展名的URL将<strong>不会</strong>被压缩，不区分大小写。</p>
				</td>
			</tr>
			<tr>
				<td>支持的MimeType</td>
				<td>
					<ValuesBox :values="config.mimeTypes" @change="changeMimeTypes" placeholder="比如 text/*"></ValuesBox>
					<p class="comment">响应的Content-Type里包含这些MimeType的内容将会被压缩。</p>
				</td>
			</tr>
			</tbody>
			<MoreOptionsTbody @change="changeAdvancedVisible" v-if="isOn()"></MoreOptionsTbody>
			<tbody v-show="isOn() && moreOptionsVisible">
			<tr>
				<td>压缩算法</td>
				<td>
					<div>
						<TCheckboxBinary v-model="config.useDefaultTypes">
							<span v-if="config.useDefaultTypes">使用默认顺序<span class="grey small">（zstd、brtoli、gzip、 deflate）</span></span>
							<span v-if="!config.useDefaultTypes">使用默认顺序</span>
						</TCheckboxBinary>
					</div>
					<div v-show="!config.useDefaultTypes">
						<TDivider></TDivider>
						<div ref="compressionTypesBoxRef" class="label-group !gap-4">
							<div class="checkbox-wrapper" v-for="t in allTypes" :key="t.code">
								<TCheckboxBinary v-model="t.isOn" :id="'compression-type-' + t.code" @change="changeType">
									<span>{{ t.name }} &nbsp;
										<i class="icon bars small grey handle"></i></span>
								</TCheckboxBinary>
							</div>
						</div>
					</div>

					<p class="comment" v-show="!config.useDefaultTypes">选择支持的压缩算法和优先顺序，拖动<i class="icon bars small grey"></i>图标排序。
					</p>
				</td>
			</tr>
			<tr>
				<td>支持已压缩内容</td>
				<td>
					<TCheckboxBinary v-model="config.decompressData"></TCheckboxBinary>
					<p class="comment">支持对已压缩内容尝试重新使用新的算法压缩；不选中表示保留当前的压缩格式。</p>
				</td>
			</tr>
			<tr>
				<td>内容最小长度</td>
				<td>
					<SizeCapacityBox :v-name="'minLength'" :v-value="config.minLength" :v-unit="'kb'"></SizeCapacityBox>
					<p class="comment">0表示不限制，内容长度从文件尺寸或Content-Length中获取。</p>
				</td>
			</tr>
			<tr>
				<td>内容最大长度</td>
				<td>
					<SizeCapacityBox :v-name="'maxLength'" :v-value="config.maxLength" :v-unit="'mb'"></SizeCapacityBox>
					<p class="comment">0表示不限制，内容长度从文件尺寸或Content-Length中获取。</p>
				</td>
			</tr>
			<tr>
				<td>支持Partial<br/>Content</td>
				<td>
					<TCheckboxBinary v-model="config.enablePartialContent"></TCheckboxBinary>
					<p class="comment">支持对分片内容（PartialContent）的压缩；除非客户端有特殊要求，一般不需要启用。</p>
				</td>
			</tr>
			<tr>
				<td>例外URL</td>
				<td>
					<URLPatternsBox v-model="config.exceptURLPatterns"></URLPatternsBox>
					<p class="comment">如果填写了例外URL，表示这些URL跳过不做处理。</p>
				</td>
			</tr>
			<tr>
				<td>限制URL</td>
				<td>
					<URLPatternsBox v-model="config.onlyURLPatterns"></URLPatternsBox>
					<p class="comment">如果填写了限制URL，表示只对这些URL进行压缩处理；如果不填则表示支持所有的URL。</p>
				</td>
			</tr>
			<tr>
				<td>匹配条件</td>
				<td>
					<HTTPRequestCondsBox :v-conds="config.conds" @change="changeConds"></HTTPRequestCondsBox>
				</td>
			</tr>
			</tbody>
		</DefinitionTable>
	</div>
</template>

<style scoped>
.checkbox-wrapper {
	@apply py-1;

	:deep(&.dragging) {
		@apply bg-gray-100 dark:bg-gray-700;
	}

	:deep(&.placeholder) {
		@apply border-[1px];
		border-color: var(--p-primary-color);
		@apply bg-white;
	}
}
</style>
