// [z-paging]工具类

import zLocalConfig from '../config/index'
import c from './z-paging-constant'

const storageKey = 'Z-PAGING-REFRESHER-TIME-STORAGE-KEY';
let config = null;
let configLoaded = false;
let cachedSystemInfo = null;
const timeoutMap = {};

// 获取默认配置信息
function gc(key, defaultValue) {
	// 这里return一个函数以解决在vue3+appvue中，props默认配置读取在main.js之前执行导致uni.$zp全局配置无效的问题。相当于props的default中传入一个带有返回值的函数
	return () => {
		// 处理z-paging全局配置
		_handleDefaultConfig();
		// 如果全局配置不存在，则返回默认值
		if (!config) return defaultValue;
		const value = config[key];
		// 如果全局配置存在但对应的配置项不存在，则返回默认值；反之返回配置项
		return value === undefined ? defaultValue : value;
	};
}

// 获取最终的touch位置
function getTouch(e) {
	let touch = null;
	if (e.touches && e.touches.length) {
		touch = e.touches[0];
	} else if (e.changedTouches && e.changedTouches.length) {
		touch = e.changedTouches[0];
	} else if (e.datail && e.datail != {}) {
		touch = e.datail;
	} else {
		return { touchX: 0, touchY: 0 }
	}
	return {
		touchX: touch.clientX,
		touchY: touch.clientY
	};
}

// 判断当前手势是否在z-paging内触发
function getTouchFromZPaging(target) {
	if (target && target.tagName && target.tagName !== 'BODY' && target.tagName !== 'UNI-PAGE-BODY') {
		const classList = target.classList;
		if (classList && classList.contains('z-paging-content')) {
			// 此处额外记录当前z-paging是否是页面滚动、是否滚动到了顶部、是否是聊天记录模式以传给renderjs。避免不同z-paging组件renderjs内部判断数据互相影响导致的各种问题
			return {
				isFromZp: true,
				isPageScroll: classList.contains('z-paging-content-page'),
				isReachedTop: classList.contains('z-paging-reached-top'),
				isUseChatRecordMode: classList.contains('z-paging-use-chat-record-mode')
			};
		} else {
			return getTouchFromZPaging(target.parentNode);
		}
	} else {
		return { isFromZp: false };
	}
}

// 递归获取z-paging所在的parent，如果查找不到则返回null
function getParent(parent) {
	if (!parent) return null;
	if (parent.$refs.paging) return parent;
	return getParent(parent.$parent);
}

// 打印错误信息
function consoleErr(err) {
	console.error(`[z-paging]${err}`);
}

// 延时操作，如果key存在，调用时清除对应key之前的延时操作
function delay(callback, ms = c.delayTime, key) {
	const timeout = setTimeout(callback, ms);;
	if (!!key) {
		timeoutMap[key] && clearTimeout(timeoutMap[key]);
		timeoutMap[key] = timeout;
	}
	return timeout;
}

// 设置下拉刷新时间
function setRefesrherTime(time, key) {
	const datas = getRefesrherTime() || {};
	datas[key] = time;
	uni.setStorageSync(storageKey, datas);
}

// 获取下拉刷新时间
function getRefesrherTime() {
	return uni.getStorageSync(storageKey);
}

// 通过下拉刷新标识key获取下拉刷新时间
function getRefesrherTimeByKey(key) {
	const datas = getRefesrherTime();
	return datas && datas[key] ? datas[key] : null;
}

// 通过下拉刷新标识key获取下拉刷新时间(格式化之后)
function getRefesrherFormatTimeByKey(key, textMap) {
	const time = getRefesrherTimeByKey(key);
	const timeText = time ? _timeFormat(time, textMap) : textMap.none;
	return `${textMap.title}${timeText}`;
}

// 将文本的px或者rpx转为px的值
function convertToPx(text) {
	const dataType = Object.prototype.toString.call(text);
	if (dataType === '[object Number]') return text;
	let isRpx = false;
	if (text.indexOf('rpx') !== -1 || text.indexOf('upx') !== -1) {
		text = text.replace('rpx', '').replace('upx', '');
		isRpx = true;
	} else if (text.indexOf('px') !== -1) {
		text = text.replace('px', '');
	}
	if (!isNaN(text)) {
		if (isRpx) return Number(rpx2px(text));
		return Number(text);
	}
	return 0;
}

// rpx => px，预留的兼容处理
function rpx2px(rpx) {
	return uni.upx2px(rpx);
}

// 同步获取系统信息，兼容不同平台
function getSystemInfoSync(useCache = false) {
	if (useCache && cachedSystemInfo) {
		return cachedSystemInfo;
	}
	// 目前只用到了deviceInfo、appBaseInfo和windowInfo中的信息，因此仅整合这两个信息数据
	const infoTypes = ['DeviceInfo', 'AppBaseInfo', 'WindowInfo'];
	const { deviceInfo, appBaseInfo, windowInfo } = infoTypes.reduce((acc, key) => {
		const method = `get${key}`;
		if (uni[method] && uni.canIUse(method)) {
			acc[key.charAt(0).toLowerCase() + key.slice(1)] = uni[method]();
		}
		return acc;
	}, {});
	// 如果deviceInfo、appBaseInfo和windowInfo都可以从各自专属的api中获取，则整合它们的数据
	if (deviceInfo && appBaseInfo && windowInfo) {
		cachedSystemInfo = { ...deviceInfo, ...appBaseInfo, ...windowInfo };
	} else {
		// 使用uni.getSystemInfoSync兜底，确保能获取到最终的系统信息
		cachedSystemInfo = uni.getSystemInfoSync();
	}
	return cachedSystemInfo;
}

// 获取当前时间
function getTime() {
	return (new Date()).getTime();
}

// 获取z-paging实例id，随机生成10位数字+字母
function getInstanceId() {
	const s = [];
	const hexDigits = "0123456789abcdef";
	for (let i = 0; i < 10; i++) {
		s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
	}
	return s.join('') + getTime();
}

// 等待一段时间
function wait(ms) {
	return new Promise(resolve => {
		setTimeout(resolve, ms);
	});
}

// 是否是promise
function isPromise(func) {
	return Object.prototype.toString.call(func) === '[object Promise]';
}

// 添加单位
function addUnit(value, unit) {
	if (Object.prototype.toString.call(value) === '[object String]') {
		let tempValue = value;
		tempValue = tempValue.replace('rpx', '').replace('upx', '').replace('px', '');
		if (value.indexOf('rpx') === -1 && value.indexOf('upx') === -1 && value.indexOf('px') !== -1) {
			tempValue = parseFloat(tempValue) * 2;
		}
		value = tempValue;
	}
	return unit === 'rpx' ? value + 'rpx' : (value / 2) + 'px';
}

// 深拷贝
function deepCopy(obj) {
	if (typeof obj !== 'object' || obj === null) return obj;
	let newObj = Array.isArray(obj) ? [] : {};
	for (let key in obj) {
		if (obj.hasOwnProperty(key)) {
			newObj[key] = deepCopy(obj[key]);
		}
	}
	return newObj;
}

// 对短时间内重复插入的数据进行整合，并一次性插入
function useBufferedInsert(fn, delay = 50) {
	let buffer = [];
	let timer = null;
	let latestArgs = [];
	return function insertBuffered(data, ...args) {
		const newData = Object.prototype.toString.call(data) !== '[object Array]' ? [data] : data;
		buffer.push(...newData);
		latestArgs = args;
		if (!timer) {
			timer = setTimeout(() => {
				fn(buffer.length === 1 ? buffer[0] : buffer, ...latestArgs);
				buffer = [];
				timer = null;
			}, buffer.length === 1 ? 10 : delay);
		}
	};
}

// ------------------ 私有方法 ------------------------
// 处理全局配置
function _handleDefaultConfig() {
	// 确保只加载一次全局配置
	if (configLoaded) return;
	// 优先从config.js中读取
	if (zLocalConfig && Object.keys(zLocalConfig).length) {
		config = zLocalConfig;
	}
	// 如果在config.js中读取不到，则尝试到uni.$zp读取
	if (!config && uni.$zp) {
		config = uni.$zp.config;
	}
	// 将config中的短横线写法全部转为驼峰写法，使得读取配置时可以直接通过key去匹配，而非读取每个配置时候再去转，减少不必要的性能开支
	config = config ? Object.keys(config).reduce((result, key) => {
		result[_toCamelCase(key)] = config[key];
		return result;
	}, {}) : null;
	configLoaded = true;
}

// 时间格式化
function _timeFormat(time, textMap) {
	const date = new Date(time);
	const currentDate = new Date();
	// 设置time对应的天，去除时分秒，使得可以直接比较日期
	const dateDay = new Date(time).setHours(0, 0, 0, 0);
	// 设置当前的天，去除时分秒，使得可以直接比较日期
	const currentDateDay = new Date().setHours(0, 0, 0, 0);
	const disTime = dateDay - currentDateDay;
	let dayStr = '';
	const timeStr = _dateTimeFormat(date);
	if (disTime === 0) {
		dayStr = textMap.today;
	} else if (disTime === -86400000) {
		dayStr = textMap.yesterday;
	} else {
		dayStr = _dateDayFormat(date, date.getFullYear() !== currentDate.getFullYear());
	}
	return `${dayStr} ${timeStr}`;
}

// date格式化为年月日
function _dateDayFormat(date, showYear = true) {
	const year = date.getFullYear();
	const month = date.getMonth() + 1;
	const day = date.getDate();
	return showYear ? `${year}-${_fullZeroToTwo(month)}-${_fullZeroToTwo(day)}` : `${_fullZeroToTwo(month)}-${_fullZeroToTwo(day)}`;
}

// data格式化为时分
function _dateTimeFormat(date) {
	const hour = date.getHours();
	const minute = date.getMinutes();
	return `${_fullZeroToTwo(hour)}:${_fullZeroToTwo(minute)}`;
}

// 不满2位在前面填充0
function _fullZeroToTwo(str) {
	str = str.toString();
	return str.length === 1 ? '0' + str : str;
}

// 驼峰转短横线
function _toKebab(value) {
	return value.replace(/([A-Z])/g, "-$1").toLowerCase();
}

// 短横线转驼峰
function _toCamelCase(value) {
	return value.replace(/-([a-z])/g, (_, group1) => group1.toUpperCase());
}


export default {
	gc,
	setRefesrherTime,
	getRefesrherFormatTimeByKey,
	getTouch,
	getTouchFromZPaging,
	getParent,
	convertToPx,
	getTime,
	getInstanceId,
	consoleErr,
	delay,
	wait,
	isPromise,
	addUnit,
	deepCopy,
	rpx2px,
	getSystemInfoSync,
	useBufferedInsert
};
