/*
 * Copyright (c) 2000-2001 Silicon Graphics, Inc.
 * All Rights Reserved.
 *
 * 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.
 *
 * This program is distributed in the hope that it would 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 the Free Software Foundation,
 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdlib.h>
#include <sys/dirent.h>
#include <stdio.h>
#include <getopt.h>
#include <assert.h>

#include "config.h"

#include "types.h"
#include "util.h"
#include "mlog.h"
#include "getopt.h"
#include "stream.h"
#include "global.h"
#include "drive.h"
#include "media.h"

/* media.c - selects and initializes a media strategy
 */


/* declarations of externally defined global symbols *************************/

extern void usage(void);

/* declare all media strategies here
 */
extern media_strategy_t media_strategy_simple;
extern media_strategy_t media_strategy_rmvtape;


/* forward declarations of locally defined static functions ******************/

static media_t *media_alloc(drive_t *, char *);


/* definition of locally defined global variables ****************************/


/* definition of locally defined static variables *****************************/

/* media strategy array - ordered by precedence
 */
static media_strategy_t *strategyp[] = {
	&media_strategy_simple,
	&media_strategy_rmvtape,
};


/* definition of locally defined global functions ****************************/

/* media_create - select and initialize a media strategy.
 * and create and initialize media managers for each stream.
 */
media_strategy_t *
media_create(int argc, char *argv[], drive_strategy_t *dsp)
{
	int c;
	size_t mediaix;
	size_t mediacnt;
	media_t **mediapp;
	char *medialabel;
	media_strategy_t **spp = strategyp;
	media_strategy_t **epp = strategyp + sizeof(strategyp)
					     /
					     sizeof(strategyp[0]);
	media_strategy_t *chosen_sp;
	int id;
	bool_t ok;

	/* sanity check asserts
	 */
	assert(sizeof(media_hdr_t) == MEDIA_HDR_SZ);
	assert(MEDIA_MARKLOG_SZ == sizeof(media_marklog_t));

	/* scan the command line for a media label
	 */
	medialabel = 0;
	optind = 1;
	opterr = 0;
	while ((c = getopt(argc, argv, GETOPT_CMDSTRING)) != EOF) {
		switch (c) {
#ifdef DUMP
		case GETOPT_MEDIALABEL:
			if (medialabel) {
				mlog(MLOG_NORMAL,
				      _("too many -%c arguments: "
				      "\"-%c %s\" already given\n"),
				      c,
				      c,
				      medialabel);
				usage();
				return 0;
			}
			if (!optarg || optarg[0] == '-') {
				mlog(MLOG_NORMAL,
				      _("-%c argument missing\n"),
				      c);
				usage();
				return 0;
			}
			medialabel = optarg;
			break;
#endif /* DUMP */
		}
	}

	/* if no media label specified, synthesize one
	 */
	if (!medialabel) {
		/* not useful
		mlog(MLOG_VERBOSE,
		      _("WARNING: no media label specified\n"));
		*/
		medialabel = "";
	}

	/* create a media_t array, and a media_ts for each drive.
	 * Initialize each media_t's generic portions. these will
	 * be lended to each media strategy during the strategy
	 * match phase, and given to the winning strategy.
	 */
	mediacnt = dsp->ds_drivecnt;
	mediapp = (media_t **)calloc(mediacnt, sizeof(media_t *));
	assert(mediapp);
	for (mediaix = 0; mediaix < mediacnt; mediaix++) {
		mediapp[mediaix] = media_alloc(dsp->ds_drivep[mediaix],
					 	  medialabel);
	}

	/* choose the first strategy which claims appropriateness.
	 * if none match, return null. Also, initialize the strategy ID
	 * and pointer to the drive strategy. the ID is simply the index
	 * of the strategy in the strategy array. it is placed in the
	 * media_strategy_t as well as the write headers.
	 */
	chosen_sp = 0;
	for (id = 0; spp < epp; spp++, id++) {
		(*spp)->ms_id = id;
		if (!chosen_sp) {
			/* lend the media_t array to the strategy
			 */
			(*spp)->ms_mediap = mediapp;
			(*spp)->ms_dsp = dsp;
			(*spp)->ms_mediacnt = mediacnt;
			for (mediaix = 0; mediaix < mediacnt; mediaix++) {
				media_t *mediap = mediapp[mediaix];
				mediap->m_strategyp = *spp;
				mediap->m_writehdrp->mh_strategyid = id;
			}
			if ((*(*spp)->ms_match)(argc, argv, dsp)) {
				chosen_sp = *spp;
			}
		}
	}
	if (!chosen_sp) {
		mlog(MLOG_NORMAL,
#ifdef DUMP
		      _("no media strategy available for selected "
		      "dump destination(s)\n")
#endif /* DUMP */
#ifdef RESTORE
		      _("no media strategy available for selected "
		      "restore source(s)\n")
#endif /* RESTORE */
			);
		usage();
		return 0;
	}

	/* give the media_t array to the chosen strategy
	 */
	for (mediaix = 0; mediaix < mediacnt; mediaix++) {
		media_t *mediap = mediapp[mediaix];
		mediap->m_strategyp = chosen_sp;
		mediap->m_writehdrp->mh_strategyid = chosen_sp->ms_id;
	}

	/* initialize the strategy. this will cause each of the managers
	 * to be initialized as well. if error, return 0.
	 */
	ok = (*chosen_sp->ms_create)(chosen_sp, argc, argv);
	if (!ok) {
		return 0;
	}

	/* return the selected strategy
	 */
	return chosen_sp;
}

bool_t
media_init(media_strategy_t *msp, int argc, char *argv[])
{
	bool_t ok;

	ok = (*msp->ms_init)(msp, argc, argv);

	return ok;
}

void
media_complete(media_strategy_t *msp)
{
	(*msp->ms_complete)(msp);
}

/* media_get_upper_hdrs - supply pointers to portion of media file headers
 * set aside for upper software layers, as well as to the global hdrs
 */
void
media_get_upper_hdrs(media_t *mediap,
		      global_hdr_t **grhdrpp,
		      char **rhdrpp,
		      size_t *rhdrszp,
		      global_hdr_t **gwhdrpp,
		      char **whdrpp,
		      size_t *whdrszp)
{
	*grhdrpp = mediap->m_greadhdrp;
	*rhdrpp = mediap->m_readhdrp->mh_upper;
	*rhdrszp = sizeof(mediap->m_readhdrp->mh_upper);

	*gwhdrpp = mediap->m_gwritehdrp;
	*whdrpp = mediap->m_writehdrp->mh_upper;
	*whdrszp = sizeof(mediap->m_writehdrp->mh_upper);
}


/* definition of locally defined static functions ****************************/

/* media_alloc - allocate and initialize the generic portions of a media
 * descriptor and read and write media headers
 */
static media_t *
media_alloc(drive_t *drivep,
	     char *medialabel)
{
	media_t *mediap;
	global_hdr_t *grhdrp;
	global_hdr_t *gwhdrp;
	media_hdr_t *mrhdrp;
	media_hdr_t *mwhdrp;
	size_t mrhdrsz;
	size_t mwhdrsz;

	mediap = (media_t *)calloc(1, sizeof(media_t));
	assert(mediap);

	grhdrp = 0;
	gwhdrp = 0;
	mrhdrp = 0;
	mwhdrp = 0;
	drive_get_upper_hdrs(drivep,
			      &grhdrp,
			      (char **)&mrhdrp,
			      &mrhdrsz,
			      &gwhdrp,
			      (char **)&mwhdrp,
			      &mwhdrsz);
	assert(grhdrp);
	assert(gwhdrp);
	assert(mrhdrp);
	assert(mwhdrp);
	assert(mrhdrsz == MEDIA_HDR_SZ);
	assert(mwhdrsz == MEDIA_HDR_SZ);

	mediap->m_greadhdrp = grhdrp;
	mediap->m_gwritehdrp = gwhdrp;
	mediap->m_readhdrp = mrhdrp;
	mediap->m_writehdrp = mwhdrp;
	mediap->m_drivep = drivep;

	strncpyterm(mwhdrp->mh_medialabel,
		     medialabel,
		     sizeof(mwhdrp->mh_medialabel));

#ifdef DUMP
	uuid_create(mwhdrp->mh_mediaid);
#else /* DUMP */
	uuid_clear(mwhdrp->mh_mediaid);
#endif /* DUMP */

	return mediap;
}
