/**
 * Helpers
 * 汎用ヘルパー関数
 */

/**
 * デバウンス関数
 * @param {Function} func 実行する関数
 * @param {number}   wait 待機時間（ミリ秒）
 * @return {Function}
 */
export function debounce( func, wait = 300 ) {
	let timeout;
	return function executedFunction( ...args ) {
		const later = () => {
			clearTimeout( timeout );
			func( ...args );
		};
		clearTimeout( timeout );
		timeout = setTimeout( later, wait );
	};
}

/**
 * スロットル関数
 * @param {Function} func  実行する関数
 * @param {number}   limit 制限時間（ミリ秒）
 * @return {Function}
 */
export function throttle( func, limit = 300 ) {
	let inThrottle;
	return function executedFunction( ...args ) {
		if ( ! inThrottle ) {
			func.apply( this, args );
			inThrottle = true;
			setTimeout( () => ( inThrottle = false ), limit );
		}
	};
}

/**
 * ディープクローン
 * @param {*} obj クローンするオブジェクト
 * @return {*}
 */
export function deepClone( obj ) {
	if ( obj === null || typeof obj !== 'object' ) {
		return obj;
	}

	if ( obj instanceof Date ) {
		return new Date( obj.getTime() );
	}

	if ( obj instanceof Array ) {
		return obj.map( ( item ) => deepClone( item ) );
	}

	if ( obj instanceof Object ) {
		const clonedObj = {};
		for ( const key in obj ) {
			if ( obj.hasOwnProperty( key ) ) {
				clonedObj[ key ] = deepClone( obj[ key ] );
			}
		}
		return clonedObj;
	}
}

/**
 * ユニークIDを生成
 * @param {string} prefix プレフィックス
 * @return {string}
 */
export function generateUniqueId( prefix = 'id' ) {
	return `${ prefix }-${ Date.now() }-${ Math.random()
		.toString( 36 )
		.substr( 2, 9 ) }`;
}

/**
 * 配列をシャッフル
 * @param {Array} array シャッフルする配列
 * @return {Array}
 */
export function shuffleArray( array ) {
	const shuffled = [ ...array ];
	for ( let i = shuffled.length - 1; i > 0; i-- ) {
		const j = Math.floor( Math.random() * ( i + 1 ) );
		[ shuffled[ i ], shuffled[ j ] ] = [ shuffled[ j ], shuffled[ i ] ];
	}
	return shuffled;
}

/**
 * 範囲内の乱数を生成
 * @param {number} min 最小値
 * @param {number} max 最大値
 * @return {number}
 */
export function randomInRange( min, max ) {
	return Math.floor( Math.random() * ( max - min + 1 ) ) + min;
}

/**
 * オブジェクトが空かチェック
 * @param {Object} obj チェックするオブジェクト
 * @return {boolean}
 */
export function isEmptyObject( obj ) {
	return Object.keys( obj ).length === 0;
}

/**
 * 配列をチャンクに分割
 * @param {Array}  array 分割する配列
 * @param {number} size  チャンクサイズ
 * @return {Array}
 */
export function chunkArray( array, size ) {
	const chunks = [];
	for ( let i = 0; i < array.length; i += size ) {
		chunks.push( array.slice( i, i + size ) );
	}
	return chunks;
}

/**
 * sleepユーティリティ
 * @param {number} ms ミリ秒
 * @return {Promise}
 */
export function sleep( ms ) {
	return new Promise( ( resolve ) => setTimeout( resolve, ms ) );
}

export default {
	debounce,
	throttle,
	deepClone,
	generateUniqueId,
	shuffleArray,
	randomInRange,
	isEmptyObject,
	chunkArray,
	sleep,
};
