/*
 * filter eval function to create a thumbnail attribute
 */
static int
f_eval_thumbnailer(lf_obj_handle_t ohandle, void *data)
{
	struct filter_args *fargs = (struct filter_args *)data;
	RGBImage       *img = NULL, *scaledimg = NULL;
	size_t		img_size = 0;
	int		err = 0;
	double		scale = 1.0;
	int		pass = 0;
	FILE	       *memstream;
	char	       *jpeg_data = NULL;
	size_t		jpeg_len;

	lf_log(LOGL_TRACE, "f_thumbnailer: enter");

	/* read RGB image data */
	err = lf_read_attr(ohandle, RGB_IMAGE, &img_size, NULL);
	if (err && err != ENOMEM) {
		lf_log(LOGL_ERR, "f_thumbnailer: failed to read img attribute");
		return 0;
	}

	img = (RGBImage *)malloc(img_size);
	assert(img);

	err = lf_read_attr(ohandle, RGB_IMAGE, &img_size, (unsigned char *)img);
	assert(!err);

	/* compute scale factor */
	scale = dmax(scale, (double)img->width / fargs->width);
	scale = dmax(scale, (double)img->height / fargs->height);

	scaledimg = image_gen_image_scale(img, (int)ceil(scale));

	if ((scaledimg->height == 0) || (scaledimg->width == 0)) {
	  // fail
	  pass = 1;
	  goto done;
	}

	/* compress jpeg */
	memstream = open_memstream(&jpeg_data, &jpeg_len);
	assert(memstream);
	compress_rgbimage(scaledimg, memstream);
	fclose(memstream);

	/* save thumbnail as an attribute */
	err = lf_write_attr(ohandle, THUMBNAIL_ATTR, jpeg_len,
			    (unsigned char *)jpeg_data);
	assert(!err);
	pass = 1;
done:
	if (img) free(img);
	if (scaledimg) free(scaledimg);
	if (jpeg_data) free(jpeg_data);
	lf_log(LOGL_TRACE, "f_thumbnailer: done");
	return pass;
}
static
int f_eval_xquery (lf_obj_handle_t ohandle, void *filter_args) {
  XQQuery *query = ((struct ctx *) filter_args)->query;
  XQQuery *post_query = ((struct ctx *) filter_args)->post_query;

  // create context objects
  AutoDelete<DynamicContext> context(query->createDynamicContext());
  AutoDelete<DynamicContext> post_context(post_query->createDynamicContext());


  // slurp in the entire object
  size_t len;
  const void *data;
  lf_ref_attr(ohandle, "", &len, &data);

  // parse the document, set it as context item
  xercesc::MemBufInputSource input_source((const XMLByte *) data, len, X("diamond"));
  Node::Ptr doc = context->parseDocument(input_source);
  context->setContextItem(doc);
  context->setContextPosition(1);
  context->setContextSize(1);

  // execute user query
  Result result = query->execute(context);

  // convert into diamond attributes, by executing our "post_query"
  post_context->setContextItem(result->toSequence(context).first());
  post_context->setContextPosition(1);
  post_context->setContextSize(1);

  bool settingName = true;
  char *attributeName = NULL;
  try {
    Result post_result = post_query->execute(post_context);
    Item::Ptr item;
    while(item = post_result->next(post_context)) {
      char *str = strdup(UTF8(item->asString(post_context)));
      if (settingName) {
	attributeName = strdup(str);
      } else {
	//std::cout << "writing attribute '" << attributeName << "':'" << str << "'" << std::endl;
	lf_write_attr(ohandle, attributeName,
		      strlen(str) + 1, (unsigned char *) str);
	free(attributeName);
      }
      free(str);
      settingName = !settingName;
    }
  } catch(XQException &e) {
    std::cerr << "XQException: " << UTF8(e.getError()) << std::endl;
    return 0;
  }

  return 1;
}
static
int f_eval_boostldm(lf_obj_handle_t ohandle, void *f_data)
{
	int err;
	int i;
	boostldm_config_t *fconfig = (boostldm_config_t *) f_data;
	size_t featureLen = MAX_ATTR_VALUE;
	unsigned char featureStr[MAX_ATTR_VALUE];
	int numFeatures;
	double f;
	double distance = 0;
	char fname[MAX_ATTR_NAME];
	
	lf_log(LOGL_TRACE, "f_eval_boostldm: enter");
	
	// extract the features for this object
	err = lf_read_attr(ohandle, NUM_BDMF, &featureLen, featureStr);
	assert(err == 0);
	numFeatures = atoi((char *)featureStr);
	assert(numFeatures == fconfig->numFeatures);

	for(i=0; i<numFeatures; i++) {
		sprintf(fname, "%s%02d", BDMF_PREFIX, i);
		featureLen = MAX_ATTR_VALUE;  // reset, o.w. could be too small
		err = lf_read_attr(ohandle, fname, &featureLen, featureStr);
		assert(err == 0);
		f = atof((char *)featureStr);

		// Given two objects x1[] and x2[], each with 51 bits
		// The weights (alpha) for each bit are given in a file called attr/alpha.
		// We can hardcode them into the searchlet.
	    distance += alpha[i] * (fconfig->features[i] != f);
	}
	
	// scale distance to spread out results
	//	int similarity = 200.0 * exp(negDistance) / (1.0 + exp(negDistance));
	int similarity = 5000.0 * (.2 - distance - 0.04);
	
	printf("distance = %f, similarity %d\n", distance, similarity);
    		
	// save results as attributes
	err = lf_write_attr(ohandle, "similarity", sizeof(int), 
	   					(unsigned char *) &similarity);
	assert(err == 0);

	return similarity;
}
int f_eval_string (lf_obj_handle_t ohandle, void *filter_args) {
  context_t *ctx = (context_t *) filter_args;
  const char *target_str = ctx->target_str;
  int result = 0;

  // slurp in the object
  size_t len;
  const void *data;
  lf_ref_attr(ohandle, "", &len, &data);

  char *source = malloc(len + 1);
  if (source == NULL) {
    return 0;
  }

  memcpy(source, data, len);
  source[len] = '\0';

  char *strstr_result = strstr(source, target_str);

  if (strstr_result != NULL) {
    // found, get index
    int index = strstr_result - source;

    // put into attribute
    char *index_str;
    asprintf(&index_str, "%d", index);
    lf_write_attr(ohandle, "string-index", strlen(index_str) + 1,
		  (unsigned char *) index_str);
    free(index_str);

    result = 1;
  }

  free(source);
  return result;
}