#include "system.h"
const char *__progname;

#include <rpmcli.h>

#include "rpmdb.h"
#include "rpmps.h"

#include "rpmte.h"

#define _RPMTS_INTERNAL         /* ts->goal, ts->dbmode, ts->suggests */
#include "rpmts.h"

#include "manifest.h"
#include "misc.h"		/* rpmGlob */
#include "debug.h"

static int noDeps = 1;

static rpmVSFlags vsflags = 0;

static inline /*@observer@*/ const char * const identifyDepend(int_32 f)
	/*@*/
{
    if (isLegacyPreReq(f))
	return "PreReq:";
    f = _notpre(f);
    if (f & RPMSENSE_SCRIPT_PRE)
	return "Requires(pre):";
    if (f & RPMSENSE_SCRIPT_POST)
	return "Requires(post):";
    if (f & RPMSENSE_SCRIPT_PREUN)
	return "Requires(preun):";
    if (f & RPMSENSE_SCRIPT_POSTUN)
	return "Requires(postun):";
    if (f & RPMSENSE_SCRIPT_VERIFY)
	return "Requires(verify):";
    if (f & RPMSENSE_FIND_REQUIRES)
	return "Requires(auto):";
    return "Requires:";
}

static int
rpmGraph(rpmts ts, struct rpmInstallArguments_s * ia, const char ** fileArgv)
	/*@*/
{
    rpmps ps;
    const char ** pkgURL = NULL;
    char * pkgState = NULL;
    const char ** fnp;
    const char * fileURL = NULL;
    int numPkgs = 0;
    int numFailed = 0;
    int prevx = 0;
    int pkgx = 0;
    const char ** argv = NULL;
    int argc = 0;
    const char ** av = NULL;
    int ac = 0;
    Header h;
    rpmRC rpmrc;
    int rc = 0;
    rpmVSFlags ovsflags;
    int i;

    if (fileArgv == NULL)
	return 0;

    if (ia->qva_flags & VERIFY_DIGEST)
	vsflags |= _RPMVSF_NODIGESTS;
    if (ia->qva_flags & VERIFY_SIGNATURE)
	vsflags |= _RPMVSF_NOSIGNATURES;
    ovsflags = rpmtsSetVSFlags(ts, vsflags);

    /* Build fully globbed list of arguments in argv[argc]. */
    for (fnp = fileArgv; *fnp; fnp++) {
	av = _free(av);
	ac = 0;
	rc = rpmGlob(*fnp, &ac, &av);
	if (rc || ac == 0) continue;

	argv = xrealloc(argv, (argc+2) * sizeof(*argv));
	memcpy(argv+argc, av, ac * sizeof(*av));
	argc += ac;
	argv[argc] = NULL;
    }
    av = _free(av);	ac = 0;

restart:
    /* Allocate sufficient storage for next set of args. */
    if (pkgx >= numPkgs) {
	numPkgs = pkgx + argc;
	pkgURL = xrealloc(pkgURL, (numPkgs + 1) * sizeof(*pkgURL));
	memset(pkgURL + pkgx, 0, ((argc + 1) * sizeof(*pkgURL)));
	pkgState = xrealloc(pkgState, (numPkgs + 1) * sizeof(*pkgState));
	memset(pkgState + pkgx, 0, ((argc + 1) * sizeof(*pkgState)));
    }

    /* Copy next set of args. */
    for (i = 0; i < argc; i++) {
	fileURL = _free(fileURL);
	fileURL = argv[i];
	argv[i] = NULL;

	pkgURL[pkgx] = fileURL;
	fileURL = NULL;
	pkgx++;
    }
    fileURL = _free(fileURL);

    /* Continue processing file arguments, building transaction set. */
    for (fnp = pkgURL+prevx; *fnp != NULL; fnp++, prevx++) {
	const char * fileName;
	FD_t fd;

	(void) urlPath(*fnp, &fileName);

	/* Try to read the header from a package file. */
	fd = Fopen(*fnp, "r.ufdio");
	if (fd == NULL || Ferror(fd)) {
	    rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), *fnp,
			Fstrerror(fd));
	    if (fd) {
		Fclose(fd);
		fd = NULL;
	    }
	    numFailed++; *fnp = NULL;
	    continue;
	}

	/* Read the header, verifying signatures (if present). */
	ovsflags = rpmtsSetVSFlags(ts, vsflags);
	rpmrc = rpmReadPackageFile(ts, fd, *fnp, &h);
	ovsflags = rpmtsSetVSFlags(ts, ovsflags);
	Fclose(fd);
	fd = NULL;

	switch (rpmrc) {
	case RPMRC_FAIL:
	default:
	    rpmMessage(RPMMESS_ERROR, _("%s cannot be installed\n"), *fnp);
	    numFailed++; *fnp = NULL;
	    /*@switchbreak@*/ break;
	case RPMRC_OK:
	    rc = rpmtsAddInstallElement(ts, h, (fnpyKey)fileName, 0, NULL);
	    /*@switchbreak@*/ break;
	case RPMRC_NOTFOUND:
	    goto maybe_manifest;
	    /*@notreached@*/ /*@switchbreak@*/ break;
	}
	h = headerFree(h); 
	continue;

maybe_manifest:
	/* Try to read a package manifest. */
	fd = Fopen(*fnp, "r.fpio");
	if (fd == NULL || Ferror(fd)) {
	    rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), *fnp,
			Fstrerror(fd));
	    if (fd) {
		Fclose(fd);
		fd = NULL;
	    }
	    numFailed++; *fnp = NULL;
	    break;
	}

	/* Read list of packages from manifest. */
	rc = rpmReadPackageManifest(fd, &argc, &argv);
	if (rc)
	    rpmError(RPMERR_MANIFEST, _("%s: read manifest failed: %s\n"),
			fileURL, Fstrerror(fd));
	Fclose(fd);
	fd = NULL;

	/* If successful, restart the query loop. */
	if (rc == 0) {
	    prevx++;
	    goto restart;
	}

	numFailed++; *fnp = NULL;
	break;
    }

    if (numFailed > 0) goto exit;

    if (!noDeps) {
	rc = rpmtsCheck(ts);
	if (rc) {
	    numFailed += numPkgs;
	    goto exit;
	}
	ps = rpmtsProblems(ts);
	if (rpmpsNumProblems(ps) > 0) {
	    rpmMessage(RPMMESS_ERROR, _("Failed dependencies:\n"));
	    rpmpsPrint(NULL, ps);
	    numFailed += numPkgs;

            /*@-branchstate@*/
	    if (ts->suggests != NULL && ts->nsuggests > 0) {
		rpmMessage(RPMMESS_NORMAL, _("    Suggested resolutions:\n"));
		for (i = 0; i < ts->nsuggests; i++) {
		    const char * str = ts->suggests[i];

		    if (str == NULL)
			break;

		    rpmMessage(RPMMESS_NORMAL, "\t%s\n", str);
		    ts->suggests[i] = NULL;
		    str = _free(str);
		}
		ts->suggests = _free(ts->suggests);
	    }
	    /*@=branchstate@*/
	}
	ps = rpmpsFree(ps);
    }

    if (numFailed > 0) goto exit;

    rc = rpmtsOrder(ts);
    if (rc)
	goto exit;

    {	rpmtsi pi;
	rpmte p;
	rpmte q;
	unsigned char * selected =
			alloca(sizeof(*selected) * (rpmtsNElements(ts) + 1));
	int oType = TR_ADDED;

	fprintf(stdout, "digraph XXX {\n");

	fprintf(stdout, "  rankdir=LR\n");

	fprintf(stdout, "//===== Packages:\n");
	pi = rpmtsiInit(ts);
	while ((p = rpmtsiNext(pi, oType)) != NULL) {
	    fprintf(stdout, "//%5d%5d %s\n", rpmteTree(p), rpmteDepth(p), rpmteN(p));
	    q = rpmteParent(p);
	    if (q != NULL)
		fprintf(stdout, "  \"%s\" -> \"%s\"\n", rpmteN(p), rpmteN(q));
	    else {
		fprintf(stdout, "  \"%s\"\n", rpmteN(p));
		fprintf(stdout, "  { rank=max ; \"%s\" }\n", rpmteN(p));
	    }
	}
	pi = rpmtsiFree(pi);

	fprintf(stdout, "}\n");
    }

    rc = 0;

exit:
    for (i = 0; i < numPkgs; i++)
        pkgURL[i] = _free(pkgURL[i]);
    pkgState = _free(pkgState);
    pkgURL = _free(pkgURL);
    argv = _free(argv);

    return rc;
}

static struct poptOption optionsTable[] = {
 { "check", '\0', POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN, &noDeps, 0,
	N_("don't verify package dependencies"), NULL },
 { "nolegacy", '\0', POPT_BIT_SET,	&vsflags, RPMVSF_NEEDPAYLOAD,
        N_("don't verify header+payload signature"), NULL },

 { "anaconda", '\0', POPT_BIT_SET|POPT_ARGFLAG_DOC_HIDDEN,
	&rpmIArgs.transFlags, RPMTRANS_FLAG_ANACONDA,
	N_("use anaconda \"presentation order\""), NULL},

 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
	N_("Common options for all rpm modes and executables:"),
	NULL }, 

   POPT_AUTOALIAS
   POPT_AUTOHELP
   POPT_TABLEEND
};

int
main(int argc, char *const argv[])
{
    rpmts ts = NULL;
    struct rpmInstallArguments_s * ia = &rpmIArgs;
    poptContext optCon;
    int ec = 0;

    optCon = rpmcliInit(argc, argv, optionsTable);
    if (optCon == NULL)
	exit(EXIT_FAILURE);

    ts = rpmtsCreate();
    if (rpmcliQueryFlags & VERIFY_DIGEST)
	vsflags |= _RPMVSF_NODIGESTS;
    if (rpmcliQueryFlags & VERIFY_SIGNATURE)
	vsflags |= _RPMVSF_NOSIGNATURES;
    if (rpmcliQueryFlags & VERIFY_HDRCHK)
	vsflags |= RPMVSF_NOHDRCHK;
    (void) rpmtsSetVSFlags(ts, vsflags);

    ec = rpmGraph(ts, ia, poptGetArgs(optCon));

    ts = rpmtsFree(ts);

    optCon = rpmcliFini(optCon);

    return ec;
}
