/*-
 * ~~~~~~licensing~~~~~~
 * generator-api
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.base.generator.template.impl;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Optional;
import java.util.stream.Collectors;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.BundleTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import ru.entaxy.base.generator.template.Template;
import ru.entaxy.base.generator.template.TemplateImpl;
import ru.entaxy.base.generator.template.TemplateService;
import ru.entaxy.platform.base.support.JSONUtils;

public class TemplateBundleTrackerCustomizer implements BundleTrackerCustomizer<TemplateProvider> {

	private static final Logger log = LoggerFactory.getLogger(TemplateBundleTrackerCustomizer.class);
	
	@Override
	public TemplateProvider addingBundle(Bundle bundle, BundleEvent event) {
		try {
			TemplateProvider provider = createTemplateProvider(bundle);
			if (provider != null)
				register(provider);
			return provider;
		} catch (Exception e) {
			log.error("Error creating TemplateProvider for bundle ["
					+ bundle.getBundleId() + "]"
					, e);
		}
		return null;
	}

	@Override
	public void modifiedBundle(Bundle bundle, BundleEvent event, TemplateProvider object) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void removedBundle(Bundle bundle, BundleEvent event, TemplateProvider object) {
		
		unregister(object);
		
	}

	protected TemplateProvider createTemplateProvider(Bundle bundle) throws Exception {
		
		TemplateProvider result = new TemplateProvider();
		result.bundle = bundle;
		result.bundleContext = bundle.getBundleContext();
		
		boolean isTemplateProvider = Boolean.valueOf(
				Optional
				.ofNullable(bundle.getHeaders().get(TemplateService.TEMPLATE_PROVIDER_HEADER))
				.orElse("false")
			);
		if (!isTemplateProvider)
			return null;
		
//		Enumeration<String> entries = bundle.getEntryPaths(TemplateProvider.TEMPLATE_PATH + "/metadata.json");
		URL metadataUrl = bundle.getEntry(TemplateProvider.TEMPLATE_PATH + "/metadata.json");
		
		if (metadataUrl != null) {
			// descriptor is found

			GsonBuilder builder = new GsonBuilder();
			Gson gson = builder.create();
			
			JsonObject root = JSONUtils.getJsonRootObject(metadataUrl);
			
			JsonElement templatesElement = root.get("templates");
			if (templatesElement == null)
				return null;
			if (!templatesElement.isJsonArray())
				return null;
			JsonArray templates = templatesElement.getAsJsonArray();
			for (int i=0; i<templates.size(); i++) {
				TemplateMetadata meta = gson.fromJson(templates.get(i), TemplateMetadata.class);
				ProvidedTemplate providedTemplate = new ProvidedTemplate(meta);
				result.providedTemplates.add(providedTemplate);
			}
		
		} else {
			// try to locate templates
			
			log.debug("BEGIN :: Searching for templates in " + bundle.getBundleId());
			
			Enumeration<URL> foundEntries = bundle.findEntries(TemplateProvider.TEMPLATE_PATH, "*.*", true);
			if (foundEntries == null)
				return null;
			while (foundEntries.hasMoreElements()) {
				URL entry = foundEntries.nextElement();
				log.debug("FOUND :: " + entry.toString());
				if (entry.toString().endsWith("/")) {
					log.debug(":: .. is folder");
					continue;
				}
				String localPath = entry.toString();
				localPath = localPath.substring(localPath.indexOf(TemplateProvider.TEMPLATE_PATH) + TemplateProvider.TEMPLATE_PATH.length() + 1);
				String fullname = localPath.substring(localPath.lastIndexOf("/")+1);
				String path = localPath.lastIndexOf("/")>=0
						?localPath.substring(0, localPath.lastIndexOf("/"))
						:"";
				String fullPath = TemplateProvider.TEMPLATE_PATH + "/" + path;
				String fileName = fullname.substring(0, fullname.lastIndexOf("."));
				String fileType = fullname.substring(fullname.lastIndexOf(".")+1);
				String id = path.replace("/", ".") 
						+ (path.isEmpty()?"":".") 
						+ fileName;
				log.debug(":: localPath = " + localPath + "; fullName = " + fullname + "; fileName = " + fileName
						+ "; fileType = " + fileType + "; path = " + path);
				
				TemplateMetadata meta = new TemplateMetadata();
				meta.setId(id);
				meta.setFilename(fileName);
				meta.setFullname(fullname);
				meta.setName(id);
				meta.setType(fileType);
				meta.setPath(fullPath);
				meta.setDescription("Found in bundle [" + bundle.getBundleId() + "] on path [" + localPath + "]");
				ProvidedTemplate providedTemplate = new ProvidedTemplate(meta);
				result.providedTemplates.add(providedTemplate);
			}
			
			log.debug("END :: Searching for templates in " + bundle.getBundleId());
		}
		
		return result;
	}
	
	protected void register(TemplateProvider templateProvider) {
		
		for (ProvidedTemplate provided: templateProvider.providedTemplates) {
			
			TemplateImpl template = new TemplateImpl();
			template.setBundleContext(templateProvider.bundleContext);
			template.load(provided);
			// TODO fill object
			
			Dictionary<String, String> props = new Hashtable<String, String>();
			props.put(TemplateService.PROP_ID, provided.getId());
			props.put(TemplateService.PROP_NAME, provided.getName());
			props.put(TemplateService.PROP_TYPE, provided.getType());
			props.put(TemplateService.PROP_DESCRIPTION, provided.getDescription());
			if (provided.getAdditionalProperties() != null) {
				provided.getAdditionalProperties().forEach((k,v) -> {props.put(TemplateService.PROP_PREFIX + k, v);});
			}
			
			ServiceRegistration<Template> registration = 
					templateProvider.bundleContext.registerService(
							Template.class, template, props
					);
			
			provided.registration = registration;
		}
		
	}

	protected void unregister(TemplateProvider templateProvider) {
		
		for (ProvidedTemplate provided: templateProvider.providedTemplates) {
			provided.registration.unregister();
		}
		
	}
	
}
