import type { Vue } from 'vue-facing-decorator';
import SwiperCore from 'swiper';
import { Navigation, Pagination, Autoplay, FreeMode } from 'swiper/modules';
import { AutoplayOptions } from 'swiper/types';
import { Prop, toNative } from 'vue-facing-decorator';
import TsxComponent, { Component } from '../../app/vuetsx';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import './css/swiper.css';

type VueType = typeof Vue.prototype;
interface IVue extends VueType { }
SwiperCore.use([
	Navigation,
	Pagination,
	Autoplay,
	FreeMode,
]);

interface SwiperArgs {
	hasPagination?: boolean;
	direction?: 'horizontal' | 'vertical';
	slidesPerView?: 'auto' | number;
	slidesPerGroup?: number;
	spaceBetween?: number;
	freeMode?: boolean;
	clickable?: boolean;
	grabCursor?: boolean;
	speed?: number;
	autoplay?: AutoplayOptions;
	preventClicks?: boolean;
	navigation?: { nextEl: () => HTMLElement | string | IVue; prevEl: () => HTMLElement | string | IVue };
	slideChanged?: (e?: SwiperSlideChangedArgs) => void;
	zoomChanged?: (scale: number) => void;
	zoom?: boolean | SwiperZoomArgs;

}

export interface SwiperSlideChangedArgs {
	activeIndex: number;
	previousIndex: number;
	swiper: any;
}

export interface SwiperZoomArgs {
	enabled: boolean;
	maxRatio?: number;
	minRatio?: number;
}

@Component
class SwiperComponent extends TsxComponent<SwiperArgs> implements SwiperArgs {
	@Prop() hasPagination?: boolean;
	@Prop() direction?: 'horizontal' | 'vertical';
	@Prop() slidesPerView?: 'auto' | number;
	@Prop() slidesPerGroup?: number;
	@Prop() spaceBetween?: number;
	@Prop() freeMode?: boolean;
	@Prop() clickable?: boolean;
	@Prop() grabCursor?: boolean;
	@Prop() speed: number;
	@Prop() autoplay: AutoplayOptions;
	@Prop() preventClicks: boolean;
	@Prop() navigation?: { nextEl: () => HTMLElement | string | IVue; prevEl: () => HTMLElement | string | IVue };
	@Prop() zoom?: boolean | SwiperZoomArgs;
	@Prop() slideChanged?: (e?: SwiperSlideChangedArgs) => void;
	@Prop() zoomChanged?: (scale: number) => void;
	zoomed: boolean = false;

	mounted() {
		const rootElem = this.$el;
		if (rootElem != null) {
			const existingInstance = (rootElem as any).swiper;
			if (existingInstance == null) {
				const args: any = {
					zoom: this.getZoomArgs(),
					direction: this.direction || 'horizontal',
					spaceBetween: this.spaceBetween > 0 ? this.spaceBetween : 0,
					pagination: this.shouldRenderPagination()
						? {
							el: this.$refs.swiperPagination,
							type: 'bullets',
							clickable: this.clickable
						}
						: {
							el: null,
						},
				};

				if (this.slidesPerView != null) {
					args.slidesPerView = this.slidesPerView;
				}

				if (this.slidesPerGroup != null) {
					args.slidesPerGroup = this.slidesPerGroup;
				}

				if (this.freeMode != null) {
					args.freeMode = this.freeMode;
				}

				if (this.grabCursor != null) {
					args.grabCursor = this.grabCursor;
				}

				if (this.speed != null) {
					args.speed = this.speed;
				}

				if (this.autoplay != null) {
					args.autoplay = this.autoplay;
				}

				if (this.preventClicks != null) {
					args.preventClicks = this.preventClicks;
				}

				if (this.navigation != null) {
					const getForProp = (prop) => {
						if (prop == null) {
							return null;
						}

						const propVal = prop();
						if (propVal == null) {
							return null;
						}

						return propVal.$el ?? propVal;
					};

					args.navigation = {
						prevEl: getForProp(this.navigation.prevEl),
						nextEl: getForProp(this.navigation.nextEl),
					};
				}

				if (this.slideChanged != null) {
					args.on = {
						slideChange: (swiper: any) => {
							this.slideChanged({
								activeIndex: swiper.activeIndex,
								previousIndex: swiper.previousIndex,
								swiper,
							});
						},
					};
				}

				if (this.zoomChanged != null) {
					args.on = {
						...args.on,
						zoomChange: (swiperEvent: any, scale: number) => {
							this.zoomed = scale > 1;
							this.zoomChanged(scale);
						},
					};
				}

				new SwiperCore(rootElem as any, args);
			}
		}
	}

	getSwiperInstance() {
		const rootElem = this.$el;
		if (rootElem != null) {
			return (rootElem as any).swiper;
		}

		return null;
	}

	getZoomArgs(): SwiperZoomArgs {
		if (this.zoom == true) {
			return {
				enabled: true,
			};
		}

		return this.zoom as SwiperZoomArgs;
	}

	shouldRenderPagination(): boolean {
		return this.hasPagination != false;
	}

	render(h) {
		const zoomEnabled = (this.zoom == true || (this.zoom as SwiperZoomArgs)?.enabled == true);

		return (
			<div class="swiper">
				{this.shouldRenderPagination()
					&& (
						<div
							ref="swiperPagination"
							class={`swiper-pagination swiper-pagination-bullets swiper-pagination-horizontal ${this.zoomed
								? 'hide-swiper-pagination-bulets'
								: ''}`}
						>
						</div>
					)}

				<div class="swiper-wrapper">
					{this.$slots?.default()}
				</div>
			</div>
		);
	}
}

const Swiper = toNative(SwiperComponent);
export default Swiper;
