package ca.uhn.fhir.util;

/*-
 * #%L
 * HAPI FHIR - Core Library
 * %%
 * Copyright (C) 2014 - 2019 University Health Network
 * %%
 * 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.
 * #L%
 */

import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.api.Constants;
import org.hl7.fhir.instance.model.api.*;

import java.util.List;

public class MetaUtil {

	private MetaUtil() {
		// non-instantiable
	}

	public static String getSource(FhirContext theContext, IBaseMetaType theMeta) {
		if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
			return getSourceR4Plus(theContext, theMeta);
		} else if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
			return getSourceDstu3((IBaseHasExtensions) theMeta);
		} else {
			throw new UnsupportedOperationException(MetaUtil.class.getSimpleName() + ".getSource() not supported on FHIR Version " + theContext.getVersion().getVersion());
		}
	}

	private static String getSourceDstu3(IBaseHasExtensions theMeta) {
		IBaseHasExtensions metaWithExtensions = theMeta;
		List<? extends IBaseExtension<?, ?>> extensions = metaWithExtensions.getExtension();
		for (IBaseExtension extension : extensions) {
			if (Constants.EXT_META_SOURCE.equals(extension.getUrl())) {
				IPrimitiveType<String> value = (IPrimitiveType<String>) extension.getValue();
				return value.getValueAsString();
			}
		}
		return null;
	}

	private static String getSourceR4Plus(FhirContext theFhirContext, IBaseMetaType theMeta) {
		BaseRuntimeElementCompositeDefinition<?> elementDef = (BaseRuntimeElementCompositeDefinition<?>) theFhirContext.getElementDefinition(theMeta.getClass());
		BaseRuntimeChildDefinition sourceChild = elementDef.getChildByName("source");
		if (sourceChild == null) {
			return null;
		}
		List<IBase> sourceValues = sourceChild.getAccessor().getValues(theMeta);
		String retVal = null;
		if (sourceValues.size() > 0) {
			retVal = ((IPrimitiveType<?>) sourceValues.get(0)).getValueAsString();
		}
		return retVal;
	}

	/**
	 * Sets the value for <code>Resource.meta.source</code> for R4+ resources, and places the value in
	 * an extension on <code>Resource.meta</code>
	 * with the URL <code>http://hapifhir.io/fhir/StructureDefinition/resource-meta-source</code> for DSTU3.
	 *
	 * @param theContext  The FhirContext object
	 * @param theResource The resource to modify
	 * @param theValue    The source URI
	 * @see <a href="http://hl7.org/fhir/resource-definitions.html#Resource.meta">Meta.source</a>
	 */
	@SuppressWarnings("unchecked")
	public static void setSource(FhirContext theContext, IBaseResource theResource, String theValue) {
		if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
			MetaUtil.setSource(theContext, theResource.getMeta(), theValue);
		} else if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
			IBaseExtension<?, ?> sourceExtension = ((IBaseHasExtensions) theResource.getMeta()).addExtension();
			sourceExtension.setUrl(Constants.EXT_META_SOURCE);
			IPrimitiveType<String> value = (IPrimitiveType<String>) theContext.getElementDefinition("uri").newInstance();
			value.setValue(theValue);
			sourceExtension.setValue(value);
		}
	}

	public static void setSource(FhirContext theContext, IBaseMetaType theMeta, String theValue) {
		BaseRuntimeElementCompositeDefinition<?> elementDef = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theMeta.getClass());
		BaseRuntimeChildDefinition sourceChild = elementDef.getChildByName("source");
		List<IBase> sourceValues = sourceChild.getAccessor().getValues(theMeta);
		IPrimitiveType<?> sourceElement;
		if (sourceValues.size() > 0) {
			sourceElement = ((IPrimitiveType<?>) sourceValues.get(0));
		} else {
			sourceElement = (IPrimitiveType<?>) theContext.getElementDefinition("uri").newInstance();
			sourceChild.getMutator().setValue(theMeta, sourceElement);
		}
		sourceElement.setValueAsString(theValue);
	}

}

@@ -979,16 +979,73 @@
 l.api.*;
+%0Aimport org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;
 %0A%0Aimport
@@ -1062,16 +1062,17 @@
 .List;%0A%0A
+%0A
 public c
@@ -1086,16 +1086,95 @@
 taUtil %7B
+%0A%09private static final Logger ourLog = LoggerFactory.getLogger(MetaUtil.class);
 %0A%0A%09priva
@@ -4017,16 +4017,163 @@
 value);%0A
+%09%09%7D else %7B%0A%09%09%09ourLog.error(MetaUtil.class.getSimpleName() + %22.setSource() not supported on FHIR Version %22 + theContext.getVersion().getVersion());%0A
 %09%09%7D%0A%09%7D%0A%0A

fix test. this is better