/* @(#)link.c 1.3 95/09/16 */

/*
 * Copyright (c) 1994, 1995 by Wayne C. Gramlich.  All rights reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * for any purpose is hereby granted without fee provided that the above
 * copyright notice and this permission are retained.  The author makes
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 */

#include "assert.h"
#include "config_extern.h"
#include "error_extern.h"
#include "html_extern.h"
#include "link_extern.h"
#include "local_extern.h"
#include "memory_extern.h"
#include "remote_extern.h"
#include "str_extern.h"
#include "url_extern.h"
#include "vector_extern.h"
#include "vote_extern.h"

struct link_struct {
    Str describe;	/* DESCRIBE field of link or (or "") */
    Url href;		/* HREF field of link (never "") */
    Local local;	/* Local document associated with link (or 0) */
    Str name;		/* NAME field of link (anchor name) (or "") */
    Remote remote;	/* Remote document this link is a member of */
    Votes votes;	/* Votes associated with link */
};

static Link link_restore(Remote, FILE *, Errors);
static void link_save(Link, FILE *);

/* 
 * link_create(remote, href, name, describe)
 *	This routine will create and return a {Link} object with the
 *	{remote}, {href}, {name,} and {describe} fields filled in.
 */
Link
link_create(
    Remote remote,
    Url href,
    Str name,
    Str describe)
{
    Link link;

    link = (Link)memory_allocate_zeroed(sizeof *link);
    link->remote = remote;
    link->href = href;
    link->name = name;
    link->describe = describe;
    link->votes = votes_create();
    return link;
}

/*
 * link_href(link)
 *	This routine will return the HREF {Url} object associated with {link}.
 */
Url
link_href(
    Link link)
{
    return link->href;
}

/*
 * link_restore(remote, in_file, errors)
 *	This routine will read a {Link} object from {in_file} that was
 *	written by {link_save()} and return it.
 */
static Link
link_restore(
    Remote remote,
    FILE *in_file,
    Errors errors)
{
    Chr chr;
    Str describe;
    Url href;
    Link link;
    Str message;
    Str name;

    chr = (Chr)getc(in_file);
    if (chr != 'L') {
	message = str_printf("Trouble restoring link #1 ('%c')", chr);;
	goto error;
    }
    if (getc(in_file) != ' ') {
	message = (Str)"Trouble restoring link #2";
	goto error;
    }
    href = url_restore(in_file);
    if (getc(in_file) != ' ') {
	message = (Str)"Trouble restoring link #3";
	goto error;
    }
    name = str_restore(in_file);
    if (getc(in_file) != ' ') {
	message = (Str)"Trouble restoring link #4";
	goto error;
    }
    describe = str_restore(in_file); 
    if (getc(in_file) != '\n') {
	message = (Str)"Trouble restoring link #5";
	goto error;
    }
    link = link_create(remote, href, name, describe);
    link->votes = votes_restore(link, in_file, errors);
    return link;
  error:
    /* errors_append(errors, (Str)"Trouble restoring link.");*/
    errors_append(errors, message);
    return (Link)0;
}

/*
 * link_save(link, out_file)
 *	This routine will write {link} to {out_file} in such a way that it
 *	can be reread via link_restore().
 */
static void
link_save(
    Link link,
    FILE *out_file)
{
    (void)fprintf(out_file, "L ");
    url_save(link->href, out_file);
    (void)fprintf(out_file, " ");
    str_save(link->name, out_file);
    (void)fprintf(out_file, " ");
    str_save(link->describe, out_file);
    (void)fprintf(out_file, "\n");
    votes_save(link->votes, out_file);
}

/*
 * link_remote(link)
 *	This routine will return the {Remote} object associated with {link}.
 */
Remote
link_remote(
    Link link)
{
    return link->remote;
}

/*
 * link_vote_url(link, config)
 *	This routine will return the {Url} of the vote file associated
 *	with {link}.
 */
Url
link_vote_url(
    Link link,
    Config config)
{
    Url annote_url;
    Str anchor;
    Str document;
    Url local_url;
    Str name;
    Remote remote;
    Url url;
    Url vote_url;

    remote = link_remote(link);
    url = remote_url(remote);
    local_url = link->href;
    anchor = url_anchor(local_url);
    name = link->name;
    document = url_document(local_url);

    document = str_printf("%s.%s.%s.%s", document, str_encode(url_str(url)),
      str_encode(anchor), str_encode(name));

    annote_url = url_annote(local_url, config);
    vote_url = url_copy_replace_document(annote_url, document);
    return vote_url;
}

/*
 * link_votes(link)
 *	This routine will return the {Votes} object associated with {link}.
 */
Votes
link_votes(
    Link link)
{
    return link->votes;
}

/*
 * links_append(links, link)
 *	This routine will append {link} to the end of {links}.
 */
void
links_append(
    Links links,
    Link link)
{
    vector_append((Vector)links, (Pointer)link);
}

/*
 * links_create()
 *	This routine will create and return an empty vector of {Link} objects.
 */
Links
links_create(void)
{
    Links links;

    links = (Links)vector_create();
    return links;
}

/*
 * links_restore(remote, db_file_name, errors)
 *	This routine will read all of the {Remote} objects contained in
 *	the file named {db_file_name} and return them.
 */
Links
links_restore(
    Remote remote,
    FILE *db_file,
    Errors errors)
{
    unsigned count;
    unsigned index;
    Link link;
    Links links;
    unsigned size;

    links = links_create();
    (void)fscanf(db_file, "%u\n", &size);
    for (index = 0; index < size; index++) {
	count = errors_size(errors);
	link = link_restore(remote, db_file, errors);
	if (errors_size(errors) > count) {
	    errors_append(errors,
	      str_printf("Error restoring link %d", index));
	} else {
	    links_append(links, link);
	}
    }
    return links;
}

/*
 * links_save(links, db_file)
 *	This routine will write {links} to {db_file}.
 */
void
links_save(
    Links links,
    FILE *db_file)
{
    Link link;

    (void)fprintf(db_file, "%u\n", links_size(links));
    LINKS_LOOP(link, links) {
	link_save(link, db_file);
    }
}

/*
 * links_size(links)
 *	This routine will return the number of {Link} objects in {links}.
 */
unsigned
links_size(
    Links links)
{
    unsigned size;

    size = vector_size((Vector)links);
    return size;
}

/*
 * links_votes_merge(links_old, links_new)
 *	This routine will copy any vote information from {links_old}
 *	over to {links_new}.
 */
void
links_votes_merge(
    Links links_old,
    Links links_new)
{
    Link link_old;
    Link link_new;

    /* Append any new links */
    LINKS_LOOP(link_new, links_new) {
	LINKS_LOOP(link_old, links_old) {
	    if (url_document_equal(link_new->href, link_old->href) &&
	      str_equal(link_new->name, link_old->name)) {
		link_new->votes = link_old->votes;
		break;
	    }
	}
    }
}

/*
 */
void
links_write(
    Links links,
    FILE *annote_file,
    int list_src,
    Config config)
{
    Link link;

    LINKS_LOOP(link, links) {
	unsigned total;
	Url vote_url;
	Votes votes;
	unsigned yes;

	(void)fprintf(annote_file, "<DT><A HRef=\"");
	url_write(remote_url(link->remote), annote_file);
	(void)fprintf(annote_file, "\">");
	if (list_src) {
	    url_write(link->href, annote_file);
	} else {
	    if (str_is_empty(remote_title(link->remote))) {
		url_write(remote_url(link->remote), annote_file);
		(void)fprintf(annote_file, "(Untitled)");
	    } else {
		str_write(remote_title(link->remote), annote_file);
	    }
	}
	(void)fprintf(annote_file, "</A>\n");

	/* Write out the vote reference: */
	votes = link->votes;
	total = votes_size(votes);
	yes = votes_yes(votes);

	(void)fprintf(annote_file, "<A HRef=\"");
	vote_url = link_vote_url(link, config);
	url_write(vote_url, annote_file);
	(void)fprintf(annote_file, "\">");
	if (total == 0) {
	    (void)fprintf(annote_file, "(No votes)");
	} else {
	    (void)fprintf(annote_file, "(%u/%u=%u%%)",
	      yes, total, yes * 100 / total);
	}
	(void)fprintf(annote_file, "</A>\n");

	(void)fprintf(annote_file, "<DD>");
	if (str_is_empty(link->describe)) {
	    (void)fprintf(annote_file, "No description");
	} else {
	    str_write(link->describe, annote_file);
	}
	(void)fprintf(annote_file, "\n");
    }
}

