// -*- mode: c++; c-basic-offset: 4 -*-
/*
 * fromdump.{cc,hh} -- element reads packets from handler
 * John Bicket
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Click LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Click LICENSE file; the license in that file is
 * legally binding.
 */

#include <click/config.h>
#include "fromhandler.hh"
#include <click/confparse.hh>
#include <click/router.hh>
#include <click/straccum.hh>
#include <click/standard/scheduleinfo.hh>
#include <click/error.hh>
#include <click/glue.hh>
#include <click/handlercall.hh>
#include <click/packet_anno.hh>
#include <click/userutils.hh>
CLICK_DECLS

FromHandler::FromHandler()
    :  _task(this)
{
}

FromHandler::~FromHandler()
{

}

int
FromHandler::configure(Vector<String> &conf, ErrorHandler *errh)
{
    if (cp_va_kparse(conf, this, errh,
		     "HANDLER", cpkP+cpkM, cpString, &_handler,
		     cpEnd) < 0)
	return -1;

    _active = false;
    return 0;
}

int
FromHandler::initialize(ErrorHandler *errh)
{
    ScheduleInfo::initialize_task(this, &_task, errh);
    return 0;
}

u_int8_t
char_to_hex(char c)
{
    switch (c) {
    case '0': return 0;
    case '1': return 1;
    case '2': return 2;
    case '3': return 3;
    case '4': return 4;
    case '5': return 5;
    case '6': return 6;
    case '7': return 7;
    case '8': return 8;
    case '9': return 9;
    case 'a': return 10;
    case 'b': return 11;
    case 'c': return 12;
    case 'd': return 13;
    case 'e': return 14;
    case 'f': return 15;
    }
    return 0;
}
bool
FromHandler::get_packet()
{
    String s = HandlerCall::call_read(_handler, this, ErrorHandler::default_handler());
    if (s.length() <= 1) {
	return false;
    }
    Timestamp t = Timestamp::now();
    int n = s.find_left('|', 0);
    if (n > 0) {
	String ts = s.substring(0, n-1);
	if (!cp_time(ts, &t)) {
	    click_chatter("parsing time failed\n");
	}
	s = s.substring(n+2, s.length() - n - 1);
    }
    WritablePacket *p = Packet::make(s.length()/2);
    if (!p) {
	return false;
    }
    p->set_timestamp_anno(t);
    memset(p->data(), 0, p->length());
    for (int x = 0; x + 1 < s.length(); x += 2) {
	p->data()[x/2] = (char_to_hex(s[x]) << 4) | char_to_hex(s[x + 1]);
    }
    output(0).push(p);
    return true;
}

bool
FromHandler::run_task(Task *)
{
    if (!_active) {
	return false;
    }
    bool a = get_packet();
    if (a) {
	_task.fast_reschedule();
	return true;
    }
    if (_active) {
	_end = Timestamp::now();
    }
    _active = false;
    return false;
}


void
FromHandler::set_active(bool b) {
    _active = b;
    if (_active) {
	_start = Timestamp::now();
	_end = _start;
	_task.reschedule();
    }
}
enum { H_ACTIVE, H_TIME};

String
FromHandler::read_handler(Element *e, void *thunk)
{
    FromHandler *fd = static_cast<FromHandler *>(e);
    switch ((intptr_t)thunk) {
      case H_ACTIVE:
	return cp_unparse_bool(fd->_active) + "\n";
    case H_TIME: {
	StringAccum sa;
	sa << fd->_end - fd->_start << "\n";
	return sa.take_string();
    }
      default:
	return "<error>\n";
    }
}

int
FromHandler::write_handler(const String &s_in, Element *e, void *thunk, ErrorHandler *errh)
{
    FromHandler *fd = static_cast<FromHandler *>(e);
    String s = cp_uncomment(s_in);
    switch ((intptr_t)thunk) {
      case H_ACTIVE: {
	  bool active;
	  if (cp_bool(s, &active)) {
	      fd->set_active(active);
	      return 0;
	  } else
	      return errh->error("'active' should be Boolean");
      }
      default:
	return -EINVAL;
    }
}

void
FromHandler::add_handlers()
{
    add_read_handler("time", read_handler, (void *)H_TIME);
    add_read_handler("active", read_handler, (void *)H_ACTIVE);
    add_write_handler("active", write_handler, (void *)H_ACTIVE);
}

CLICK_ENDDECLS
ELEMENT_REQUIRES(userlevel)
EXPORT_ELEMENT(FromHandler)
