/*
 * determine if instances are associated based on the ConcreteDependency association
 */
bool wbem::memory::MemoryControllerFactory::isAssociated(const std::string &associationClass,
		framework::Instance* pAntInstance,
		framework::Instance* pDepInstance)
{
	LogEnterExit logging(__FUNCTION__, __FILE__, __LINE__);
	bool result = false;

	if (associationClass == framework_interface::ASSOCIATION_CLASS_CONCRETEDEPENDENCY)
	{
		framework::Attribute antAttribute;
		framework::Attribute depAttribute;
		if (pAntInstance->getAttribute(PROCESSORAFFINITY_KEY, antAttribute) == framework::SUCCESS &&
				pDepInstance->getAttribute(DEVICEID_KEY, depAttribute) == framework::SUCCESS)
		{
			std::string depString = framework::StringUtil::removeString(depAttribute.asStr(), SYSTEMPROCESSOR_DEVICEID_PREFIX);
			std::string antString = antAttribute.asStr();

			int depSocketId = framework::StringUtil::stringToUint64(depString);
			int antSocketId = framework::StringUtil::stringToUint64(antString);

			result = (antSocketId == depSocketId);
		}
	}
	else
	{
		COMMON_LOG_WARN_F("Cannot calculate if instances are an association "
				"based on association class: %s", associationClass.c_str());
	}

	return result;


}
/*
 * splits a instance ID attribute into uid + decoded metric type.
 * sets deviceUid and 'type' and returns non-zero on success.
 */
bool wbem::performance::PerformanceMetricFactory::splitInstanceID(
	const framework::Attribute& instanceId, std::string &deviceUid, metric_type& metric)
{
	LogEnterExit logging(__FUNCTION__, __FILE__, __LINE__);
	bool found = false;
	metric = METRIC_UNDEFINED;

	const std::string &instanceIdStr = instanceId.stringValue();
	int index = core::device::findUidStart(instanceIdStr);
	if (index >= 0)
	{
		found = true;
		deviceUid = instanceIdStr.substr(index);
		std::string metricStr = instanceIdStr.substr(0, index);
		metric = stringToMetric(metricStr);
	}
	else
	{
		COMMON_LOG_WARN_F("Could not find a uid in %s", instanceId.stringValue().c_str());
	}

	return found;
}
/*
 * Determine if the metric value is associated based on device uid.
 */
bool wbem::performance::PerformanceMetricFactory::isAssociated(
		const std::string &associationClass,
		framework::Instance* pAntInstance,
		framework::Instance* pDepInstance)
{
	LogEnterExit logging(__FUNCTION__, __FILE__, __LINE__);
	bool result = false;

	if (associationClass == wbem::framework_interface::ASSOCIATION_CLASS_METRIC_FOR_ME)
	{
		if (pDepInstance->getClass() == wbem::performance::PERFORMANCE_METRIC_CREATIONCLASSNAME
			&& pAntInstance->getClass() == wbem::physical_asset::NVDIMM_CREATIONCLASSNAME)
		{
			std::vector<std::string> stringsToRemove;
			stringsToRemove.push_back(wbem::performance::METRICVAL_BYTES_READ_STR);
			stringsToRemove.push_back(wbem::performance::METRICVAL_BYTES_WRITTEN_STR);
			stringsToRemove.push_back(wbem::performance::METRICVAL_HOST_READS_STR);
			stringsToRemove.push_back(wbem::performance::METRICVAL_HOST_WRITES_STR);
			stringsToRemove.push_back(wbem::performance::METRICVAL_BLOCK_READS_STR);
			stringsToRemove.push_back(wbem::performance::METRICVAL_BLOCK_WRITES_STR);
			result = framework_interface::NvmAssociationFactory::filteredFkMatch(
					pAntInstance, TAG_KEY, std::vector<std::string>(),
					pDepInstance, INSTANCEID_KEY, stringsToRemove);
		}
		else
		{
			COMMON_LOG_WARN("Incorrect antecedent and dependent class instances.");
		}
	}
	else
	{
		COMMON_LOG_WARN_F("Cannot calculate if instances are an association "
				"based on association class: %s", associationClass.c_str());
	}

	return result;
}
/*
 * Execute a passthrough IOCTL
 */
int ioctl_passthrough_cmd(struct fw_cmd *p_cmd)
{
	COMMON_LOG_ENTRY();
	int rc = NVM_ERR_UNKNOWN;

	if (p_cmd == NULL)
	{
		COMMON_LOG_ERROR("Invalid parameter, cmd is NULL");
		rc = NVM_ERR_INVALIDPARAMETER;
	}
	else if ((p_cmd->input_payload_size > 0 && p_cmd->input_payload == NULL) ||
			(p_cmd->input_payload != NULL && p_cmd->input_payload_size == 0) ||
			(p_cmd->output_payload_size > 0 && p_cmd->output_payload == NULL) ||
			(p_cmd->output_payload != NULL && p_cmd->output_payload_size == 0) ||
			(p_cmd->large_input_payload_size > 0 && p_cmd->large_input_payload == NULL) ||
			(p_cmd->large_input_payload != NULL && p_cmd->large_input_payload_size == 0) ||
			(p_cmd->large_output_payload_size > 0 && p_cmd->large_output_payload == NULL) ||
			(p_cmd->large_output_payload != NULL && p_cmd->large_output_payload_size == 0))
	{
		COMMON_LOG_ERROR("bad input/output payload(s)");
		rc = NVM_ERR_INVALIDPARAMETER;
	}
	// avoid any commands that require early HW or large payloads
#if __EARLY_HW__ || (__LARGE_PAYLOAD__ == 0)
	else if ((p_cmd->opcode == 0x08 && p_cmd->sub_opcode == 0x02) || // get fw debug log
			(p_cmd->opcode == 0x08 && p_cmd->sub_opcode == 0x05) || // get error log
			(p_cmd->opcode == 0x0A)) // inject error
	{
		COMMON_LOG_ERROR_F("Intel DIMM Gen 1 FW command OpCode: 0x%x SubOpCode: "
				"0x%x is not supported",
				p_cmd->opcode, p_cmd->sub_opcode);
		rc = NVM_ERR_NOTSUPPORTED;
	}
#endif
	else
	{
		size_t input_buf_size = sizeof (DSM_VENDOR_SPECIFIC_COMMAND_INPUT_PAYLOAD)
				- ARG3_OPCODE_PARAMETER_DATA_BUFFER_PLACEHOLDER	+ p_cmd->input_payload_size;

		size_t output_buf_size = sizeof (DSM_VENDOR_SPECIFIC_COMMAND_OUTPUT_PAYLOAD)
			- DSM_VENDOR_SPECIFIC_COMMAND_OUTPUT_PAYLOAD_PLACEHOLDERS
			+ DEV_SMALL_PAYLOAD_SIZE;

		size_t arg3_size = input_buf_size + output_buf_size;
		size_t buf_size = sizeof (ULONG) + sizeof (NFIT_DEVICE_HANDLE) + arg3_size;

		PPASS_THROUGH_IOCTL p_ioctl_data = calloc(1, buf_size);
		if (p_ioctl_data)
		{
			p_ioctl_data->NfitDeviceHandle.DeviceHandle =
					p_cmd->device_handle;

			p_ioctl_data->InputPayload.Arg3OpCodeParameterDataLength = p_cmd->input_payload_size;
			p_ioctl_data->InputPayload.Arg3OpCode = BUILD_DSM_OPCODE(p_cmd->opcode,
					p_cmd->sub_opcode);

			if (p_cmd->input_payload_size > 0)
			{
				memmove(p_ioctl_data->InputPayload.Arg3OpCodeParameterDataBuffer,
						p_cmd->input_payload,
						p_cmd->input_payload_size);
			}

			if (p_cmd->large_input_payload_size > 0)
			{
				rc = bios_write_large_payload(p_cmd);
			}

			PDSM_VENDOR_SPECIFIC_COMMAND_OUTPUT_PAYLOAD p_output_payload = NULL;

			p_output_payload =
				(PDSM_VENDOR_SPECIFIC_COMMAND_OUTPUT_PAYLOAD)
				(p_ioctl_data->InputPayload.Arg3OpCodeParameterDataBuffer
					+ p_cmd->input_payload_size);

			if ((rc = execute_ioctl(buf_size, p_ioctl_data, IOCTL_CR_PASS_THROUGH))
				== NVM_SUCCESS &&
				(rc = ind_err_to_nvm_lib_err(p_ioctl_data->ReturnCode)) == NVM_SUCCESS)
			{
				if ((rc = dsm_err_to_nvm_lib_err(
					win_dsm_status_to_int(p_output_payload->Arg3Status))) == NVM_SUCCESS)
				{
					if (p_cmd->output_payload_size > 0)
					{
						size_t bytes_to_copy = p_cmd->output_payload_size;
						if (p_output_payload->Arg3OutputBufferLength <
								p_cmd->output_payload_size)
						{
							// User expected more data than the command returned
							// We can safely copy it, but there could be a developer error
							bytes_to_copy = p_output_payload->Arg3OutputBufferLength;
							COMMON_LOG_WARN_F(
									"output buffer size %llu larger than size returned %u",
									p_cmd->output_payload_size,
									p_output_payload->Arg3OutputBufferLength);
						}
						memmove(p_cmd->output_payload, p_output_payload->Arg3OutputBuffer,
								bytes_to_copy);
					}

					if (p_cmd->large_output_payload_size > 0)
					{
						rc = bios_read_large_payload(p_cmd);
					}
				}
			}

			free(p_ioctl_data);
		}
		else
		{
			COMMON_LOG_ERROR("couldn't allocate input payload");
			rc = NVM_ERR_NOMEMORY;
		}
	}

	s_memset(&p_cmd, sizeof (p_cmd));
	COMMON_LOG_EXIT_RETURN_I(rc);
	return rc;
}