<script setup>

import "@/lib/array.js"
import {onMounted, ref, shallowRef, watch} from "vue"
import TCheckboxBinary from "@/components/ui/TCheckboxBinary.vue";
import ValuesBox from "@/components/common/ValuesBox.vue";
import TSelect from "@/components/ui/TSelect.vue";
import TOption from "@/components/ui/TOption.vue";
import TInputText from "@/components/ui/TInputText.vue";
import MoreOptionsIndicator from "@/components/common/MoreOptionsIndicator.vue";
import SizeCapacityBox from "@/components/common/SizeCapacityBox.vue";
import CodeLabel from "@/components/common/CodeLabel.vue";
import TimeDurationBox from "@/components/common/TimeDurationBox.vue";
import "@/configs/definitions.js"
import RequestVariablesDescriber from "@/components/common/RequestVariablesDescriber.vue";
import HTTPExpiresTimeConfigBox from "@/components/server/HTTPExpiresTimeConfigBox.vue";
import HTTPRequestCondsBox from "@/components/server/HTTPRequestCondsBox.vue";
import HTTPCondURLExtension from "@/components/server/HTTPCondURLExtension.vue";
import HTTPCondURLEqIndex from "@/components/server/HTTPCondURLEqIndex.vue";
import HTTPCondURLAll from "@/components/server/HTTPCondURLAll.vue";
import HTTPCondURLPrefix from "@/components/server/HTTPCondURLPrefix.vue";
import HTTPCondURLEq from "@/components/server/HTTPCondURLEq.vue";
import HTTPCondURLRegexp from "@/components/server/HTTPCondURLRegexp.vue";
import HTTPCondURLWildcardMatch from "@/components/server/HTTPCondURLWildcardMatch.vue";
import HTTPCondUserAgentRegexp from "@/components/server/HTTPCondUserAgentRegexp.vue";
import HTTPCondParams from "@/components/server/HTTPCondParams.vue";
import HTTPCondURLNotExtension from "@/components/server/HTTPCondURLNotExtension.vue";
import HTTPCondURLNotPrefix from "@/components/server/HTTPCondURLNotPrefix.vue";
import HTTPCondURLNotEq from "@/components/server/HTTPCondURLNotEq.vue";
import HTTPCondURLNotRegexp from "@/components/server/HTTPCondURLNotRegexp.vue";
import HTTPCondMimeType from "@/components/server/HTTPCondMimeType.vue";
import HTTPCondUserAgentNotRegexp from "@/components/server/HTTPCondUserAgentNotRegexp.vue";

const props = defineProps(["v-cache-ref", "v-is-reverse"])

const variablesDescriberRef = ref()

// compose component
window.REQUEST_COND_COMPONENTS.forEach(c => {
	switch (c.component) {
		case "http-cond-url-extension":
			c.component = shallowRef(HTTPCondURLExtension)
			break
		case "http-cond-url-eq-index":
			c.component = shallowRef(HTTPCondURLEqIndex)
			break
		case "http-cond-url-all":
			c.component = shallowRef(HTTPCondURLAll)
			break
		case "http-cond-url-prefix":
			c.component = shallowRef(HTTPCondURLPrefix)
			break
		case "http-cond-url-eq":
			c.component = shallowRef(HTTPCondURLEq)
			break
		case "http-cond-url-regexp":
			c.component = shallowRef(HTTPCondURLRegexp)
			break
		case "http-cond-url-wildcard-match":
			c.component = shallowRef(HTTPCondURLWildcardMatch)
			break
		case "http-cond-user-agent-regexp":
			c.component = shallowRef(HTTPCondUserAgentRegexp)
			break
		case "http-cond-params":
			c.component = shallowRef(HTTPCondParams)
			break
		case "http-cond-url-not-extension":
			c.component = shallowRef(HTTPCondURLNotExtension)
			break
		case "http-cond-url-not-prefix":
			c.component = shallowRef(HTTPCondURLNotPrefix)
			break
		case "http-cond-url-not-eq":
			c.component = shallowRef(HTTPCondURLNotEq)
			break
		case "http-cond-url-not-regexp":
			c.component = shallowRef(HTTPCondURLNotRegexp)
			break
		case "http-cond-user-agent-not-regexp":
			c.component = shallowRef(HTTPCondUserAgentNotRegexp)
			break
		case "http-cond-mime-type":
			c.component = shallowRef(HTTPCondMimeType)
			break
	}
})

const {
	cacheRef,
	keyIgnoreArgs,
	moreOptionsVisible,
	condCategory,
	condType,
	condComponent,
	condIsCaseInsensitive,
	components
} = (function () {
	let cacheRef = props.vCacheRef
	if (cacheRef == null) {
		cacheRef = {
			isOn: true,
			cachePolicyId: 0,
			key: "${scheme}://${host}${requestPath}${isArgs}${args}",
			life: {count: 1, unit: "day"},
			status: [200],
			maxSize: {count: 128, unit: "mb"},
			minSize: {count: 0, unit: "kb"},
			skipCacheControlValues: ["private", "no-cache", "no-store"],
			skipSetCookie: true,
			enableRequestCachePragma: false,
			conds: null, // 复杂条件
			simpleCond: null, // 简单条件
			allowChunkedEncoding: true,
			allowPartialContent: true,
			forcePartialContent: false,
			enableIfNoneMatch: false,
			enableIfModifiedSince: false,
			enableReadingOriginAsync: false,
			isReverse: props.vIsReverse,
			methods: [],
			expiresTime: {
				isPrior: false,
				isOn: false,
				overwrite: true,
				autoCalculate: true,
				duration: {count: -1, "unit": "hour"}
			}
		}
	}
	if (cacheRef.key == null) {
		cacheRef.key = ""
	}
	if (cacheRef.methods == null) {
		cacheRef.methods = []
	}

	if (cacheRef.life == null) {
		cacheRef.life = {count: 2, unit: "hour"}
	}
	if (cacheRef.maxSize == null) {
		cacheRef.maxSize = {count: 32, unit: "mb"}
	}
	if (cacheRef.minSize == null) {
		cacheRef.minSize = {count: 0, unit: "kb"}
	}

	let condType = "url-extension"
	let condComponent = window.REQUEST_COND_COMPONENTS.$find(function (k, v) {
		return v.type == "url-extension"
	})

	return {
		cacheRef: ref(cacheRef),

		keyIgnoreArgs: ref(typeof cacheRef.key == "string" && cacheRef.key.indexOf("${args}") < 0),

		moreOptionsVisible: ref(false),

		condCategory: ref("simple"), // 条件分类：simple|complex
		condType: ref(condType),
		condComponent: ref(condComponent),
		condIsCaseInsensitive: ref((cacheRef.simpleCond != null) ? cacheRef.simpleCond.isCaseInsensitive : true),

		components: ref(window.REQUEST_COND_COMPONENTS)
	}
})()

const changeOptionsVisible = function (v) {
	moreOptionsVisible.value = v
}

const changeLife = function (v) {
	cacheRef.value.life = v
}

const changeMaxSize = function (v) {
	cacheRef.value.maxSize = v
}

const changeMinSize = function (v) {
	cacheRef.value.minSize = v
}

const changeConds = function (v) {
	cacheRef.value.conds = v
	cacheRef.value.simpleCond = null
}

const changeStatusList = function (list) {
	let result = []
	list.forEach(function (status) {
		let statusNumber = parseInt(status)
		if (isNaN(statusNumber) || statusNumber < 100 || statusNumber > 999) {
			return
		}
		result.push(statusNumber)
	})
	cacheRef.value.status = result
}

const changeMethods = function (methods) {
	cacheRef.value.methods = methods.map(function (v) {
		return v.toUpperCase()
	})
}

const changeKey = function (key) {
	variablesDescriberRef.value.update(key)
}

const changeExpiresTime = function (expiresTime) {
	cacheRef.value.expiresTime = expiresTime
}

// 切换条件类型
const changeCondCategory = function (newCondCategory) {
	condCategory.value = newCondCategory

	// TODO 重新实现

	// resize window
	let dialog = window.parent.document.querySelector("*[role='dialog']")
	if (dialog == null) {
		return
	}
	switch (condCategory.value) {
		case "simple":
			dialog.style.width = "45em"
			break
		case "complex":
			let width = window.innerWidth
			if (width > 1024) {
				width = 1024
			}

			dialog.style.width = width + "px"
			if (cacheRef.value.conds != null) {
				cacheRef.value.conds.isOn = true
			}
			break
	}
}

const changeCondType = function (condType, isInit) {
	if (!isInit && cacheRef.value.simpleCond != null) {
		cacheRef.value.simpleCond.value = null
	}
	let def = components.value.$find(function (k, component) {
		return component.type == condType
	})
	if (def != null) {
		condComponent.value = def
	}
}

watch(keyIgnoreArgs, function (b) {
	if (typeof cacheRef.value.key != "string") {
		return
	}
	if (b) {
		cacheRef.value.key = cacheRef.value.key.replace("${isArgs}${args}", "")
		return;
	}
	if (cacheRef.value.key.indexOf("${isArgs}") < 0) {
		cacheRef.value.key = cacheRef.value.key + "${isArgs}"
	}
	if (cacheRef.value.key.indexOf("${args}") < 0) {
		cacheRef.value.key = cacheRef.value.key + "${args}"
	}
})

onMounted(() => {
	variablesDescriberRef.value.update(cacheRef.value.key)
	if (cacheRef.value.simpleCond != null) {
		condType.value = cacheRef.value.simpleCond.type
		changeCondType(cacheRef.value.simpleCond.type, true)
		condCategory.value = "simple"
	} else if (cacheRef.value.conds != null && cacheRef.value.conds.groups != null) {
		condCategory.value = "complex"
	}
	changeCondCategory(condCategory.value)
})

</script>

<template>
	<tbody>
	<tr v-if="condCategory == 'simple'">
		<td class="title">缓存对象 *</td>
		<td>
			<TSelect name="condType" v-model="condType" @change="changeCondType(condType, false)">
				<TOption value="url-extension">文件扩展名</TOption>
				<TOption value="url-eq-index">首页</TOption>
				<TOption value="url-all">全站</TOption>
				<TOption value="url-prefix">URL目录前缀</TOption>
				<TOption value="url-eq">URL完整路径</TOption>
				<TOption value="url-wildcard-match">URL通配符</TOption>
				<TOption value="url-regexp">URL正则匹配</TOption>
				<TOption value="params">参数匹配</TOption>
			</TSelect>
			<p class="comment !mt-4">
				<a href="" @click.prevent="changeCondCategory('complex')">切换到复杂条件 &raquo;</a>
			</p>
		</td>
	</tr>
	<tr v-if="condCategory == 'simple'">
		<td>{{ condComponent.paramsTitle }} *</td>
		<td>
			<component :is="condComponent.component" :v-cond="cacheRef.simpleCond" v-if="condComponent.type != 'params'"></component>
			<table class="ui table definition" v-if="condComponent.type == 'params'">
				<component :is="condComponent.component" :v-cond="cacheRef.simpleCond"></component>
			</table>
		</td>
	</tr>
	<tr v-if="condCategory == 'simple' && condComponent.caseInsensitive">
		<td>不区分大小写</td>
		<td>
			<TCheckboxBinary name="condIsCaseInsensitive" value="1" v-model="condIsCaseInsensitive"/>
			<p class="comment">选中后表示对比时忽略参数值的大小写。</p>
		</td>
	</tr>
	<tr v-if="condCategory == 'complex'">
		<td class="title">匹配条件分组 *</td>
		<td>
			<HTTPRequestCondsBox :v-conds="cacheRef.conds" @change="changeConds"></HTTPRequestCondsBox>
			<p class="comment !mt-4"><a href="" @click.prevent="changeCondCategory('simple')">&laquo; 切换到简单条件</a></p>
		</td>
	</tr>
	<tr v-show="!vIsReverse">
		<td>缓存有效期 *</td>
		<td>
			<TimeDurationBox :v-value="cacheRef.life" @change="changeLife" :v-min-unit="'minute'" maxlength="4"></TimeDurationBox>
		</td>
	</tr>
	<tr v-show="!vIsReverse">
		<td>忽略URI参数</td>
		<td>
			<TCheckboxBinary v-model="keyIgnoreArgs"></TCheckboxBinary>
			<p class="comment">选中后，表示缓存Key中不包含URI参数（即问号（?））后面的内容。</p>
		</td>
	</tr>
	<tr v-show="!vIsReverse">
		<td colspan="2">
			<MoreOptionsIndicator @change="changeOptionsVisible"></MoreOptionsIndicator>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>缓存Key *</td>
		<td>
			<TInputText type="text" v-model="cacheRef.key" @input="changeKey(cacheRef.key)" class="w-full"/>
			<p class="comment">用来区分不同缓存内容的唯一Key。
				<RequestVariablesDescriber ref="variablesDescriberRef"></RequestVariablesDescriber>
				。
			</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>请求方法限制</td>
		<td>
			<ValuesBox size="5" maxlength="10" :values="cacheRef.methods" @change="changeMethods"></ValuesBox>
			<p class="comment">允许请求的缓存方法，默认支持所有的请求方法。</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>客户端过期时间<em>（Expires）</em></td>
		<td>
			<HTTPExpiresTimeConfigBox :v-expires-time="cacheRef.expiresTime" @change="changeExpiresTime"></HTTPExpiresTimeConfigBox>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>可缓存的最大内容尺寸</td>
		<td>
			<SizeCapacityBox :v-value="cacheRef.maxSize" @change="changeMaxSize"></SizeCapacityBox>
			<p class="comment">内容尺寸如果高于此值则不缓存。</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>可缓存的最小内容尺寸</td>
		<td>
			<SizeCapacityBox :v-value="cacheRef.minSize" @change="changeMinSize"></SizeCapacityBox>
			<p class="comment">内容尺寸如果低于此值则不缓存。</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>支持缓存分片内容</td>
		<td>
			<TCheckboxBinary name="allowPartialContent" value="1" v-model="cacheRef.allowPartialContent"></TCheckboxBinary>
			<p class="comment">选中后，支持缓存源站返回的某个分片的内容，该内容通过
				<CodeLabel>206 Partial Content</CodeLabel>
				状态码返回。
			</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse && cacheRef.allowPartialContent && !cacheRef.alwaysForwardRangeReques">
		<td>强制返回分片内容</td>
		<td>
			<TCheckboxBinary name="forcePartialContent" value="1" v-model="cacheRef.forcePartialContent"></TCheckboxBinary>
			<p class="comment">选中后，表示无论客户端是否发送
				<CodeLabel>Range</CodeLabel>
				报头，都会优先尝试返回已缓存的分片内容；如果你的应用有不支持分片内容的客户端（比如有些下载软件不支持
				<CodeLabel>206 Partial Content</CodeLabel>
				），请务必关闭此功能。
			</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>强制Range回源</td>
		<td>
			<TCheckboxBinary v-model="cacheRef.alwaysForwardRangeRequest"></TCheckboxBinary>
			<p class="comment">选中后，表示把所有包含Range报头的请求都转发到源站，而不是尝试从缓存中读取。</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>状态码列表</td>
		<td>
			<ValuesBox name="statusList" size="3" maxlength="3" :values="cacheRef.status" @change="changeStatusList"></ValuesBox>
			<p class="comment">允许缓存的HTTP状态码列表。</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>跳过的<br/>Cache-Control值</td>
		<td>
			<ValuesBox name="skipResponseCacheControlValues" size="10" maxlength="100" :values="cacheRef.skipCacheControlValues"></ValuesBox>
			<p class="comment">当响应的Cache-Control为这些值时不缓存响应内容，而且不区分大小写。</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>跳过Set-Cookie</td>
		<td>
			<TCheckboxBinary value="1" v-model="cacheRef.skipSetCookie"/>
			<p class="comment">选中后，当响应的报头中有Set-Cookie时不缓存响应内容，防止动态内容被缓存。</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>支持请求<br/>no-cache刷新</td>
		<td>
			<TCheckboxBinary name="enableRequestCachePragma" value="1" v-model="cacheRef.enableRequestCachePragma"/>
			<p class="comment">选中后，当请求的报头中含有Pragma: no-cache或Cache-Control: no-cache时，会跳过缓存直接读取源内容，一般仅用于调试。</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>允许<br/>If-None-Match回源</td>
		<td>
			<TCheckboxBinary v-model="cacheRef.enableIfNoneMatch"></TCheckboxBinary>
			<p class="comment">特殊情况下才需要开启，可能会降低缓存命中率。</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>允许<br/>If-Modified-Since回源</td>
		<td>
			<TCheckboxBinary v-model="cacheRef.enableIfModifiedSince"></TCheckboxBinary>
			<p class="comment">特殊情况下才需要开启，可能会降低缓存命中率。</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>允许异步读取源站</td>
		<td>
			<TCheckboxBinary v-model="cacheRef.enableReadingOriginAsync"></TCheckboxBinary>
			<p class="comment">试验功能。允许客户端中断连接后，仍然继续尝试从源站读取内容并缓存。</p>
		</td>
	</tr>
	<tr v-show="moreOptionsVisible && !vIsReverse">
		<td>支持分段内容</td>
		<td>
			<TCheckboxBinary name="allowChunkedEncoding" value="1" v-model="cacheRef.allowChunkedEncoding"></TCheckboxBinary>
			<p class="comment">选中后，Gzip等压缩后的Chunked内容可以直接缓存，无需检查内容长度。</p>
		</td>
	</tr>
	<tr v-show="false">
		<td colspan="2"><input type="hidden" name="cacheRefJSON" :value="JSON.stringify(cacheRef)"/></td>
	</tr>
	</tbody>
</template>

<style scoped>
</style>
