#include "float.h"
#include "gd.h"
#include "vw.h"

namespace MULTILABEL {
  bool is_test_label(labels& ld)
  {
    if (ld.label_v.size() == 0)
      return true;
    else 
      return false;
  }
  
  char* bufread_label(labels* ld, char* c, io_buf& cache)
  {
    size_t num = *(size_t *)c;
    ld->label_v.erase();
    c += sizeof(size_t);
    size_t total = sizeof(uint32_t)*num;
    if (buf_read(cache, c, (int)total) < total) 
      {
        cout << "error in demarshal of cost data" << endl;
        return c;
      }
    for (size_t i = 0; i<num; i++)
      {
        uint32_t temp = *(uint32_t *)c;
        c += sizeof(uint32_t);
        ld->label_v.push_back(temp);
      }
  
    return c;
  }

  size_t read_cached_label(shared_data*, void* v, io_buf& cache)
  {
    labels* ld = (labels*) v;
    ld->label_v.erase();
    char *c;
    size_t total = sizeof(size_t);
    if (buf_read(cache, c, (int)total) < total) 
      return 0;
    c = bufread_label(ld,c, cache);
  
    return total;
  }

  float weight(void*)
  {
    return 1.;
  }

  char* bufcache_label(labels* ld, char* c)
  {
    *(size_t *)c = ld->label_v.size();
    c += sizeof(size_t);
    for (unsigned int i = 0; i< ld->label_v.size(); i++)
      {
        *(uint32_t *)c = ld->label_v[i];
        c += sizeof(uint32_t);
      }
    return c;
  }

  void cache_label(void* v, io_buf& cache)
  {
    char *c;
    labels* ld = (labels*) v;
    buf_write(cache, c, sizeof(size_t)+sizeof(uint32_t)*ld->label_v.size());
    bufcache_label(ld,c);
  }

  void default_label(void* v)
  {
    labels* ld = (labels*) v;
    ld->label_v.erase();
  }

  void delete_label(void* v)
  {
    labels* ld = (labels*)v;
    if (ld) ld->label_v.delete_v();
  }

  void copy_label(void* dst, void* src)
  {
    if (dst && src) {
      labels* ldD = (labels*)dst;
      labels* ldS = (labels*)src;
      copy_array(ldD->label_v, ldS->label_v);
    }
  }

  void parse_label(parser* p, shared_data*, void* v, v_array<substring>& words)
  {
    labels* ld = (labels*)v;

    ld->label_v.erase();
    switch(words.size())
      {
      case 0:
	break;
      case 1:
	tokenize(',', words[0], p->parse_name);
	
	for (size_t i = 0; i < p->parse_name.size(); i++)
	  {
	    *(p->parse_name[i].end) = '\0';
	    uint32_t n = atoi(p->parse_name[i].begin);
	    ld->label_v.push_back(n);
	  }
	break;
      default:
	cerr << "example with an odd label, what is ";
	for (size_t i = 0; i < words.size(); i++)
	  cerr << words[i].begin << " ";
	cerr << endl;
      }
    }


label_parser multilabel = {default_label, parse_label, 
			   cache_label, read_cached_label, 
			   delete_label, weight, 
			   copy_label,
			   sizeof(labels)};

 void print_update(vw& all, bool is_test, example& ec)
  {
    if (all.sd->weighted_examples >= all.sd->dump_interval && !all.quiet && !all.bfgs)
      {
	stringstream label_string;
        if (is_test)
          label_string << " unknown";
        else
	  for(size_t i = 0; i < ec.l.multilabels.label_v.size(); i++)
	    label_string << " " << ec.l.multilabels.label_v[i];

	stringstream pred_string;
	  for(size_t i = 0; i < ec.pred.multilabels.label_v.size(); i++)
	    pred_string << " " << ec.pred.multilabels.label_v[i];
	  
	  
	  all.sd->print_update(all.holdout_set_off, all.current_pass, label_string.str(), pred_string.str(), 
			       ec.num_features, all.progress_add, all.progress_arg);
      }
  }

  void print_multilabel(int f, labels& mls, v_array<char>&)
  {
    if (f >= 0)
      {
	std::stringstream ss;
	
	for (size_t i = 0; i < mls.label_v.size(); i++)
	  {
	    if (i > 0)
	      ss << ',';
	    ss << mls.label_v[i];
	  }
	ss << '\n';
	ssize_t len = ss.str().size();
	ssize_t t = io_buf::write_file_or_socket(f, ss.str().c_str(), (unsigned int)len);
	if (t != len)
	  cerr << "write error: " << strerror(errno) << endl;
      }
  }

  void output_example(vw& all, example& ec)
  {
    labels& ld = ec.l.multilabels;

    float loss = 0.;
    if (!is_test_label(ld))
      {//need to compute exact loss
	labels preds = ec.pred.multilabels;
	labels given = ec.l.multilabels;
	
	uint32_t preds_index = 0;
	uint32_t given_index = 0;

	while(preds_index < preds.label_v.size() && given_index < given.label_v.size())
	  {
	    if (preds.label_v[preds_index] < given.label_v[given_index])
	      preds_index++;
	    else if (preds.label_v[preds_index] > given.label_v[given_index])
	      {
		given_index++;
		loss++;
	      }
	    else
	      {
		preds_index++;
		given_index++;
	      }
	  }
	loss += given.label_v.size() - given_index;
      }

    all.sd->update(ec.test_only, loss, 1.f, ec.num_features);
    
    for (int* sink = all.final_prediction_sink.begin; sink != all.final_prediction_sink.end; sink++)
      print_multilabel(*sink, ec.pred.multilabels, ec.tag);

    print_update(all, is_test_label(ec.l.multilabels), ec);
  }

  bool example_is_test(example& ec)
  {
    return is_test_label(ec.l.multilabels);
  }
}
