/*-
 * ~~~~~~licensing~~~~~~
 * adapters-core
 * ==========
 * 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.runtime.base.connecting.adapter.core.impl;

import com.google.gson.*;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.entaxy.esb.platform.runtime.base.connecting.adapter.core.api.Adapter;
import ru.entaxy.esb.platform.runtime.base.connecting.adapter.core.metadata.AdapterFieldElement;
import ru.entaxy.esb.platform.runtime.base.connecting.adapter.core.metadata.AdapterGeneratorElement;
import ru.entaxy.esb.platform.runtime.base.connecting.adapter.core.metadata.AdapterMetadataElement;
import ru.entaxy.esb.platform.runtime.base.connecting.generator.Generator;
import ru.entaxy.esb.platform.runtime.base.connecting.generator.factory.GeneratorFactory;
import ru.entaxy.esb.platform.runtime.base.connecting.generator.ftl.FTLGenerator;
import ru.entaxy.esb.system.management.blueprint.generator.Blueprint;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;

public class DefaultAdapter implements Adapter {

	protected static final Logger log = LoggerFactory.getLogger(DefaultAdapter.class);
	
	protected BundleContext bundleContext;
	
	protected boolean isInited = false;
	protected String defaultId = "none";
	protected String defaultName = "Undefined";
	protected String defaultDescription = "Undefined";
	protected AdapterMetadataElement adapterMetadata;
	protected Map<String, Generator> generatorList = new HashMap<>();

	public DefaultAdapter(BundleContext bundleContext) throws IOException {
		this.bundleContext = bundleContext;
		log.debug("Constructor of adapter {}", DefaultAdapter.class.getName());
		this.isInited = this.init();
	}

	protected boolean init() throws IOException {
		URL metadataUrl = this.bundleContext.getBundle().getEntry("/ru/entaxy/adapter/metadata.json");
		log.debug("Json URL is {}", metadataUrl.toString());

		String metadata = new BufferedReader (
		  new InputStreamReader(
				  metadataUrl.openStream(), StandardCharsets.UTF_8))
			.lines()
			.collect(Collectors.joining("\n"));

		log.debug("Adapter json description: \n" + metadata);

		GsonBuilder builder = new GsonBuilder();
		Gson gson = builder.create();

		JsonElement je = (new JsonParser()).parse(metadata);
		JsonObject root = je.getAsJsonObject();

		JsonElement adapterElement = root.get("adapter");

		this.adapterMetadata = gson.fromJson(adapterElement, AdapterMetadataElement.class);

		initGenerators(adapterMetadata.getGenerators());

		return this.adapterMetadata != null && !generatorList.isEmpty();
	}

	private void initGenerators(List<AdapterGeneratorElement> generators) {
		for (AdapterGeneratorElement generator: generators) {
			String usageType = generator.getType();
			String generatorType = generator.getGenerator();
			generatorList.put(usageType,
					GeneratorFactory.createGenerator(generatorType, usageType, bundleContext));
		}
		log.debug("generatorList: " + generatorList);
		//TODO checkGeneratorList();
	}

	/* Interface */
	
	@Override
	public boolean isInited() {
		return isInited;
	}

	@Override
	public String getId() {
		return this.isInited?this.adapterMetadata.getId():this.defaultId;
	}

	public void setId(String id) {
		this.adapterMetadata.setId(id);
	}

	@Override
	public String getName() {
		return this.isInited?this.adapterMetadata.getName():this.defaultName;
	}

	public void setName(String name) {
		this.adapterMetadata.setName(name);
	}

	@Override
	public String getDescription() {
		return this.isInited?this.adapterMetadata.getDescription():this.defaultDescription;
	}

	public void setDescription(String description) {
		this.adapterMetadata.setDescription(description);
	}
	
	@Override
	public Map<String, String> getProperties(){
		return this.adapterMetadata.getFields()
			.stream()
			.filter(field -> field.isProperty())
			.collect(Collectors.toMap(AdapterFieldElement::getName, AdapterFieldElement::getType));
	}

	@Override
	public Map<String, String> getOptions(){
		return this.adapterMetadata.getFields()
			.stream()
			.filter(field -> field.isOption())
			.collect(Collectors.toMap(AdapterFieldElement::getName, AdapterFieldElement::getType));
	}

	@Override
	public Blueprint generate(String type, Map<String, Object> map) throws Exception {
		FTLGenerator generator = (FTLGenerator) generatorList.get(type);
		if (null == generator) {
			throw new NoSuchElementException(type + " generator missed in generator list.");
		}
		// TODO: 09.07.2021 Artifact wrapper needed instead of Blueprint (not only blueprint can be return from generator)
		Blueprint result = generator.generate(map);

		return result;
	}
}
