<template>
	<div class="search-box">
		<input
			ref="input"
			aria-label="Search"
			:value="query"
			:class="{ focused: focused }"
			:placeholder="placeholder"
			class="custom-search"
			autocomplete="off"
			spellcheck="false"
			@input="query = $event.target.value"
			@focus="focused = true"
			@blur="focused = false"
			@keyup.enter="go(focusIndex)"
			@keyup.up="onUp"
			@keyup.down="onDown"
		/>
		<ul
			v-if="showSuggestions"
			class="suggestions"
			:class="{ 'align-right': alignRight }"
			style="z-index: 1"
			@mouseleave="unfocus"
		>
			<template v-if="this.suggestions && this.suggestions.length">
				<li
					v-for="(s, i) in suggestions"
					:key="i"
					class="suggestion"
					:class="{ focused: i === focusIndex }"
					@mousedown="go(i)"
					@mouseenter="focus(i)"
				>
					<a :href="s.path" @click.prevent>
						<div class="suggestion-item">
							<span class="page-title">{{ s.title || 'Document' }}</span>
							<span v-if="s.header" class="header">{{ s.header.title }}</span>
						</div>
					</a>
				</li>
			</template>
			<li v-else class="suggestion">
				<div class="suggestion-item">
					<span class="page-title">暂无结果</span>
				</div>
			</li>
		</ul>
	</div>
</template>

<script>
	import matchQuery from './match-query';
	/* global SEARCH_MAX_SUGGESTIONS, SEARCH_PATHS, SEARCH_HOTKEYS */
	export default {
		name: 'SearchBox',

		data() {
			return {
				query: '',
				focused: false,
				focusIndex: 0,
				placeholder: undefined,
				SEARCH_PATHS: null,
				SEARCH_HOTKEYS: ['s', '/'],
				SEARCH_MAX_SUGGESTIONS: 5,
			};
		},

		computed: {
			showSuggestions() {
				return this.focused && this.query && this.query.length; // && this.suggestions && this.suggestions.length;
			},

			suggestions() {
				const query = this.query.trim().toLowerCase();
				if (!query) {
					return;
				}

				const { pages } = this.$site;
				const max = this.$site.themeConfig.searchMaxSuggestions || this.SEARCH_MAX_SUGGESTIONS;
				const localePath = this.$localePath;
				const res = [];
				for (let i = 0; i < pages.length; i++) {
					if (res.length >= max) break;
					const p = pages[i];
					// filter out results that do not match current locale
					if (this.getPageLocalePath(p) !== localePath) {
						continue;
					}

					// filter out results that do not match searchable paths
					if (!this.isSearchable(p)) {
						continue;
					}

					if (matchQuery(query, p)) {
						res.push(p);
					} else if (p.headers) {
						for (let j = 0; j < p.headers.length; j++) {
							if (res.length >= max) break;
							const h = p.headers[j];
							if (h.title && matchQuery(query, p, h.title)) {
								res.push(
									Object.assign({}, p, {
										path: p.path + '#' + h.slug,
										header: h,
									})
								);
							}
						}
					}
				}
				return res;
			},

			// make suggestions align right when there are not enough items
			alignRight() {
				const navCount = (this.$site.themeConfig.nav || []).length;
				const repo = this.$site.repo ? 1 : 0;
				return navCount + repo <= 2;
			},
		},

		mounted() {
			this.placeholder = this.$site.themeConfig.searchPlaceholder || '';
			document.addEventListener('keydown', this.onHotkey);
		},

		beforeDestroy() {
			document.removeEventListener('keydown', this.onHotkey);
		},

		methods: {
			getPageLocalePath(page) {
				for (const localePath in this.$site.locales || {}) {
					if (localePath !== '/' && page.path.indexOf(localePath) === 0) {
						return localePath;
					}
				}
				return '/';
			},

			isSearchable(page) {
				let searchPaths = this.SEARCH_PATHS;

				// all paths searchables
				if (searchPaths === null) {
					return true;
				}

				searchPaths = Array.isArray(searchPaths) ? searchPaths : new Array(searchPaths);

				return (
					searchPaths.filter(path => {
						return page.path.match(path);
					}).length > 0
				);
			},

			onHotkey(event) {
				if (event.srcElement === document.body && this.SEARCH_HOTKEYS.includes(event.key)) {
					this.$refs.input.focus();
					event.preventDefault();
				}
			},

			onUp() {
				if (this.showSuggestions) {
					if (this.focusIndex > 0) {
						this.focusIndex--;
					} else {
						this.focusIndex = this.suggestions.length - 1;
					}
				}
			},

			onDown() {
				if (this.showSuggestions) {
					if (this.focusIndex < this.suggestions.length - 1) {
						this.focusIndex++;
					} else {
						this.focusIndex = 0;
					}
				}
			},

			go(i) {
				if (!this.showSuggestions) {
					return;
				}
				this.$router.push(this.suggestions[i].path);
				this.query = '';
				this.focusIndex = 0;
			},

			focus(i) {
				this.focusIndex = i;
			},

			unfocus() {
				this.focusIndex = -1;
			},
		},
	};
</script>

<style lang="stylus">
	.search-box
	  display inline-block
	  position relative
	  margin-right 1rem
	  input
	    cursor text
	    width 10rem
	    height: 2rem
	    color lighten($textColor, 25%)
	    display inline-block
	    border 1px solid darken($borderColor, 10%)
	    border-radius 2rem
	    font-size 0.9rem
	    line-height 2rem
	    padding 0 0.5rem 0 2rem
	    outline none
	    transition all .2s ease
	    background #fff url(search.svg) 0.6rem 0.5rem no-repeat
	    background-size 1rem
	    &:focus
	      cursor auto
	      border-color $accentColor
	  .suggestions
	    background #fff
	    width 20rem
	    position absolute
	    top 2 rem
	    border 1px solid darken($borderColor, 10%)
	    border-radius 6px
	    padding 0.4rem
	    list-style-type none
	    &.align-right
	      right 0
	  .suggestion
	    line-height 1.4
	    padding 0.4rem 0.6rem
	    border-radius 4px
	    cursor pointer
	    a
	      white-space normal
	      color lighten($textColor, 35%)
	      .page-title
	        font-weight 600
	      .header
	        font-size 0.9em
	        margin-left 0.25em
	    &.focused
	      background-color #f3f4f5
	      a
	        color $accentColor

	@media (max-width: $MQNarrow)
	  .search-box
	    input
	      cursor pointer
	      width 0
	      border-color transparent
	      position relative
	      &:focus
	        cursor text
	        left 0
	        width 10rem

	// Match IE11
	@media all and (-ms-high-contrast: none)
	  .search-box input
	    height 2rem

	@media (max-width: $MQNarrow) and (min-width: $MQMobile)
	  .search-box
	    .suggestions
	      left 0

	@media (max-width: $MQMobile)
	  .search-box
	    margin-right 0
	    input
	      left 1rem
	    .suggestions
	      right 0

	@media (max-width: $MQMobileNarrow)
	  .search-box
	    .suggestions
	      width calc(100vw - 4rem)
	    input:focus
	      width 8rem
	// by Lxh
	.search-box
		.suggestions
			width 30rem
		.suggestion
			$li-border-color = #f0f0f0
			border 1px solid $li-border-color
			border-radius 0
			padding 0
			&:not(:last-child)
				border-bottom none
			&:first-child
				border-top-left-radius 6px
				border-top-right-radius 6px
			&:last-child
				border-bottom 1px solid $li-border-color
				border-bottom-left-radius 6px
				border-bottom-right-radius 6px
			a
				width 100%
			.suggestion-item
				width 100%
				display flex
				.page-title
					flex 1
					width 9rem
					background-color #f7f7f7
					padding 10px
					display flex
					flex-direction column
					justify-content center
					text-align center
					word-break break-all
				.header
					flex 2
					padding-left 20px
					display flex
					flex-direction column
					justify-content center
					text-align left
</style>
