class OneDisplayPlayer {
	constructor(container, options) {

		this.fileManager = window.fileManager;

		// Generate a unique ID for this instance
		this.instanceID = `playback-container-${Math.random().toString(36).substring(2, 9)}`;
		this.containerElement = document.createElement('div');
		this.containerElement.className = `${this.instanceID}`;
		this.containerElement.style.width = '100%';
		this.containerElement.style.height = '100%';
		this.container = container;
		this.playerOptions = options || {settings: {playbackType: 'schedule'}};

		// If no container provided, create default structure
		if (this.container === null) {
			// Create main content container if it doesn't exist
			let contentDiv = document.getElementById('content');
			if (!contentDiv) {
				contentDiv = document.createElement('div');
				contentDiv.className = 'content';
				document.body.appendChild(contentDiv);
			}
			this.containerElement = contentDiv;
		} else {
			container.appendChild(this.containerElement);
		}

		// Ensure required child elements exist
		this.imageContainer = this.ensureElement(`${this.instanceID}-image-container`, 'div', {className: 'image-container'});
		this.iframeContainer = this.ensureElement(`${this.instanceID}-iframe-content`, 'iframe', {
			className: 'library-item'
		});

		// Create video elements if they don't exist
		const videoStyle = 'display: none; opacity: 0;';
		this.video0El = this.ensureElement(`${this.instanceID}-video0`, 'video', {
			crossOrigin: 'anonymous',
			muted: true,
			style: videoStyle
		});
		this.video1El =this.ensureElement(`${this.instanceID}-video1`, 'video', {
			crossOrigin: 'anonymous',
			muted: true,
			style: videoStyle
		});


		// Configuration and state
		this.apiUrl = apiUrl;
		this.cdnUrl = cdnUrl;
		this.screenKey = getScreenKey();
		this.deviceID = getDeviceID();
		this.largeJson = null;
		this.screen = null;
		this.playlist = null;
		this.playlistItems = [];
		this.currentMediaIndex = 0;
		this.currentMediaItem = null;
		this.mediaElementsOrder = []; // Array of element IDs in display order
		this.zIndexCounter = 1;
		this.mediaSwitchTimeout = null;
		this.mediaUsage = {};
		this.mediaCleanupTimeout = null;
		this.playlistPlayed = false;
		

		// Pending playlists
		this.pendingPlaylist = null;
		this.pendingPlaylistReady = false;
		this.hasParsedPlaylist = false;

		// Video slots: active is used for current playback,
		// preload is used to load the next video.
		this.activeVideoSlot = `${this.instanceID}-video0`;
		this.preloadVideoSlot = `${this.instanceID}-video1`;
		this.preloadedVideoId = { video0: null, video1: null };

		// Image slots: active is the one currently visible,
		// preload is used to load the next image.

		this.currentImageId = null;
		this.preloadedImages = {};
		this.preloadedImageUrls = {};

		// Global pointer to DOM
		this.currentDisplayedElement = null

		// Keep track of created Object URLs for proper revocation.
		this.objectURLsVideo = { video0: null, video1: null };
		this.objectURLsImage = { image0: null, image1: null };

		this.downloadedFilesIndex = {};

		// Debug-related properties.
		this.debugging = false;
		this.debugBarElement = null;
		this.debugProgressElement = null;
		this.debugStartTime = 0;
		this.debugDuration = 0;
		this.debugAnimationFrame = null;

		// IndexedDB and download queue.
		//this.db = null;
		this.downloadQueue = {}; // Will hold items with { id, url, status, retryCount }
		this.playlistStarted = false;

		// Network error tracking.
		this.networkErrorElement = null;
		this.hasNetworkError = false;

		// Timer IDs for recurring tasks.
		this.getScreenInformationTimer = null;
		this.aliveTimer = null;
		this.downloadQueueTimer = null;

		this.injectStyles();
		this.initialize();
	}

	injectStyles() {
		const style = document.createElement('style');
		style.innerHTML = `
			.${this.instanceID} * {
		}
			
		  .${this.instanceID} .content {
			position: absolute;
			top: 0;
			left: 0;
			width: 100%;
			height: 100%;
		}

		.${this.instanceID} .image-container {
			position: absolute;
			width: 100%; 
			height: 100%;
		}

		.${this.instanceID} .image-content {
			width: 100%; 
			height: 100%;
			position: absolute;
			background-position: center;
			background-repeat: no-repeat;
			background-size: contain; 
			display: none;
			opacity: 0;
		}
		.${this.instanceID} .library-item {
			position: absolute;
			width: 100%;
			height: 100%;
			border: none;
			display: none;
		}
		.${this.instanceID} video {
			position: absolute;
			width: 100%;
			height: 100%;
			display: none;
			object-fit: contain;
		}
			
		.${this.instanceID} .loading {
			position: absolute;
			width: 100%;
			height: 100%;
			display: flex;
			justify-content: center;
			align-items: center;
			background: radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(200,200,200,1) 100%);
			font-family: 'Courier New', monospace;
			z-index: 1000;
		}
		.${this.instanceID} .loading .player {
			text-align: center;
			color: #000;
		}
		.${this.instanceID} .loading .player img {
			width: 650px;
		}
		.${this.instanceID} .loading .player p {
			font-size: 2em;
			margin: 0.5em 0 0 0;
		}
		.${this.instanceID} .downloadStatus, .downloadProgress {
			font-size: 1.5em;
			color: rgb(185 28 28);
			font-family: 'Courier New', monospace;
		}`;
		document.head.appendChild(style);
	}

	ensureElement(id, tagName, attributes = {}) {
        let element = document.getElementById(id);
        if (!element) {
            element = document.createElement(tagName);
            element.id = id;
            
            // Set attributes
            for (const [key, value] of Object.entries(attributes)) {
                if (key === 'style') {
                    element.style.cssText = value;
                } else if (key === 'className') {
                    element.className = value;
                } else {
                    element.setAttribute(key, value);
                }
            }
			if(tagName === 'video') {
				element.muted = true;
			}
            
            this.containerElement.appendChild(element);
        }
        return element;
    }

	async waitForFileManagerReady() {
		const self = this;
		return new Promise(function(resolve) {
			if (!self.fileManager || self.fileManager.initialized) {
				resolve();
				return;
			}
			let attempts = 0;
			const maxAttempts = 50;

			function checkReady() {
				if (self.fileManager && self.fileManager.initialized) {
					resolve();
					return;
				}
				attempts++;
				if (attempts >= maxAttempts) {
					resolve();
					return;
				}
				setTimeout(checkReady, 100);
			}

			checkReady();
		});
	}

	revokeObjectUrlIfNeeded(url) {
		if (url && typeof url === 'string' && url.indexOf('blob:') === 0) {
			try {
				URL.revokeObjectURL(url);
			} catch (e) {
			}
		}
	}

	async initialize() {

		
		// Restore stored screen and playlist objects if available.
		const storedScreen = getStoredValue("screenObject");
		if (storedScreen !== null) {
			try {
				this.screen = JSON.parse(storedScreen);
			} catch (e) {
				this.screen = null;
			}
		}

		if(this.playerOptions.settings.playbackType == 'static' && this.playerOptions.settings.playlist_id !== null && this.playerOptions.settings.playlist_id !== '') {
			const storedPlaylist = getStoredValue("playlistObject-" + this.playerOptions.settings.playlist_id);
			if (storedPlaylist !== null) {
				try {
					this.playlist = JSON.parse(storedPlaylist);
				} catch (e) {
					this.playlist = null;
				}
			}
		} else {
			const storedPlaylist = getStoredValue("playlistObject");
			if (storedPlaylist !== null) {
				try {
					this.playlist = JSON.parse(storedPlaylist);
				} catch (e) {
					this.playlist = null;
				}
			}
		}



/* 		const usage = getStoredValue("mediaUsageObject");
		if (usage !== null) {
			try {
				this.mediaUsage = JSON.parse(usage);
			} catch (e) {
				this.mediaUsage = {};
			}
		}

		const storedCache = getStoredValue("downloadsCache");
		if (storedCache !== null) {
			try {
				this.downloadedFilesIndex = JSON.parse(storedCache);
			} catch (e) {
				this.downloadedFilesIndex = {};
			}
		} else {
			this.downloadedFilesIndex = {};
			setStoredValue("downloadsCache", JSON.stringify({}));
		} */

		this.playlistItems = this.playlist !== null ? this.playlist.items : [];

		/* if(typeof window.tizen === 'undefined') {
			await this.setupIndexedDB();
		} */

		await this.getScreenInformation();
		//await this.initializeMediaUsage();
		//this.cleanupMedia();
		this.setAliveStatus();
		this.fetchScreenLayout();


		
	}

/* 
	async initializeMediaUsage() {

		if(typeof window.tizen !== 'undefined') {
			// List files in the Tizen downloads directory
			await new Promise((resolve, reject) => {
				tizen.filesystem.resolve(
					'downloads',
					(dirEntry) => {
						dirEntry.listFiles(
							(files) => {
								files.forEach((file) => {
									// Use the file URI as a key.
									if (!this.mediaUsage[file.name]) {
										// Initialize with null or a default timestamp if desired.
										this.mediaUsage[file.name] = { lastUsed: Date.now(), lastUsedDateTime: Date().toLocaleString() };
									}
								});
								resolve();
							},
							(error) => {
								console.error("Error listing files:", error);
								reject(error);
							}
						);
					},
					(error) => {
						console.error("Error resolving downloads directory:", error);
						reject(error);
					},
					"r" // mode: read
				);
			});
		} else {
			// Web version - check IndexedDB
			try {
				// Get all keys from IndexedDB
				const keys = await this.getAllIndexedDBKeys();
				
				// Update media usage for each file
				keys.forEach(key => {
					const fileName = this.getFileNameFromKey(key); // Helper function to extract filename
					if (fileName && !this.mediaUsage[fileName]) {
						this.mediaUsage[fileName] = { 
							lastUsed: Date.now(), 
							lastUsedDateTime: new Date().toLocaleString() 
						};
					}
				});
			} catch (error) {
				console.error("Error initializing media usage from IndexedDB:", error);
				throw error;
			}
		}

		// Save the updated usage object
		setStoredValue("mediaUsageObject", JSON.stringify(this.mediaUsage));

	} */

	/* async getAllIndexedDBKeys() {
		return new Promise((resolve, reject) => {
			if (!this.db) {
				reject(new Error("IndexedDB not initialized"));
				return;
			}
	
			const transaction = this.db.transaction(["items"], "readonly");
			const store = transaction.objectStore("items");
			const request = store.getAllKeys();
	
			request.onsuccess = (event) => {
				resolve(event.target.result);
			};
			request.onerror = (event) => {
				reject(event.target.error);
			};
		});
	} */
	
	// Helper method to extract filename from key (adjust based on your key structure)
	/* getFileNameFromKey(key) {
		// Assuming your key might be the filename or contains it
		// Modify this based on how you structure your keys in IndexedDB
		if (typeof key === 'string') {
			// If key is the URL, extract filename
			if (key.includes('/')) {
				return key.split('/').pop();
			}
			return key;
		}
		return null;
	} */


	// --- Transition Helper ---
	/**
	 * Fades in the new element (from opacity 0 to 1) and, once the fade-in is complete,
	 * fades out the old element. This ensures that the outgoing content “sticks” until the new content is fully visible.
	 *
	 * @param {HTMLElement} newEl - The incoming element.
	 * @param {HTMLElement|null} oldEl - The outgoing element.
	 * @param {number} fadeDuration - The duration of each fade in milliseconds.
	 */
	fadeTransition(newEl, oldEl, fadeDuration = 500) {
		if (!newEl) return;
		if (oldEl === null) {
			newEl.style.display = 'block';
			newEl.style.opacity = 1;
			return;
		}
		if (newEl !== null && oldEl !== null &&
			(
				(newEl.tagName.toLowerCase() === 'video' && oldEl.tagName.toLowerCase() === 'video') ||
				newEl.tagName.toLowerCase() == 'div' ||
				newEl.tagName.toLowerCase() == 'iframe'
			)) {

			newEl.style.transition = 'none';
			newEl.style.display = 'block';
			newEl.style.opacity = 1;

			setTimeout(() => {
				oldEl.style.transition = 'none';
				oldEl.style.display = 'none';
				oldEl.style.opacity = 0;
			}, 500);

			return;
		}
		fadeDuration = 900;
		// Prepare and show the new element.
		newEl.style.transition = `opacity ${fadeDuration}ms ease-in-out`;
		newEl.style.opacity = 0;
		newEl.style.display = 'block';

		// Force reflow so the browser registers the new state.
		newEl.offsetWidth;

		// Begin fade-in.
		setTimeout(() => {
			newEl.style.opacity = 1;
		}, 5);

		// If there is an old element, fade it out and clean it up.
		if (oldEl) {
			oldEl.style.transition = `opacity ${fadeDuration}ms ease-in-out`;
			oldEl.style.opacity = 0;
			// Force the removal after fade duration + slight delay.
			setTimeout(() => {
				oldEl.style.display = 'none';
				oldEl.classList.remove('visible');
			}, fadeDuration + 50);
		}
	}

	// --- Z-index & Display Helpers ---
	updateZIndexForElement(newElementId) {
		// Remove if it exists already.
		this.mediaElementsOrder = this.mediaElementsOrder.filter(id => id !== newElementId);
		// Push it to the end (highest order).
		this.mediaElementsOrder.push(newElementId);

		// Update each element's z-index based on its order.
		this.mediaElementsOrder.forEach((id, index) => {
			let el = document.getElementById(id);
			if (el) {
				el.style.zIndex = index + 1; // z-index starts at 1.
			}
		});
	}

	// --- IndexedDB Setup & File Handling ---
	/* async setupIndexedDB() {
	  return new Promise((resolve, reject) => {
		let request = window.indexedDB.open("media", 1);
		request.onupgradeneeded = (event) => {
		  let db = event.target.result;
		  if (!db.objectStoreNames.contains("items")) {
			db.createObjectStore("items");
		  }
		};
		request.onsuccess = (event) => {
		  this.db = event.target.result;
		  resolve();
		};
		request.onerror = (event) => reject(event);
	  });
	} */
  
	/* async getFileFromDB(id) {
	  return new Promise((resolve, reject) => {
		let transaction = this.db.transaction(["items"], "readonly");
		let request = transaction.objectStore("items").get(id);
		request.onsuccess = (event) => resolve(event.target.result);
		request.onerror = (event) => reject(event);
	  });
	} */
  
	/* async storeFileInDB(id, blob) {
	  return new Promise((resolve, reject) => {
		let transaction = this.db.transaction(["items"], "readwrite");
		let store = transaction.objectStore("items");
		let request = store.put(blob, id);
		request.onsuccess = () => {
			//this.downloadedFilesCache.add(id);
			resolve();
		};
		request.onerror = (event) => reject(event);
	  });
	} */
	

	// --- Playlist & Library Methods (unchanged) ---  
	async parseScreenData(data) {
		try {
			await this.waitForFileManagerReady();
			let hasNewPlaylist = false;
			if (typeof data.playlist !== "undefined") {
				// Handle new playlist detection.
				if (this.playlist !== null && this.playlist.id !== data.playlist.id && this.pendingPlaylist === null) {
					this.pendingPlaylist = data.playlist;
					hasNewPlaylist = true;
					if(this.debugging) {
						console.log("Switching to new playlist, " + data.playlist.name);
					}
				}

				// Update and store current screen/playlist information
				this.screen = data;
				this.playlist = data.playlist !== undefined ? data.playlist : null;
				this.playlistItems = this.playlist && this.playlist.items ? this.playlist.items : [];
				setStoredValue("screenObject", JSON.stringify(this.screen));
				if(this.playerOptions.settings.playbackType == 'static' && this.playerOptions.settings.playlist_id !== null && this.playerOptions.settings.playlist_id !== '') {
					setStoredValue("playlistObject-" + this.playerOptions.settings.playlist_id, JSON.stringify(this.playlist));
				} else {
					setStoredValue("playlistObject", JSON.stringify(this.playlist));
				}
				
			}

			// Debug mode.
			if (data && data.debugging === "1") {
				if (!this.debugging) {
					window.debugObject.startDebugger();
					this.debugging = true;
				}
			} else {
				this.debugging = false;
				window.debugObject.stopDebugger();
			}

			// Queue downloads for media (except URLs).
			for (let item of this.playlistItems) {
				if (item.library_type !== "url") {
					let exists = await this.fileManager.checkFileExists(item.id, item.path, false);
					//let exists = await this.checkFileExists(item.id, item.path, false);
					if (!exists) {
						this.fileManager.queueDownload(item);
						//this.queueDownload(item);
					}
				}
			}
			// Start processing the download queue if not already started.
			/* if (!this.downloadQueueTimer) {
				this.checkDownloadQueue();
			}
			await this.waitForDownloads(); */
			/*if(!this.fileManager.downloadQueueTimer) {
				this.fileManager.processDownloadQueue();
			}
			await this.fileManager.waitForDownloads();*/
			
			// only start if not already running
			//this.fileManager.processDownloadQueue();
			// no need for a timer flag; the guard inside prevents re-entry
			await this.fileManager.waitForDownloads();
			
			if (hasNewPlaylist && this.hasParsedPlaylist) {
				// Mark that the pending playlist is fully downloaded.
				this.pendingPlaylistReady = true;

				await this.resetPlayerToNewPlaylist();
				return;
			}

			// Always preload all images if new ones have been added.
			await this.preloadAllImages();

			// Hide loading if still present from initialization
			this.hideLoading();

			// If playlist has not been started, and the playlist contains items, start it.
			if (!this.playlistStarted && this.playlistItems.length > 0) {
				this.playlistStarted = true;
				this.hasParsedPlaylist = true;
				// Preload video if the first item is a video.
				/*if (this.playlistItems[0].library_type === "video") {
					await this.loadVideoIntoSlot(this.playlistItems[0], this.activeVideoSlot);
					this.preloadedVideoId[this.activeVideoSlot] = this.playlistItems[0].id;
					if (this.debugging) {
						console.log(`Preloaded video "${this.playlistItems[0].name}" into ${this.activeVideoSlot}`);
					}
				}*/
				// Play media!
				this.playNextMedia();
			}
		} catch (error) {
			console.error("Error parsing library:", error);
		}
	}

	// Fetch layout if not already available
	async fetchScreenLayout() {
		if(typeof window.layoutObject === 'undefined' || window.layoutObject === null) {
			try {
				const response = await axios.get(`${this.apiUrl}/display/${this.screenKey}/layout`);
				if (typeof response.data.layout !== 'undefined') {
					window.location.reload();
				}
			  } catch (error) {
				console.error('Error fetching layout:', error);
			  }
		}
		setTimeout(() => this.fetchScreenLayout(), 30000);
		
	  }

	async getScreenInformation() {
		try {
			if(this.playerOptions.settings.playbackType == 'static' && this.playerOptions.settings.playlist_id !== null && this.playerOptions.settings.playlist_id !== '') {
				const response = await axios.get(`${this.apiUrl}/display/${this.screenKey}/playlist/${this.playerOptions.settings.playlist_id}`);
				await this.parseScreenData(response.data);
			} else {
				const response = await axios.get(`${this.apiUrl}/display/${this.screenKey}/playlist`);
				await this.parseScreenData(response.data);
			}
		} catch (error) {
			const storedData = getStoredValue("screenObject");
			if (storedData) {
				try {
					const storedScreenObject = JSON.parse(storedData);
					await this.parseScreenData(storedScreenObject);
				} catch (err) {
					console.error("Error parsing cached screen data:", err);
				}
			}
		}
		// Schedule the next API call after 30 seconds.
		this.getScreenInformationTimer = setTimeout(() => this.getScreenInformation(), 15000);
	}


	hideLoading() {
		const loadingEl = document.querySelector(".loading");
		if (loadingEl) {
			loadingEl.style.display = "none";
		}
	}

	async resetPlayerToNewPlaylist() {
		this.playlistPlayed = false;
		// Check if pending playlist is ready or not
		/*if (!this.pendingPlaylistReady) {
			console.log("Pending playlist not ready yet");
			return;
		}*/

		// Clear timeouts
		if (this.mediaSwitchTimeout) {
			clearTimeout(this.mediaSwitchTimeout);
			this.mediaSwitchTimeout = null;
		}
		this.playlist = this.pendingPlaylist;
		this.playlistItems = (this.playlist && this.playlist.items) ? this.playlist.items : [];
		this.currentMediaIndex = 0;
		this.currentMediaItem = null;
		this.pendingPlaylist = null;
		this.pendingPlaylistReady = false;
		this.zIndexCounter = 1;

		if (this.preloadedImageUrls) {
			for (let key in this.preloadedImageUrls) {
				if (this.preloadedImageUrls.hasOwnProperty(key)) {
					this.revokeObjectUrlIfNeeded(this.preloadedImageUrls[key]);
				}
			}
		}
		if (this.objectURLsVideo) {
			for (let slot in this.objectURLsVideo) {
				if (this.objectURLsVideo.hasOwnProperty(slot)) {
					this.revokeObjectUrlIfNeeded(this.objectURLsVideo[slot]);
					this.objectURLsVideo[slot] = null;
				}
			}
		}

		// Clear DOM of images
		this.imageContainer.innerHTML = '';

		// Clear preloaded image URL's
		this.preloadedImages = {};
		this.preloadedImageUrls = {};
		this.playlistStarted = false;


		await this.preloadAllImages();
		this.playlistStarted = true;
		if (this.playlistItems[0].library_type === "video") {
			await this.loadVideoIntoSlot(this.playlistItems[0], this.activeVideoSlot);
			this.preloadedVideoId[this.activeVideoSlot] = this.playlistItems[0].id;
			if (this.debugging) {
				console.log(`Preloaded video "${this.playlistItems[0].name}" into ${this.activeVideoSlot}`);
			}
		}

		this.playNextMedia();
	}
/* 
	async checkFileExists(id, filename, force) {
		let fileExists = false;
		if (typeof window.tizen !== "undefined") {
			fileExists = await this.checkFileExistsOnStorage(id, filename, force);
		} else {
			fileExists = await this.checkFileExistsInDB(id);
		}
		return fileExists;
	} */

	/* async checkFileExistsInDB(id) {

		if (typeof this.downloadedFilesIndex[id] !== 'undefined') {
			return true;
		}

		return new Promise((resolve, reject) => {
			let transaction = this.db.transaction(["items"], "readonly");
			let request = transaction.objectStore("items").get(id);
			request.onsuccess = (event) => {
				let exists = event.target.result !== undefined;
				if (exists) {
					//this.downloadedFilesIndex[id] = {id: id, file: file.toURI()}
				}
				resolve(exists);
			};
			request.onerror = (event) => reject(event);
		});
	} */

	/* async checkFileExistsOnStorage(id, filename, forceCheck = false) {
		if (!forceCheck && typeof this.downloadedFilesIndex[id] !== 'undefined') {
			return true;
		}

		return new Promise((resolve, reject) => {
			tizen.filesystem.resolve('downloads', (dir) => {
				try {
					// Try to resolve the file by name. If it doesn't exist, an exception is thrown.
					const file = dir.resolve(filename);
					if (file) {
						this.downloadedFilesIndex[id] = { id: id, file: file.toURI(), filename: filename }
						resolve(true);
					} else {
						resolve(false);
					}
				} catch (e) {
					resolve(false);
				}
			}, (error) => {
				reject(new Error(`Error resolving storage directory: ${error.message}`));
			});
		});
	} */

	/* queueDownload(item) {
		if (!this.downloadQueue[item.id]) {
			this.downloadQueue[item.id] = {
				id: item.id,
				filename: item.path,
				url: `${this.cdnUrl}/media/${item.path}`,
				status: "pending",
				retryCount: 0,
			};
		}
	} */

	/* async checkDownloadQueue() {
		for (let id in this.downloadQueue) {
			if (this.downloadQueue[id].status === "pending") {
				try {
					await this.downloadFile(this.downloadQueue[id]);
					delete this.downloadQueue[id];
				} catch (error) {
					console.error(error);
					delete this.downloadQueue[id];
				}
			}
		}
		this.downloadQueueTimer = setTimeout(() => this.checkDownloadQueue(), 5000);
	} */

	/* async deleteFile(fileName) {
		if(typeof window.tizen !== 'undefined') {
	    	return new Promise((resolve, reject) => {
	    	// Tizen version > 7.0 (?)
			
				if (typeof tizen.filesystem !== 'undefined' && typeof tizen.filesystem.deleteFile === 'function') {
					tizen.filesystem.deleteFile('downloads/' + fileName, () => {
						console.warn(`File deleted successfully: ${fileName}`);
						resolve();
					}, (error) => {
						console.error(`Error deleting file ${fileName}:`, error);
						resolve();
						//reject(error);
					});
				} else {
					// Tizen version 4.0 (?)
					
					tizen.filesystem.resolve('downloads', (dir) => {
						dir.deleteFile("downloads/" + fileName, () => {
							console.warn(`File deleted successfully: ${fileName}`);
							resolve();
						}, (error) => {
							console.error(`Error deleting file ${fileName}:`, error.message);
							 resolve();
							//reject(error);
						});
					}, (error) => {
						console.error(`Error resolving downloads directory:`, error.message);
						resolve();
						//reject(error);
					}, "rw");
					
				}
			

	    	});
		} else {
			// For web version with IndexedDB
			try {
				// Find the ID associated with this filename
				let idToDelete = null;
				for (const [id, meta] of Object.entries(this.downloadedFilesIndex)) {
					if (meta.filename === fileName) {
						idToDelete = id;
						break;
					}
				}
				
				if (idToDelete) {
					const transaction = this.db.transaction(["items"], "readwrite");
					const store = transaction.objectStore("items");
					await new Promise((resolve, reject) => {
						const request = store.delete(idToDelete);
						request.onsuccess = () => {
							delete this.downloadedFilesIndex[idToDelete];
							//this.downloadedFilesCache.delete(idToDelete);
							setStoredValue('downloadsCache', JSON.stringify(this.downloadedFilesIndex));
							resolve();
						};
						request.onerror = (event) => reject(event);
					});
				}
			} catch (error) {
				console.error("Error deleting file from IndexedDB:", error);
				throw error;
			}
		}
	} */




	/* async downloadFile(fileInfo) {
		const MAX_RETRIES = 3;
		let fileName = fileInfo.url.split("/").pop();
		let statusEl = document.getElementById("downloadStatus");
		let progressEl = document.getElementById("downloadProgress");

		if (statusEl && progressEl) {
			progressEl.textContent = `Downloading ${fileName}... 0%`;
		}

		

		// Remove existing file if necessary.
		if (this.checkFileExists(fileInfo.id, fileInfo.filename, true)) {
			await this.deleteFile(fileInfo.filename);
		}
		
		if(typeof window.tizen !== 'undefined') {
			if (this.debugging) {
				console.log(`Downloading ${fileName} using Tizen Download API...`);
			}

			return new Promise((resolve, reject) => {
				let retryCount = 0;
				let self = this;
	
				const startDownload = () => {
					// Create the Tizen download request.
					let req = new tizen.DownloadRequest(fileInfo.url, 'downloads', fileName, 'ALL');
					req.httpHeader['Pragma'] = 'no-cache';
	
					let listener = {
						onprogress: function (id, receivedSize, totalSize) {
							if (totalSize && progressEl) {
								let percent = Math.floor((receivedSize / totalSize) * 100);
								progressEl.textContent = `Downloading ${fileName}... ${percent}%`;
							}
						},
						onpaused: function (id) {
							if (self.debugging) {
								console.log(`Download paused: ${id}`);
							}
						},
						oncanceled: function (id) {
							if (self.debugging) {
								console.log(`Download cancelled: ${id}`);
							}
							if (progressEl) {
								progressEl.textContent = "Download cancelled";
							}
							reject(new Error("Download cancelled"));
						},
						oncompleted: function (id, fullPath) {
							if (self.debugging) {
								console.log(`Download completed: ${fullPath}`);
							}
							if (statusEl) {
								statusEl.textContent =
									Object.keys(self.downloadQueue).length === 0
										? "Downloaded all files."
										: "Downloaded some files.";
							}
							// Save file metadata.
							self.storeFileMetaData(fileInfo.id, "file://" + fullPath, fileName);
							resolve(fileInfo);
						},
						onfailed: function (error) {
							if (self.debugging) {
								console.warn(`Download of ${fileName} failed: ${error}`);
							}
							retryCount++;
							if (retryCount < MAX_RETRIES) {
								// Retry the download.
								startDownload();
							} else {
								reject(new Error(`Download for ${fileInfo.id} failed after ${MAX_RETRIES} attempts.`));
							}
						}
					};
	
					// Start the download.
					tizen.download.start(req, listener);
				};
	
				startDownload();
			});
		}

		if (typeof window.tizen === 'undefined') {
			if (this.debugging) {
				console.log(`Downloading ${fileName} using axios and storing in IndexedDB...`);
			}
			
			return new Promise(async (resolve, reject) => {
				let retryCount = 0;
				let self = this;
	
				const startDownload = async () => {
					try {
						const response = await axios.get(fileInfo.url, {
							responseType: 'blob',
							onDownloadProgress: (progressEvent) => {
								if (progressEvent.total && progressEl) {
									let percent = Math.floor((progressEvent.loaded / progressEvent.total) * 100);
									progressEl.textContent = `Downloading ${fileName}... ${percent}%`;
								}
							}
						});
	
						if (response.data) {
							// Store the blob in IndexedDB
							await this.storeFileInDB(fileInfo.id, response.data);
							
							// Store metadata (you might want to modify this to store IndexedDB reference)
							await this.storeFileMetaData(fileInfo.id, fileInfo.id, fileName); // Using ID as reference
							
							resolve(fileInfo);
						} else {
							throw new Error(`Download for ${fileInfo.id} returned no data.`);
						}
					} catch (error) {
						if (self.debugging) {
							console.warn(`Download of ${fileName} failed: ${error}`);
						}
						retryCount++;
						if (retryCount < MAX_RETRIES) {
							// Retry the download
							setTimeout(() => startDownload(), 1000 * retryCount); // Add delay between retries
						} else {
							reject(new Error(`Download for ${fileInfo.id} failed after ${MAX_RETRIES} attempts.`));
						}
					}
				};
	
				await startDownload();
			});
		}
	} */

	/* async getFileUrlFromDB(id) {
		const blob = await this.getFileFromDB(id);
		return URL.createObjectURL(blob);
	} */

	// Helper function to store file metadata using localStorage.
	/* async storeFileMetaData(id, fileUri, name) {
		return new Promise((resolve) => {
			this.downloadedFilesIndex[id] = { id: id, file: fileUri, filename: name };
			setStoredValue('downloadsCache', JSON.stringify(this.downloadedFilesIndex));
			resolve();
		});
	} */

	// Helper function to get the file URI from Tizen filesystem.
	/* async getFileFromStorage(fileName) {
		return new Promise((resolve, reject) => {
			tizen.filesystem.resolve('downloads', (dir) => {
				try {
					let file = dir.resolve(fileName);
					resolve(file.toURI());
				} catch (e) {
					resolve(null);
				}
			}, (error) => {
				reject(new Error(`Error resolving storage directory: ${error.message}`));
			});
		});
	} */

	/* async waitForDownloads() {
		let statusEl = document.getElementById("downloadStatus");
		while (Object.keys(this.downloadQueue).length > 0) {
			if (statusEl) {
				statusEl.textContent = `Waiting on ${Object.keys(this.downloadQueue).length} download(s)...`;
			}
			await new Promise((resolve) => setTimeout(resolve, 500));
		}
	} */

	async loadVideoIntoSlot(item, slot) {

		let videoElement = document.getElementById(slot);
		if (!videoElement) return;
		videoElement.pause();
		videoElement.removeAttribute("src");
		videoElement.load();

		try {
			let fileUri = null;
			// Get the file URI, either from cache or from the Tizen filesystem
			if (typeof this.fileManager.downloadedFilesIndex[item.id] !== 'undefined') {
				if(typeof window.tizen === 'undefined') {
					fileUri = await this.fileManager.getFileUrlFromDB(item.id);
				} else {
					fileUri = this.fileManager.downloadedFilesIndex[item.id].file;
				}
				
			} else {
				if(typeof window.tizen !== 'undefined') {
					console.log("Getting file from tizen storage..")
					fileUri = await this.fileManager.getFileFromStorage(item.path);
				} else {
					console.log("Getting file from indexedDB..")
					fileUri = await this.fileManager.getFileUrlFromDB(item.id);
				}
				this.fileManager.downloadedFilesIndex[item.id] = { id: item.id, file: fileUri, filename: item.path };
				setStoredValue("downloadsCache", JSON.stringify(this.fileManager.downloadedFilesIndex));
			}

			if (fileUri) {
				if (this.objectURLsVideo[slot] && this.objectURLsVideo[slot] !== fileUri) {
					this.revokeObjectUrlIfNeeded(this.objectURLsVideo[slot]);
				}
				this.objectURLsVideo[slot] = fileUri;
				videoElement.src = fileUri;
				videoElement.load();
				if (this.debugging) {
					console.log(`Loaded video "${item.name}" into ${slot}`);
				}
			} else {
				console.warn(`File with id ${item.id} not found on storage.`);
			}
		} catch (error) {
			console.error("Error loading video from storage:", error);
		}
	}

	// --- Display Methods ---
	// For URL items.
	displayIframe(item, oldEl) {
		let srcUrl = `${item.path}`;
		function getYouTubeVideoId(url) {
			const youtubePatterns = [
				/(?:youtube\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/
			];
			for (let pattern of youtubePatterns) {
				let match = url.match(pattern);
				if (match) return match[1];
			}
			return null;
		}
		let youtubeVideoId = getYouTubeVideoId(srcUrl);
		if (youtubeVideoId) {
			srcUrl = `https://www.youtube.com/embed/${youtubeVideoId}?autoplay=1&mute=1`;
		}
		//this.iframeContainer
		//let iframeElement = document.querySelector(".library-item");
		this.iframeContainer.src = srcUrl;
		this.iframeContainer.style.display = "block";
		this.updateZIndexForElement(this.instanceID + "-iframe-content");
		this.fadeTransition(this.iframeContainer, oldEl, 500);
		if (this.debugging)
			console.log(`Displaying URL "${srcUrl}"`);
	}

	// --- Media Usage Methods ---
	/* updateMediaUsage(filename) {
		// Update last used timestamp to current time
		this.mediaUsage[filename] = { lastUsed: Date.now(), lastUsedDateTime: Date().toLocaleString() };
		setStoredValue("mediaUsageObject", JSON.stringify(this.mediaUsage));
	} */

	// --- Find ID by Filename ---
	/* findIdByFilename(filename) {
		// Search through downloadedFilesIndex instead of undefined 'files' object
		const entry = Object.values(this.downloadedFilesIndex).find(
			file => file.filename === filename
		);
		return entry ? entry.id : null;
	} */

	// --- Remove unused files ---
	/* cleanupMedia() {
		if( this.playlistPlayed ) {
			console.log("Playlist has finished atleast one lap, cleaning media!")
			const now = Date.now();
			//const threshold = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
			const threshold = 5 * 60 * 1000; // 60,000 milliseconds
			console.log("Threshold: " + threshold + " ms");
			Object.keys(this.mediaUsage).forEach((file) => {
				const lastUsed = this.mediaUsage[file].lastUsed;
				// If lastUsed is set and older than 24 hours, remove it.
				if (lastUsed && (now - lastUsed) > threshold) {
					console.log("Within threshold, gonna try and scrap that file now, hold on!");
					let fileId = this.findIdByFilename(file);
					if (fileId !== null) {
						console.log("Found the file ID from the filename ("+file+") = " + fileId);
						delete this.downloadedFilesIndex[fileId];
						console.log(`Removed file ${file} (ID: ${fileId}) from downloadedFilesIndex`);
					}
					this.deleteFile(file)
					delete this.mediaUsage[file];
				}
			});
	
			setStoredValue("mediaUsageObject", JSON.stringify(this.mediaUsage));
		}

		this.mediaCleanupTimeout = setTimeout(
			() => this.cleanupMedia(),
			60000
		);
	} */

	// --- Main Playback Loop ---
	async playNextMedia() {
		clearTimeout(this.mediaSwitchTimeout);
		this.mediaSwitchTimeout = null;
		
		if (!this.playlistItems.length) return;

		// The media index does not exist, probably the playlist has been changed or the playlistItem has been removed?
		if (typeof this.playlistItems[this.currentMediaIndex] === "undefined") {
			this.currentMediaIndex = (this.currentMediaIndex + 1) % this.playlistItems.length;
			this.playNextMedia(); //Immediately play next media
			if (this.debugging) {
				console.log(`Media item not found, skipping!`);
			}
			return;
		}
		let sameMediaItem = false;
		// Check if we're about to play the same item, in that case, just set the timout for the same duration-
		if (this.currentMediaItem !== null && this.currentMediaItem.id == this.playlistItems[this.currentMediaIndex].id) {
			sameMediaItem = true;
		}

		let currentItem = this.playlistItems[this.currentMediaIndex];
		this.currentMediaItem = this.playlistItems[this.currentMediaIndex];

		// Save last time played for object
		this.fileManager.updateMediaUsage(this.currentMediaItem.path);
		//this.updateMediaUsage(this.currentMediaItem.path);

		// Increment statistics for played media
		if(!window.playerPreview) {
			window.statisticsObject.addStatistics(this.currentMediaItem.id, parseFloat(this.currentMediaItem.show_seconds));
		}

		if (this.debugging) {
			window.debugObject.nowPlaying(currentItem);
			console.log(`Switching to media item index ${this.currentMediaIndex}: ${currentItem.name}`);
		}

		// Use the currently displayed element (could be video, image, or iframe)
		let oldEl = this.currentDisplayedElement;
		
		if (currentItem.library_type === "video") {

			// TODO: Pause the oldEl.
			if(oldEl && oldEl.tagName.toLowerCase() === 'video') {
				oldEl.pause();
			}

			let videoElement = document.getElementById(this.activeVideoSlot);
			
			if(this.debugging) {
				videoElement.addEventListener('error', evt => {
					console.error(evt);
					console.error(
						`Playback error code ${videoElement.error.code};`
					);
				}, { once: true });
			}


			if (this.preloadedVideoId[this.activeVideoSlot] !== currentItem.id) {
				await this.loadVideoIntoSlot(currentItem, this.activeVideoSlot);
				this.preloadedVideoId[this.activeVideoSlot] = currentItem.id;
			}

			videoElement.loop = false;
			videoElement.pause();
			videoElement.style.opacity = 0;
			videoElement.currentTime = 0;
			videoElement.style.display = "block";
			this.updateZIndexForElement(this.activeVideoSlot);

			try {
				await videoElement.play();
			} catch (error) {
				if(this.debugging) {
					console.error("Error playing video:", error);
				}
			}

			this.fadeTransition(videoElement, oldEl, 500);
			this.currentDisplayedElement = videoElement;


			// Preload the next video, if applicable.
			let nextVideoIndex = this.findNextVideoIndex(this.currentMediaIndex + 1);
			if (nextVideoIndex !== null) {
				let nextVideoItem = this.playlistItems[nextVideoIndex];
				let slotToUse = this.preloadVideoSlot;
				if (this.preloadedVideoId[slotToUse] !== nextVideoItem.id) {
					await this.loadVideoIntoSlot(nextVideoItem, slotToUse);
					this.preloadedVideoId[slotToUse] = nextVideoItem.id;
				}
			}
			// Swap active and preload video slots.
			[this.activeVideoSlot, this.preloadVideoSlot] = [this.preloadVideoSlot, this.activeVideoSlot];

		} else if (currentItem.library_type === "image") {

			if (oldEl != null && oldEl.tagName.toLowerCase() === 'video') {
				oldEl.pause();
			}

			if (this.preloadedImages && this.preloadedImages[currentItem.id]) {
				let newImageEl = this.preloadedImages[currentItem.id];
				this.updateZIndexForElement(this.instanceID + "-image_" + currentItem.id);
				if (!sameMediaItem) {
					this.fadeTransition(newImageEl, oldEl, 500);
				}

				if (oldEl && oldEl.classList && oldEl.classList.contains("visible")) {
					oldEl.classList.remove("visible");
				}
				newImageEl.classList.add("visible");
				this.currentDisplayedElement = newImageEl;
				this.currentImageId = currentItem.id;
				if (this.debugging) {
					console.log(`Displaying image "${currentItem.name}"`);
				}
			} else {
				console.warn("Image not preloaded:" + currentItem.id);
			}
		} else if (currentItem.library_type === "url") {
			if (oldEl != null && oldEl.tagName.toLowerCase() === 'video') {
				oldEl.pause();
			}
			// Display URL content via an iframe.
			//console.log("Going to URL from", oldEl);
			if(!sameMediaItem) {
				this.displayIframe(currentItem, oldEl);
			}
			
			let iframeEl = this.iframeContainer;
			this.currentDisplayedElement = iframeEl;
		} else {
			console.warn("Unsupported media type:" + currentItem.library_type);
		}


		// Advance the media index.
		if (this.playlistItems.length > 1) {
			this.currentMediaIndex = (this.currentMediaIndex + 1) % this.playlistItems.length;
			if (this.currentMediaIndex === 0 && this.playlistPlayed === false) {
				this.playlistPlayed = true;
			}
		}

		// Schedule the next media switch based on the current item's show duration.
		this.mediaSwitchTimeout = setTimeout(
			() => this.playNextMedia(),
			parseFloat(currentItem.show_seconds) * 1000
		);
	}

	// --- Debug & Utility Methods ---
	async destroy() {
		if (this.getScreenInformationTimer) {
			clearTimeout(this.getScreenInformationTimer);
			this.getScreenInformationTimer = null;
		}
		if (this.aliveTimer) {
			clearTimeout(this.aliveTimer);
			this.aliveTimer = null;
		}
		if (this.downloadQueueTimer) {
			clearTimeout(this.downloadQueueTimer);
			this.downloadQueueTimer = null;
		}
		if (this.mediaSwitchTimeout) {
			clearTimeout(this.mediaSwitchTimeout);
			this.mediaSwitchTimeout = null;
		}
		if (this.debugAnimationFrame) {
			cancelAnimationFrame(this.debugAnimationFrame);
			this.debugAnimationFrame = null;
		}
		//this.removeDebugBar();
		this.hideNetworkError();
	}
	
	/*async clearFilesystem() {
		await new Promise((resolve, reject) => {
            tizen.filesystem.resolve(
                'downloads',
                (dirEntry) => {
                    dirEntry.listFiles(
                        (files) => {
                            files.forEach((file) => {
                            	console.warn("Deleting file: " + file.name);
                            	// I want to wait until I get a response from this.deleteFile()
                            	this.deleteFile(file.name);
                            });
                            resolve();
                        },
                        (error) => {
                            console.error("Error listing files:", error);
                            reject(error);
                        }
                    );
                },
                (error) => {
                    console.error("Error resolving downloads directory:", error);
                    reject(error);
                },
                "r" // mode: read
            );
        });
	}*/
	
	/* async clearFilesystem() {
	    try {
	        const dirEntry = await new Promise((resolve, reject) => {
	            tizen.filesystem.resolve(
	                'downloads',
	                resolve,
	                reject,
	                "r"
	            );
	        });

	        const files = await new Promise((resolve, reject) => {
	            dirEntry.listFiles(resolve, reject);
	        });

	        for (const file of files) {
	            console.warn("Deleting file: " + file.name);
	            await this.deleteFile(file.name); // Ensure deletion completes before proceeding
	        }

	        console.warn("All files deleted successfully.");
	    } catch (error) {
	        console.error("Error clearing filesystem:", error);
	    }
	} */

	// --- Show network error element ---
	showNetworkError() {
		if (this.networkErrorElement) return;
		this.networkErrorElement = document.createElement("div");
		this.networkErrorElement.classList.add("network-error");
		this.networkErrorElement.innerHTML = '<img style="width: 50px;" src="/cloudError.svg">';
		document.body.appendChild(this.networkErrorElement);
	}

	// --- Hide network error element ---
	hideNetworkError() {
		if (this.networkErrorElement) {
			this.networkErrorElement.remove();
			this.networkErrorElement = null;
		}
	}
	
	async parseDeviceCommands(data) {
	    if (data.commands.length > 0) {
	        for (const command of data.commands) {
	            if (command.command.toLowerCase() === 'disconnect') {
	                console.warn("Screen deactivated command received!");
	                // Send command ack
	                console.warn("Sending command acknowledgement..");
	                await this.acknowledgeCommand(command);
	                // Clear timers
	                console.warn("Resetting timers..");
	                this.destroy();
	                // Remove all files
	                console.warn("Clearing filesystem..");
	                await this.fileManager.clearFilesystem();
	                //await this.clearFilesystem();
	                // Purge local storage
	                console.warn("Clearing cached screen/playlist/download information..");
	                removeStoredValue('screenKey');
	        		removeStoredValue("screenObject");
	        		removeStoredValue("playlistObject");
	        		removeStoredValue("mediaUsageObject");
	        		removeStoredValue("downloadsCache");
	        		removeStoredValue("statisticsObject");
	                // Reload app
	        		console.warn("Reloading app..");
	        		setTimeout(() => {window.location.reload()}, 3000);
	                //window.location.reload();
	            }
	        }
	    }
	}
	
	async acknowledgeCommand(commandObject) {
		const url = `${this.apiUrl}/ack/${this.deviceID}${this.screenKey ? `/${this.screenKey}` : ""}`;
		const response = await axios.post(url, {command: commandObject});
	}

	async setAliveStatus() {
		if(window.playerPreview) {
			return;
		}
		try {
			const url = `${this.apiUrl}/ping/${this.deviceID}${this.screenKey ? `/${this.screenKey}` : ""}`;
			const response = await axios.post(url);
			await this.parseDeviceCommands(response.data);
			this.hasNetworkError = false;
			this.hideNetworkError();
		} catch (error) {
			this.showNetworkError();
			this.hasNetworkError = true;
			if (this.debugging) {
				console.warn("Error updating alive status:", error);
			}
		}
		this.aliveTimer = setTimeout(() => this.setAliveStatus(), 30000);
	}

	findNextVideoIndex(startIndex) {
		let len = this.playlistItems.length;
		for (let i = 0; i < len; i++) {
			let idx = (startIndex + i) % len;
			if (this.playlistItems[idx].library_type === "video") {
				return idx;
			}
		}
		return null;
	}

	async preloadAllImages() {
		//const container = document.getElementById("image-container");
		if (!this.imageContainer) {
			console.error("Container element not found!");
			return;
		}

		for (let item of this.playlistItems) {
			if (item.library_type === "image") {
				// Check if image is already preloaded and exists in DOM.
				if (this.preloadedImages[item.id] && document.getElementById(this.instanceID + "-image_" + item.id) !== null) {
					continue;
				}
				try {
					// Get the file URI from cache or the Tizen filesystem.
					let fileUri = null;
					if (typeof this.fileManager.downloadedFilesIndex[item.id] !== 'undefined') {
						if(typeof window.tizen === 'undefined') {
							fileUri = await this.fileManager.getFileUrlFromDB(item.id);
						} else {
							fileUri = this.fileManager.downloadedFilesIndex[item.id].file;
						}
					} else {
						if(typeof window.tizen !== 'undefined') {
							console.log("Getting file from tizen storage..")
							fileUri = await this.fileManager.getFileFromStorage(item.path);
						} else {
							console.log("Getting file from indexedDB..")
							fileUri = await this.fileManager.getFileUrlFromDB(item.id);
						}
						
						this.fileManager.downloadedFilesIndex[item.id] = { id: item.id, file: fileUri, filename: item.path };
						setStoredValue("downloadsCache", JSON.stringify(this.fileManager.downloadedFilesIndex));
					}
					if (fileUri) {
						if (this.preloadedImageUrls[item.id] && this.preloadedImageUrls[item.id] !== fileUri) {
							this.revokeObjectUrlIfNeeded(this.preloadedImageUrls[item.id]);
						}
						// Create a div element with the image as a background.
						let imageEl = document.createElement("div");
						imageEl.id = this.instanceID + "-image_" + item.id;
						imageEl.classList.add("image-content");
						imageEl.style.backgroundImage = `url(${fileUri})`;
						imageEl.style.display = "none";
						imageEl.style.opacity = 0;
						imageEl.style.transition = "opacity 0.5s ease-in-out";
						imageEl.style.backgroundSize = 'contain';
						this.imageContainer.appendChild(imageEl);

						// Keep track of preloaded images.
						this.preloadedImages[item.id] = imageEl;
						this.preloadedImageUrls[item.id] = fileUri;
					} else {
						console.warn(`File with id ${item.id} not found on storage.`);
					}
				} catch (error) {
					console.error("Error preloading image:", error);
				}
			}
		}
		// Small delay if needed.
		await new Promise((resolve) => setTimeout(resolve, 500));
	}
}
