#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <getopt.h>
#include <string.h>
#include <limits.h>
#include "sysfs.h"
#include "si.h"

struct si_net_data {
	char			* d_device;
	char			* d_group;
	char			* d_attr;
	char			* d_value;

	struct record_col	d_group_rc;

	struct record_col	d_obj_rc;
	struct record_col	d_attr_rc;
	struct record_col	d_link_rc;
};

/**
 *	net_init - Check arguments and allocate private data structures.
 *	@a:	Session data.
 */

static int net_init(struct si_action * a)
{
	struct si_net_data * data;

	if (a->a_argc > 4)
		return -EINVAL;

	data = calloc(1, sizeof(struct si_net_data));
	if (!data)
		return -ENOMEM;

	switch (a->a_argc) { 
	case 4:
		data->d_value = a->a_argv[3];
	case 3:
		data->d_attr = a->a_argv[2];
	case 2:
		data->d_group = a->a_argv[1];
	case 1:
		data->d_device = a->a_argv[0];
	default:
		break;
	}
	a->a_data = data;
	return 0;
}


static int read_one_attr(struct si_action * a, struct record_col * rc, char * name)
{
	char buffer[PAGE_SIZE];
	int error;

	error = rc_init(rc, 1);
	if (error)
		return error;

	error = sysfs_read_file(name, buffer);
	if (error < 0)
		goto RecordFail;

	error = record_add(rc, "%s %s", name, buffer); 
	if (error)
		goto RecordFail;
	rc_add(a, rc);
	return 0;

 RecordFail:
	rc_exit(rc);
	return error;
}

static int write_attr(char * attr, char * value)
{
	int error;
	error = sysfs_write_file(attr, value, strlen(value));
	if (error > 0)
		error = 0;
	return error;
}

static int record_one_group(struct si_action * a, char * name, struct record_col * rc)
{
	struct si_net_data * data = a->a_data;
	int error;
	
	/*
	 * Check what group we're looking for.
	 * If it's 'attr', then we don't descend and just read the
	 * attributes in the current directory.
	 */

	if (!strcmp(name, "attr")) {
		if (data->d_attr) {
			if (data->d_value)
				error = write_attr(data->d_attr, data->d_value);
			else
				error = read_one_attr(a, rc, data->d_attr);
		} else
			error = read_attributes(a, rc);
	} else {
		struct sysfs_object so;
		error = sysfs_object_init(&so, name);
		if (error)
			return error;

		if (data->d_attr) {
			if (data->d_value)
				error = write_attr(data->d_attr, data->d_value);
			else
				error = read_one_attr(a, rc, data->d_attr);
		} else {
			error = read_attributes(a, rc);
			if (!error)
				rc->rc_name = name;
		}
		sysfs_object_exit(&so);
	}
	record_first(rc, "[%s]", name);
	return error;
}

#if 0
static int record_groups(struct si_action * a)
{
	struct sysfs_object_list sol;
	struct si_net_data * data = a->a_data;
	int error;
	int i;

	error = sysfs_list_children(&sol);
	if (error)
		return error;

	data->d_group_cols = calloc(sol.sol_num, sizeof(struct record_col));
	if (!data->d_group_cols) {
		error = -ENOMEM;
		goto Unlist;
	}

	for (i = 0; i < sol.sol_num; i++) {
		error = record_one_group(a, sol.sol_list[i], &data->d_group_cols[i]);
		if (error)
			goto GroupFail;
	}
	data->d_group_num = sol.sol_num;

 Unlist:
	sysfs_unlist_children(&sol);
	return error;

 GroupFail:
	do {
		rc_exit(&data->d_group_cols[i]);
	} while (i > 0);
	free(data->d_group_cols);
}
#endif

static int record_basic_stuff(struct si_action * a)
{
	struct si_net_data * data = a->a_data;
	int error;

	error = record_links(a, &data->d_link_rc);
	if (error)
		return error;

	error = read_attributes(a, &data->d_attr_rc);
	if (error)
		goto FreeLinks;

	error = record_children(a, &data->d_obj_rc);
	if (error)
		goto FreeAttr;
	return 0;

 FreeAttr:
	rc_exit(&data->d_attr_rc);
 FreeLinks:
	rc_exit(&data->d_link_rc);
	return error;
}

static int record_one(struct si_action * a)
{
	struct si_net_data * data = a->a_data;
	struct sysfs_object so;
	char class_path[PATH_MAX];
	int error = 0;
	int len;

	len = snprintf(class_path, PATH_MAX, "class/net/%s", data->d_device);
	if (len > PATH_MAX)
		return -EOVERFLOW;

	error = sysfs_object_init(&so, class_path);
	if (error)
		return error;

	if (data->d_group)
		error = record_one_group(a, data->d_group, &data->d_group_rc);
	else
		error = record_basic_stuff(a);

	sysfs_object_exit(&so);
	return error;
}

static int record_all(struct si_action * a)
{
	struct si_net_data * data = a->a_data;
	struct sysfs_object so;
	int error = 0;

	error = sysfs_object_init(&so, "class/net");
	if (error)
		return error;

	error = record_children(a, &data->d_obj_rc);
	sysfs_object_exit(&so);
	return error;
}

static int net_exec(struct si_action * a)
{
	struct si_net_data * data = a->a_data;
	int error = 0;

	if (data->d_device)
		error = record_one(a);
	else
		error = record_all(a);

	return error;
}

static void net_exit(struct si_action * a)
{
	struct si_net_data * data = a->a_data;

	a->a_data = NULL;

	rc_exit(&data->d_obj_rc);
	rc_exit(&data->d_attr_rc);
	rc_exit(&data->d_link_rc);

	if (data->d_group)
		rc_exit(&data->d_group_rc);

	free(data);
}

static const char * net_help = "Print Network Information.";
static const char * net_usage = "[<device> [ <group> [ <attr> [ <value> ] ] ] ]";

decl_cmd(net);

