/*-
 * ~~~~~~licensing~~~~~~
 * cluster-persistence-service
 * ==========
 * Copyright (C) 2020 - 2021 EmDev LLC
 * ==========
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.esb.platform.core.cluster.persistence;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.karaf.cellar.bundle.BundleState;
import org.apache.karaf.cellar.core.ClusterManager;
import org.apache.karaf.cellar.core.Configurations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Helper<T> {

	private static final Logger log = LoggerFactory.getLogger(Helper.class);
	
	public static final String DELIMITER = "~~_DEL_~~";
	public static final String SEPARATOR = "~~_SEP_~~";
	
	protected String mapBaseName;
	protected String classifier;
	protected Class<T> dataClass;
	
	protected Serializer<T> serializer;

	protected String rootPath;
	protected String subPath;
	
	protected ServiceProvider serviceProvider;
	
	public Helper(ServiceProvider serviceProvider, String mapBaseName, String classifier, Class<T> dataClass) {
		this.serviceProvider = serviceProvider;
		this.mapBaseName = mapBaseName;
		this.classifier = classifier;
		this.dataClass = dataClass;
		this.serializer = new Serializer<>(this.dataClass);
		this.rootPath = System.getProperty("karaf.home");
		this.subPath = "cluster-state";
	}

	protected Path getTargetPath(String groupName) {
		String fileName = "cluster." + classifier + "." + groupName + ".state";
		Path p = Paths.get(this.rootPath, this.subPath, fileName);
		return p;
	}
	
	public void saveClusterState(String groupName) {
        
		ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(this.dataClass.getClassLoader());

        if (!checkGroup(groupName))
        	return;
        
        ClusterManager clusterManager = this.serviceProvider.getClusterManager();
        
		@SuppressWarnings("unchecked")
		Map<String, T> clusterData = clusterManager.getMap(this.mapBaseName + Configurations.SEPARATOR + groupName);
		String serialized = "";
		serialized = clusterData.entrySet().stream()
			.<String>map((entry)->{return entry.getKey() + DELIMITER + serializer.toString(entry.getValue());})
			.collect(Collectors.joining(SEPARATOR));
		Path p = getTargetPath(groupName); 
		try {
//			if (!p.toFile().exists())
//				p.toFile().createNewFile();
			Files.createDirectories(p.getParent());
			Files.write(p, serialized.getBytes());
		} catch (IOException e) {
			log.error("FAILED saving state to [" + p.toUri().toString() + "]", e);
		}
        
        Thread.currentThread().setContextClassLoader(originalClassLoader);
        
	} 

	public void loadClusterState(String groupName) {

		ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(this.dataClass.getClassLoader());

        if (!checkGroup(groupName))
        	return;
        
		Path p = getTargetPath(groupName); 
		if (!p.toFile().exists()) {
			log.info("File [{}] doesn't exist, skip loading [{}] from [{}]"
					, p.toUri().toString()
					, this.classifier
					, this.mapBaseName);
			return;
		}
        
		String data = "";
		try {
			data = Files.readString(p);
		} catch (IOException e) {
			log.error("Error reading data from [" + p.toUri().toString() + "]", e);
		}
		if ((data == null) || data.length()==0 )
			return;

        ClusterManager clusterManager = this.serviceProvider.getClusterManager();
        
		@SuppressWarnings("unchecked")
		Map<String, T> clusterData = clusterManager.getMap(this.mapBaseName + Configurations.SEPARATOR + groupName);

		String[] entries = data.split(SEPARATOR);
		for (int i=0; i<entries.length; i++) {
			String[] entry = entries[i].split(DELIMITER);
			T object = serializer.fromString(entry[1]);
			clusterData.put(entry[0], object);
		}
		
        Thread.currentThread().setContextClassLoader(originalClassLoader);
        
	} 

	protected boolean checkGroup(String groupName) {
		// TODO implement check
		return true;
	}
	
}
