/*
 * Copyright (c) International Business Machines Corp., 2006
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Author: Oliver Lohmann
 *
 * 1.2 Removed argp because we want to use uClibc.
 * 1.3 Minor cleanups
 * 1.4 Migrated to new libubi
 */

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <errno.h>
#include <mtd/ubi-media.h>

#include "config.h"
#include "error.h"
#include "example_ubi.h"
#include "ubimirror.h"

#define PROGRAM_VERSION "1.4"

typedef enum action_t {
	ACT_NORMAL = 0,
	ACT_ARGP_ABORT,
	ACT_ARGP_ERR,
} action_t;

#define ABORT_ARGP do {			\
	args->action = ACT_ARGP_ABORT;	\
} while (0)

#define ERR_ARGP do {			\
	args->action = ACT_ARGP_ERR;	\
} while (0)

#define VOL_ARGS_MAX 2

static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
	"ubimirror - mirrors ubi volumes.\n";

static const char *optionsstr =
"  -c, --copyright            Print copyright information.\n"
"  -s, --side=<seqnum>        Use the side <seqnum> as source.\n"
"  -?, --help                 Give this help list\n"
"      --usage                Give a short usage message\n"
"  -V, --version              Print program version\n";

static const char *usage =
"Usage: ubimirror [-c?V] [-s <seqnum>] [--copyright] [--side=<seqnum>]\n"
"            [--help] [--usage] [--version] <source> <destination>\n";

static const char copyright [] __attribute__((unused)) =
	"(C) IBM Coorporation 2007";

struct option long_options[] = {
	{ .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
	{ .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
	{ .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
	{ .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
	{ .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
	{ NULL, 0, NULL, 0}
};

typedef struct myargs {
	action_t action;
	int side;
	int vol_no;			/* index of current volume */
	/* @FIXME replace by bootenv_list, makes live easier */
	/* @FIXME remove the constraint of two entries in the array */
	const char* vol[VOL_ARGS_MAX];	/* comma separated list of src/dst
					   volumes */
	char *arg1;
	char **options;		/* [STRING...] */
} myargs;

static int
get_update_side(const char* str)
{
	uint32_t i = strtoul(str, NULL, 0);

	if ((i != 0) && (i != 1)) {
		return -1;
	}
	return i;
}


static int
parse_opt(int argc, char **argv, myargs *args)
{
	while (1) {
		int key;

		key = getopt_long(argc, argv, "cs:?V", long_options, NULL);
		if (key == -1)
			break;

		switch (key) {
			case 'c':
				err_msg("%s", copyright);
				ABORT_ARGP;
				break;
			case 's':
				args->side = get_update_side(optarg);
				if (args->side < 0) {
					err_msg("Unsupported seqnum: %s.\n"
						"Supported seqnums are '0' "
						"and '1'\n", optarg);
					ERR_ARGP;
				}
				break;
			case '?': /* help */
				err_msg("Usage: ubimirror [OPTION...] "
					"<source> <destination>\n");
				err_msg("%s", doc);
				err_msg("%s", optionsstr);
				err_msg("\nReport bugs to %s\n",
					PACKAGE_BUGREPORT);
				exit(0);
				break;
			case 'V':
				err_msg("%s", PROGRAM_VERSION);
				exit(0);
				break;
			default:
				err_msg("%s", usage);
				exit(-1);
			}
	}

	while (optind < argc) {
		/* only two entries allowed */
		if (args->vol_no >= VOL_ARGS_MAX) {
			err_msg("%s", usage);
			ERR_ARGP;
		}
		args->vol[(args->vol_no)++] = argv[optind++];
	}

	return 0;
}


int
main(int argc, char **argv) {
	int rc = 0;
	unsigned int ids[VOL_ARGS_MAX];
	char err_buf[1024];

	myargs args = {
		.action = ACT_NORMAL,
		.side = -1,
		.vol_no = 0,
		.vol = {"", ""},
		.options = NULL,
	};

	parse_opt(argc, argv, &args);
	if (args.action == ACT_ARGP_ERR) {
		rc = 127;
		goto err;
	}
	if (args.action == ACT_ARGP_ABORT) {
		rc = 126;
		goto out;
	}
	if (args.vol_no < VOL_ARGS_MAX) {
		fprintf(stderr, "missing volume number for %s\n",
			args.vol_no == 0 ? "source and target" : "target");
		rc = 125;
		goto out;
	}
	for( rc = 0; rc < args.vol_no; ++rc){
		char *endp;
		ids[rc] = strtoul(args.vol[rc], &endp, 0);
		if( *endp != '\0' ){
			fprintf(stderr, "invalid volume number %s\n",
					args.vol[rc]);
			rc = 125;
			goto out;
		}
	}
	rc = ubimirror(EXAMPLE_UBI_DEVICE, args.side, ids, args.vol_no,
		       err_buf, sizeof(err_buf));
	if( rc ){
		err_buf[sizeof err_buf - 1] = '\0';
		fprintf(stderr, "%s", err_buf);
		if( rc < 0 )
			rc = -rc;
	}
 out:
 err:
	return rc;
}