/*
 *  sync.c
 * 
 *  Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org>
 * 
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
 *  USA.
 */

#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* pacman */
#include "log.h"
#include "util.h"
#include "list.h"
#include "package.h"
#include "db.h"
#include "cache.h"
#include "deps.h"
#include "trans.h"
#include "sync.h"
#include "rpmvercmp.h"
#include "handle.h"

extern pmhandle_t *handle;

pmsync_t *sync_new(int type, pmpkg_t *lpkg, pmpkg_t *spkg)
{
	pmsync_t *sync;

	if((sync = (pmsync_t *)malloc(sizeof(pmsync_t))) == NULL) {
		return(NULL);
	}

	sync->type = type;
	sync->lpkg = lpkg;
	sync->spkg = spkg;
	
	return(sync);
}

int sync_sysupgrade(PMList **data)
{
	PMList *i, *j, *k;
	PMList *targets = NULL;

	*data = NULL;

	/* check for "recommended" package replacements */
	for(i = handle->dbs_sync; i; i = i->next) {
		PMList *j;

		for(j = db_get_pkgcache(i->data); j; j = j->next) {
			pmpkg_t *spkg = j->data;

			for(k = spkg->replaces; k; k = k->next) {
				PMList *m;

				for(m = db_get_pkgcache(handle->db_local); m; m = m->next) {
					pmpkg_t *lpkg = m->data;

					if(!strcmp(k->data, lpkg->name)) {
						if(pm_list_is_strin(lpkg->name, handle->ignorepkg)) {
							_alpm_log(PM_LOG_WARNING, "%s-%s: ignoring package upgrade (to be replaced by %s-%s)",
								lpkg->name, lpkg->version, spkg->name, spkg->version);
						} else {
							pmsync_t *sync = sync_new(PM_SYSUPG_REPLACE, lpkg, spkg);

							if(sync == NULL) {
								pm_errno = PM_ERR_MEMORY;
								goto error;
							}

							targets = pm_list_add(targets, sync);
						}
					}
				}
			}
		}
	}

	/* match installed packages with the sync dbs and compare versions */
	for(i = db_get_pkgcache(handle->db_local); i; i = i->next) {
		int cmp;
		pmpkg_t *local = i->data;
		pmpkg_t *spkg = NULL;
		pmsync_t *sync;

		for(j = handle->dbs_sync; !spkg && j; j = j->next) {

			for(k = db_get_pkgcache(j->data); !spkg && k; k = k->next) {
				pmpkg_t *sp = k->data;

				if(!strcmp(local->name, sp->name)) {
					spkg = sp;
				}
			}
		}
		if(spkg == NULL) {
			/*fprintf(stderr, "%s: not found in sync db.  skipping.", local->name);*/
			continue;
		}

		/* compare versions and see if we need to upgrade */
		cmp = rpmvercmp(local->version, spkg->version);
		if(cmp > 0 && !spkg->force) {
			/* local version is newer */
			_alpm_log(PM_LOG_FLOW1, "%s-%s: local version is newer",
				local->name, local->version);
			continue;
		} else if(cmp == 0) {
			/* versions are identical */
			continue;
		} else if(pm_list_is_strin(i->data, handle->ignorepkg)) {
			/* package should be ignored (IgnorePkg) */
			_alpm_log(PM_LOG_FLOW1, "%s-%s: ignoring package upgrade (%s)",
				local->name, local->version, spkg->version);
			continue;
		}

		sync = sync_new(PM_SYSUPG_UPGRADE, local, spkg);
		if(sync == NULL) {
			pm_errno = PM_ERR_MEMORY;
			goto error;
		}

		targets = pm_list_add(targets, sync);
	}

	*data = targets;

	return(0);

error:
	FREELIST(targets);
	return(-1);
}

int sync_resolvedeps(PMList **syncs)
{
	return(0);
}

int sync_prepare(pmdb_t *db, pmtrans_t *trans, PMList **data)
{
	PMList *i;
	PMList *trail = NULL;

	/* Resolve targets dependencies */
	for(i = trans->targets; i; i = i->next) {
		if(resolvedeps(handle->db_local, handle->dbs_sync, i->data, trans->targets, trail, data) == -1) {
			/* pm_errno is set by resolvedeps */
			goto error;
		}
	}

	/* ORE
	check for inter-conflicts and whatnot */

	/* ORE
	any packages in rmtargs need to be removed from final.
	rather than ripping out nodes from final, we just copy over
	our "good" nodes to a new list and reassign. */

	/* ORE
	Check dependencies of packages in rmtargs and make sure
	we won't be breaking anything by removing them.
	If a broken dep is detected, make sure it's not from a
	package that's in our final (upgrade) list. */

	return(0);

error:
	return(-1);
}

int sync_commit(pmdb_t *db, pmtrans_t *trans)
{
	PMList *i, *files = NULL;
	PMList *final = NULL;
	PMList *data;
	pmtrans_t *tr;

	/* remove any conflicting packages (WITHOUT dep checks) */

	/* remove to-be-replaced packages */

	/* install targets */
	tr = trans_new(PM_TRANS_TYPE_UPGRADE, 0);
	for(i = files; i; i = i->next) {
		trans_addtarget(tr, i->data);
	}

	trans_prepare(tr, &data);

	trans_commit(tr);

	trans_free(tr);

	/* propagate replaced packages' requiredby fields to their new owners */
	for(i = final; i; i = i->next) {
		/*syncpkg_t *sync = (syncpkg_t*)i->data;
		if(sync->replaces) {
			pkginfo_t *new = db_scan(db, sync->pkg->name, INFRQ_DEPENDS);
			for(j = sync->replaces; j; j = j->next) {
				pkginfo_t *old = (pkginfo_t*)j->data;
				// merge lists
				for(k = old->requiredby; k; k = k->next) {
					if(!is_in(k->data, new->requiredby)) {
						// replace old's name with new's name in the requiredby's dependency list
						PMList *m;
						pkginfo_t *depender = db_scan(db, k->data, INFRQ_DEPENDS);
						for(m = depender->depends; m; m = m->next) {
							if(!strcmp(m->data, old->name)) {
								FREE(m->data);
								m->data = strdup(new->name);
							}
						}
						db_write(db, depender, INFRQ_DEPENDS);

						// add the new requiredby
						new->requiredby = list_add(new->requiredby, strdup(k->data));
					}
				}
			}
			db_write(db, new, INFRQ_DEPENDS);
			FREEPKG(new);
		}*/
	}

	/* cache needs to be rebuilt */
	db_free_pkgcache(db);

	return(0);
}

/* vim: set ts=2 sw=2 noet: */