/*-
 * ~~~~~~licensing~~~~~~
 * test-producers
 * ==========
 * Copyright (C) 2020 - 2022 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.platform.core.producer.executor.objectmodel;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.JsonObject;

import ru.entaxy.platform.base.objects.EntaxyObject.FIELDS;

public class FactoredObjectRef extends AbstractFactored {

	private static final Logger log = LoggerFactory.getLogger(FactoredObjectRef.class);
	
	public static String IS_EXTERNAL_FIELD = "isExternal";
	public static String TARGET_ID_FIELD = "targetId";
	public static String TARGET_TYPE_FIELD = "targetType";
	public static String TARGET_VALUE_FIELD = "targetValue";
	public static String IS_RESOLVED_FIELD = "isResolved";
	public static String IS_LINK = "isLink";

	protected FactoredObject targetObject = null;
	
	protected String targetId = ""; 
	protected String targetType = ""; 
	
	protected boolean isExternal = true;
	protected boolean isResolved = true;
	protected boolean isBackRef = false;
	protected boolean refByValueOnly = false;
	protected boolean isLink = false;
	
	public FactoredObjectRef (ObjectModel objectModel) {
		super(objectModel);
		this.origin = new JsonObject();
		this.origin.addProperty(FIELDS.IS_REF, true);
		this.origin.addProperty(IS_EXTERNAL_FIELD, false);
		this.origin.addProperty(TARGET_ID_FIELD, getTargetId());
		this.origin.addProperty(TARGET_TYPE_FIELD, getTargetType());
		this.origin.addProperty(FIELDS.FACTORY_ID, this.factoryId);
		this.origin.addProperty(FIELDS.SCOPE, this.scope);
		this.origin.addProperty(IS_RESOLVED_FIELD, this.isResolved);
		this.origin.addProperty(FIELDS.IS_BACK_REF, this.isBackRef);
		this.origin.addProperty(IS_LINK, false);
	}
	
	public FactoredObjectRef (ObjectModel objectModel, FactoredObject targetObject) {
		super(objectModel);
		this.origin = new JsonObject();
		
		this.setTargetObject(targetObject); 
		this.definedIn = targetObject.definedIn;
		this.scope = targetObject.scope;
		this.factoryId = targetObject.factoryId;
		this.jsonPath = targetObject.jsonPath;
		this.relativePath = targetObject.relativePath;
		this.isExternal = false;
		
		this.isLink = false;
		
		this.origin.addProperty(FIELDS.IS_REF, true);
		this.origin.addProperty(IS_EXTERNAL_FIELD, false);
		this.origin.addProperty(TARGET_ID_FIELD, getTargetId());
		this.origin.addProperty(TARGET_TYPE_FIELD, getTargetType());
		this.origin.addProperty(FIELDS.FACTORY_ID, this.factoryId);
		this.origin.addProperty(FIELDS.SCOPE, this.scope);
		this.origin.addProperty(IS_RESOLVED_FIELD, this.isResolved);
		this.origin.addProperty(FIELDS.IS_BACK_REF, this.isBackRef);
		
		this.origin.addProperty(IS_LINK, this.isLink);
		
		objectModel.addRef(this);
	}
	
	public static boolean isRef(JsonObject jsonObject) {
		try {
			
			// if object has 'targetId' we assume it as a ref
			// even if there's no 'isRef'
			if (jsonObject.has(TARGET_ID_FIELD))
				if (!jsonObject.has(FIELDS.IS_REF))
					jsonObject.addProperty(FIELDS.IS_REF, true);
			
			if (!jsonObject.has(TARGET_ID_FIELD) && !AbstractFactored.isFactored(jsonObject))
				return false;
			if (!jsonObject.has(FIELDS.IS_REF))
				return false;
			boolean isRef = jsonObject.get(FIELDS.IS_REF).getAsBoolean();
			return isRef;
		} catch (Exception e) {
			return false;
		}
	}
	
	@Override
	public void wrap(JsonObject object, Map<String, Object> context, String path) {
		super.wrap(object, context, path);

		if (object.has(TARGET_ID_FIELD))
			this.targetId = object.get(TARGET_ID_FIELD).getAsString();

		if (object.has(TARGET_VALUE_FIELD))
			this.targetId = object.get(TARGET_VALUE_FIELD).getAsString();
		
		if (object.has(TARGET_TYPE_FIELD))
			this.targetType = object.get(TARGET_TYPE_FIELD).getAsString();

		if (object.has(IS_EXTERNAL_FIELD))
			try{
				this.isExternal = object.get(IS_EXTERNAL_FIELD).getAsBoolean();
			} catch (Exception e) {
				
			}

		if (object.has(IS_RESOLVED_FIELD))
			try{
				this.isResolved = object.get(IS_RESOLVED_FIELD).getAsBoolean();
			} catch (Exception e) {
				
			}

		if (object.has(FIELDS.IS_BACK_REF))
			try{
				this.isBackRef = object.get(FIELDS.IS_BACK_REF).getAsBoolean();
			} catch (Exception e) {
				
			}

		if (object.has(IS_LINK))
			try{
				this.isLink = object.get(IS_LINK).getAsBoolean();
			} catch (Exception e) {
				
			}
		
		
		if (object.has(FIELDS.IS_REF_BY_VALUE_ONLY))
			try{
				this.refByValueOnly = object.get(FIELDS.IS_REF_BY_VALUE_ONLY).getAsBoolean();
			} catch (Exception e) {
				
			}
		
		
		AbstractFactored contextOwner = getContextOwner(context);
		if ((contextOwner != null) && (contextOwner instanceof FactoredObject)) {
			FactoredObject fo = (FactoredObject)contextOwner;
			log.debug("\t IN CONTEXT :: " + fo.factoryId + "[" + fo.jsonPath + "]");
			computeRelativePath(fo.jsonPath);
			if (null == this.ownScope || this.ownScope.isEmpty())
				this.scope = fo.scope;
			this.definedIn = fo;
		}
		
		
		objectModel.addRef(this);
	}

	public boolean isBackRef() {
		return isBackRef;
	}

	public boolean isExternal() {
		return isExternal;
	}

	public boolean isResolved() {
		return isResolved;
	}

	public void setResolved(boolean isResolved) {
		this.isResolved = isResolved;
		this.origin.remove(IS_RESOLVED_FIELD);
		this.origin.addProperty(IS_RESOLVED_FIELD, this.isResolved);
	}

	public void setBackRef(boolean isBaskRef) {
		this.isBackRef = isBaskRef;
		this.origin.remove(FIELDS.IS_BACK_REF);
		this.origin.addProperty(FIELDS.IS_BACK_REF, this.isBackRef);
	}

	public void setExternal(boolean isExternal) {
		this.isExternal = isExternal;
		this.origin.remove(IS_EXTERNAL_FIELD);
		this.origin.addProperty(IS_EXTERNAL_FIELD, this.isExternal);
	}

	public void setLink(boolean isLink) {
		this.isLink = isLink;
		this.origin.remove(IS_LINK);
		this.origin.addProperty(IS_LINK, this.isLink);
	}
	
	public boolean isLink() {
		return isLink;
	}

	public boolean isRefByValueOnly() {
		return refByValueOnly;
	}

	public void setRefByValueOnly(boolean isRefByValueOnly) {
		this.refByValueOnly = isRefByValueOnly;
	}

	public String getTargetId() {
		if (targetObject == null)
			return targetId;
		return targetObject.getObjectId();
	}

	public String getTargetType() {
		if (targetObject == null)
			return targetType;
		return targetObject.getObjectType();
	}

	public FactoredObject getTargetObject() {
		return targetObject;
	}

	public void setTargetObject(FactoredObject targetObject) {
		if (this.targetObject != null)
			this.targetObject.references.remove(this);
		this.targetObject = targetObject;
		this.targetObject.references.add(this);
		if (this.origin == null)
			log.debug("ORIGIN IS NULL!!!");
		this.origin.remove(TARGET_ID_FIELD);
		this.origin.addProperty(TARGET_ID_FIELD, getTargetId());
	}
	
}
