package org.etsi.mts.tdl.openapi2tdl;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.Optional;

import org.etsi.mts.tdl.CollectionDataType;
import org.etsi.mts.tdl.DataElementMapping;
import org.etsi.mts.tdl.DataResourceMapping;
import org.etsi.mts.tdl.DataType;
import org.etsi.mts.tdl.Member;
import org.etsi.mts.tdl.PackageableElement;
import org.etsi.mts.tdl.StructuredDataType;
import org.etsi.mts.tdl.tdlFactory;
import org.etsi.mts.tdl.tdlPackage;
import org.etsi.mts.tdl.transform.AbstractTranslator;

import com.reprezen.jsonoverlay.JsonOverlay;
import com.reprezen.jsonoverlay.Reference;
import com.reprezen.kaizen.oasparser.OpenApi3Parser;
import com.reprezen.kaizen.oasparser.model3.OpenApi3;
import com.reprezen.kaizen.oasparser.model3.Schema;
import com.reprezen.kaizen.oasparser.ovl3.SchemaImpl;

public class OpenAPI2TDLTranslator extends AbstractTranslator {


	public void translate(String filename) throws Exception {
//		Converter.setTdlTokens(Arrays.asList(new String[] {"'name'", "'type'", "'size'", "'instance'"}));
//		Converter converter = new Converter(filename);
//		converter.runConversion();
//		getGeneratedPackage().getNestedPackage().add(converter.getPackage());
		//TODO: refine according to other translators
		//TODO: separate validation into another component
		
		OpenApi3 model = loadOpenApi3(filename);
		drm = getTypeFor("SOURCE_MAPPING", tdlPackage.Literals.DATA_RESOURCE_MAPPING);
		drm.setResourceURI("\""+new File(filename).getName()+"\"");
		drmTarget = getTypeFor("TARGET_MAPPING", tdlPackage.Literals.DATA_RESOURCE_MAPPING);
		//TODO: make configurable
		drmTarget.setResourceURI("\"generated/java\"");
		for (Schema schema: model.getSchemas().values()) {
			DataType dataType = translate(schema, "");
			addMapping(schema, dataType);
		}
		
	}

	private void addMapping(Schema schema, DataType dataType) {
		DataElementMapping sourceMapping = getTypeFor(schema.getName()+"_SOURCE_MAPPING", tdlPackage.Literals.DATA_ELEMENT_MAPPING);
		sourceMapping.setMappableDataElement(dataType);
		sourceMapping.setElementURI("\"#/components/schemas/"+schema.getName()+"\"");
		sourceMapping.setDataResourceMapping(drm);
		
		//TODO: make configurable 
		DataElementMapping targetMapping = getTypeFor(schema.getName()+"_TARGET_MAPPING", tdlPackage.Literals.DATA_ELEMENT_MAPPING);
		targetMapping.setMappableDataElement(dataType);
		//TODO: transform / adapt mapping based on configuration?
		targetMapping.setElementURI("\""+schema.getName()+"\"");
		targetMapping.setDataResourceMapping(drmTarget);

	}

	private DataType translate(Schema schema, String prefix) {
		if (schema.getType()==null && schema.getProperties().isEmpty()) {
			if (schema.getName() == null) {
				System.out.println("Why?");
			}
			return getSimpleDataTypeFor(schema.getName());
		} else {
			String name = prefix+schema.getName();
			if (schema.getName() == null) {
				name = prefix+"___item";
			}
			if (!schema.getProperties().isEmpty() || 
					schema.getType().equals("object")) {
				return translateObject(schema, name);
			} else if (schema.getType().equals("array")) {
				return translateArray(schema, name);
			} else {
				return getSimpleDataTypeFor(schema.getType());
			}
		}
	}

	private DataType translateObject(Schema schema, String name) {
		StructuredDataType dataType = getStructuredDataTypeFor(name);
		for (String propertyName : schema.getProperties().keySet()) {
			Schema propertySchema = schema.getProperty(propertyName);
			Reference reference = ((SchemaImpl) propertySchema)._getCreatingRef();
			DataType memberType = null;
			if (reference!=null) {
				memberType = translate(propertySchema, "");
			} else {
				memberType = translate(propertySchema, name+"___");
			}
			Member m = getContentWithName(propertyName, dataType, tdlPackage.Literals.MEMBER);
			m.setDataType(memberType);
			if (m.container()==null) {
				dataType.getMember().add(m);
			}
		}
		return dataType;
	}

	private DataType translateArray(Schema schema, String name) {
		SchemaImpl itemsSchema = (SchemaImpl) schema.getItemsSchema();
		CollectionDataType collectionType = getTypeFor(name, tdlPackage.Literals.COLLECTION_DATA_TYPE);
		Reference reference = itemsSchema._getCreatingRef();
		if (reference!=null) {
			DataType itemType = translate(itemsSchema, "");
			collectionType.setItemType(itemType);
		} else {
			DataType itemType = translate(itemsSchema, name+"");
			collectionType.setItemType(itemType);
		}
		return collectionType;
	}

	
	public static OpenApi3 loadOpenApi3(String filename) throws FileNotFoundException {
		File file = new File(filename);
		if (!file.exists()) {
			throw new FileNotFoundException(String.format("File '%s' does not exist", filename));
		}
		OpenApi3 model;
		try {
			model = new OpenApi3Parser().parse(file, true);
		} catch (Exception e) {
			throw new RuntimeException(String.format("Error while parsing the yaml file: %s: '%s'", e.getClass().getName(), e.getMessage()), e);
		}
		return model;
	}

}
