#!/usr/bin/php
<?

require_once("pre.php");

$config_dir = "/var/lib/gforge/uploads/coord/coord/INCF-gtNITRCConfiguration";

$progname = basename($argv[0]);
$stderr = fopen("php://stderr", "w");

function report_error($msg) {

	global $stderr;
	global $progname;

	fprintf($stderr, "$progname: $msg\n");

	return;

} // end report_error()

function get_excludes() {

	global $config_dir;

	$exclude_data = file_get_contents("$config_dir/excludes.incf_nitrc");
	if ($exclude_data === false) {
		print "error\n";
		report_error("error reading excluded IDs");
		return NULL;
	}

	$exclude_ids = array();
	foreach (preg_split("/\n/", $exclude_data) as $exclude_line) {
		if (strlen(trim($exclude_line)) == 0) {
			continue;
		}
		$parts = preg_split("/\s+/", trim($exclude_line), 2);
		$exclude_ids[] = $parts[0];
	}

	return $exclude_ids;

} // end get_excludes()

function get_trove_categories() {

	$res = db_query("SELECT trove_cat_id, fullname FROM trove_cat");
	if (!$res) {
		print "error\n";
		report_error("error getting trove categories");
		report_error(db_error());
		return NULL;
	}

	$trove_cat_names = array();
	while ($row = pg_fetch_array($res)) {
		$id = intval($row["trove_cat_id"]);
		$name = trim($row["fullname"]);
		$trove_cat_names[$id] = $name;
	}

	pg_free_result($res);

	return $trove_cat_names;

} // end get_trove_categories()

function get_trove_translation($fname) {

	global $config_dir;

	global $trove_cat_names;

	$data = file_get_contents("$config_dir/$fname");
	if ($data === false) {
		print "error\n";
		report_error("error opening $fname");
		return NULL;
	}

	$translations = array();
	foreach (preg_split("/\n/", $data) as $line) {
		if (strlen(trim($line)) == 0) {
			continue;
		}
		$parts = preg_split("/\|/", trim($line));
		if (count($parts) == 2 && $parts[1] == "ignore") {
			$cat_id = NULL;
		} elseif (count($parts) == 3) {
			$cat_id = intval($parts[1]);
			$cat_name = trim($parts[2]);
			if (!array_key_exists($cat_id, $trove_cat_names)) {
				print "error\n";
				report_error("can't find trove category ID $cat_id for INCF value \"$incf_value\" in $fname");
				return NULL;
			}
			if ($cat_name != $trove_cat_names[$cat_id]) {
				print "error\n";
				report_error("error in trove category verification in $fname");
				report_error("ID $cat_id: expecting \"$cat_name\", got \"" . $trove_cat_names[$cat_id] . "\"");
				return NULL;
			}
		} else {
			print "error\n";
			report_error("bad line in $fname: $line");
			return NULL;
		}
		$incf_value = trim($parts[0]);
		if (array_key_exists($incf_value, $translations)) {
			print "error\n";
			report_error("duplicate NITRC values for INCF value \"$incf_value\" in $fname");
			return NULL;
		}
		$translations[$incf_value] = $cat_id;
	}

	return $translations;

} // end get_trove_translation()

function check_model_array($arr, $model, $error_location) {

	if (!is_array($arr)) {
		print "error\n";
		report_error("unexpected type in $error_location");
		return false;
	}

	if ($arr["model"] != $model) {
		print "error\n";
		report_error("bad model in $error_location");
		return false;
	}

	if (!array_key_exists("pk", $arr)) {
		print "error\n";
		report_error("no primary key in $error_location");
		return false;
	}

	if (!is_string($arr["pk"])) {
		print "error\n";
		report_error("bad primary key type in $error_location");
		return false;
	}

	if (!array_key_exists("fields", $arr)) {
		print "error\n";
		report_error("no fields for $model " . $arr["pk"] . " in $error_location");
		return false;
	}

	if (!is_array($arr["fields"])) {
		print "error\n";
		report_error("bad type for fields for $model " . $arr["pk"] . " in $error_location");
		return false;
	}

	return true;

} // end check_model_array()

function read_time($time_string, $error_location) {

	$time_arr = strptime($time_string, "%Y-%m-%d %H:%M:%S");
	if ($time_arr === false) {
		print "error\n";
		report_error("bad $error_location");
		return NULL;
	}
	$time = mktime($time_arr["tm_hour"], 
	               $time_arr["tm_min"], 
	               $time_arr["tm_sec"], 
	               $time_arr["tm_mon"], 
	               $time_arr["tm_mday"], 
	               1900 + $time_arr["tm_year"]);
	if ($time === false) {
		print "error\n";
		report_error("error converting $error_location");
		return NULL;
	}

	return $time;

} // end read_time()

function read_image($image_arr, $xml_doc, $project_pk, $is_default) {

	if (!check_model_array($image_arr, "software.projectimage", "read_image() for project $project_pk")) {
		return NULL;
	}

	$screenshot_node = $xml_doc->createElement("screenshot");
	$screenshot_node->setAttribute("source_id", $image_arr["pk"]);

	if (!array_key_exists("url", $image_arr["fields"])) {
		print "error\n";
		report_error("no URL for image " . $image_arr["pk"] . " for project $project_pk");
		return NULL;
	}
	$screenshot_node->appendChild($xml_doc->createElement("file", $image_arr["fields"]["url"]));

	if (!array_key_exists("description", $image_arr["fields"])) {
		print "error\n";
		report_error("no description for image " . $image_arr["pk"] . " for project $project_pk");
		return NULL;
	}
	$screenshot_node->appendChild($xml_doc->createElement("description", $image_arr["fields"]["description"]));

	if (!array_key_exists("last_modified_date", $image_arr["fields"])) {
		print "error\n";
		report_error("no last_modified_date for image " . $image_arr["pk"] . " for project $project_pk");
		return NULL;
	}
	$last_updated = read_time($image_arr["fields"]["last_modified_date"], "last_modified_date for image " . $image_arr["pk"] . " for project $project_pk");
	if (is_null($last_updated)) {
		return NULL;
	}
	$screenshot_node->appendChild($xml_doc->createElement("last_updated", $last_updated));

	$screenshot_node->appendChild($xml_doc->createElement("is_default", $is_default));

	return $screenshot_node;

} // end read_image()

function read_document($doc_arr, $xml_doc, $project_pk) {

	if (!check_model_array($doc_arr, "software.projectdoc", "read_document() for project $project_pk")) {
		return NULL;
	}

	$doc_node = $xml_doc->createElement("doc");
	$doc_node->setAttribute("source_id", $doc_arr["pk"]);

	if (!array_key_exists("name", $doc_arr["fields"])) {
		print "error\n";
		report_error("no name for doc " . $doc_arr["pk"] . " for project $project_pk");
		return NULL;
	}
	$name = $doc_arr["fields"]["name"];

	if (!array_key_exists("description", $doc_arr["fields"])) {
		print "error\n";
		report_error("no description for doc " . $doc_arr["pk"] . " for project $project_pk");
		return NULL;
	}
	$description = $doc_arr["fields"]["description"];
	if (strlen($description) == 0) {
		$doc_node->appendChild($xml_doc->createElement("description", "no description"));
	} elseif (strlen($description) < 10) {
		$doc_node->appendChild($xml_doc->createElement("description", "Description: $description"));
	} else {
		$doc_node->appendChild($xml_doc->createElement("description", $description));
	}

	if (strlen($name) > 0) {
		$title = $name;
	} elseif (strlen($description) == 0) {
		$title = "Document " . $doc_arr["pk"];
	} elseif (strlen($description) <= 30) {
		$title = $description;
	} else {
		$title = substr($description, 0, 20) . "..." . substr($description, -10);
	}
	if (strlen($title) < 5) {
		$title = "Document: $title";
	}
	$doc_node->appendChild($xml_doc->createElement("title", $title));

	if (!array_key_exists("url", $doc_arr["fields"])) {
		print "error\n";
		report_error("no URL for doc " . $doc_arr["pk"] . " for project $project_pk");
		return NULL;
	}
	$doc_node->appendChild($xml_doc->createElement("url", $doc_arr["fields"]["url"]));

	return $doc_node;

} // end read_document()

function read_citation($citation_arr, $xml_doc, $project_pk) {

	if (!check_model_array($citation_arr, "software.citation", "read_citation() for project $project_pk")) {
		return NULL;
	}

	$doc_node = $xml_doc->createElement("doc");
	$doc_node->setAttribute("source_id", $citation_arr["pk"]);

	if (!array_key_exists("file", $citation_arr["fields"])) {
		print "error\n";
		report_error("no file for citation " . $citation_arr["pk"] . " for project $project_pk");
		return NULL;
	}
	if (!array_key_exists("url", $citation_arr["fields"])) {
		print "error\n";
		report_error("no URL for citation " . $citation_arr["pk"] . " for project $project_pk");
		return NULL;
	}
	if (strlen($citation_arr["fields"]["file"]) > 0) {
		$doc_node->appendChild($xml_doc->createElement("url", htmlspecialchars($citation_arr["fields"]["file"])));
	} elseif (strlen($citation_arr["fields"]["url"]) > 0) {
		$doc_node->appendChild($xml_doc->createElement("url", htmlspecialchars($citation_arr["fields"]["url"])));
	} else {
		$doc_node->appendChild($xml_doc->createElement("url", ""));
	}

	if (!array_key_exists("description", $citation_arr["fields"])) {
		print "error\n";
		report_error("no description for citation " . $citation_arr["pk"] . " for project $project_pk");
		return NULL;
	}
	$doc_node->appendChild($xml_doc->createElement("description", htmlspecialchars($citation_arr["fields"]["description"])));
	$doc_node->appendChild($xml_doc->createElement("title", htmlspecialchars($citation_arr["fields"]["description"])));

	return $doc_node;

} // end read_citation()

function read_release($download_arr, $xml_doc, $project_pk) {

	if (!check_model_array($download_arr, "software.projectdownload", "read_release() for project $project_pk")) {
		return NULL;
	}

	if (!array_key_exists("name", $download_arr["fields"])) {
		print "error\n";
		report_error("no name for download in read_release() for project $project_pk");
		return NULL;
	}
	$name = trim($download_arr["fields"]["name"]);

	if (!array_key_exists("description", $download_arr["fields"])) {
		print "error\n";
		report_error("no description for download in read_release() for project $project_pk");
		return NULL;
	}
	$description = trim($download_arr["fields"]["description"]);

	if (!array_key_exists("creation_date", $download_arr["fields"])) {
		print "error\n";
		report_error("no creation_date for download in read_release() for project $project_pk");
		return NULL;
	}
	$creation_date = read_time($download_arr["fields"]["creation_date"], "creation_date for download " . $download_arr["pk"] . " for project $project_pk");
	if (is_null($creation_date)) {
		return NULL;
	}

	if (!array_key_exists("url", $download_arr["fields"])) {
		print "error\n";
		report_error("no url for download in read_release() for project $project_pk");
		return NULL;
	}
	$url = $download_arr["fields"]["url"];

	if (strlen($name) > 0) {
		$use_name = $name;
	} elseif (strlen($description) > 30) {
		$use_name = substr($description, 0, 20) . "..." . substr($description, -10);
	} elseif (strlen($description) > 0) {
		$use_name = $description;
	} else {
		$use_name = "Unnamed release ID " . $download_arr["pk"];
	}

	$file_node = $xml_doc->createElement("file");
	$file_node->setAttribute("source_id", $download_arr["pk"]);

	$file_node->appendChild($xml_doc->createElement("filename", $use_name));
	$file_node->appendChild($xml_doc->createElement("url", $url));
	$file_node->appendChild($xml_doc->createElement("release_date", $creation_date));

	$release_node = $xml_doc->createElement("release");
	$release_node->setAttribute("source_id", $download_arr["pk"]);

	$release_node->appendChild($xml_doc->createElement("name", $use_name));
	$release_node->appendChild($xml_doc->createElement("notes", $description));
	$release_node->appendChild($xml_doc->createElement("changes", ""));
	$release_node->appendChild($xml_doc->createElement("preformatted", "0"));
	$release_node->appendChild($xml_doc->createElement("release_date", $creation_date));

	$release_node->appendChild($file_node);

	return $release_node;

} // end read_release()

function read_groups_by_incf_id() {

	$query = "SELECT group_id, source_id FROM groups WHERE source = 'incf'";
	$res = db_query($query);
	if (!$res) {
		print "error\n";
		report_error("error getting groups");
		report_error(db_error());
		return NULL;
	}

	$groups_by_incf_id = array();
	while ($row = pg_fetch_array($res)) {
		$incf_id = $row["source_id"];
		if (array_key_exists($incf_id, $groups_by_incf_id)) {
			print "error\n";
			report_error("multiple groups for INCF PK $incf_id");
			pg_free_result($res);
			return NULL;
		}
		$groups_by_incf_id[$incf_id] = $row["group_id"];
	}

	pg_free_result($res);

	return $groups_by_incf_id;

} // end read_groups_by_incf_id()

function read_member_roles() {

	$query = "SELECT groups.source_id, groups.unix_group_name, role.role_id " . 
	         "  FROM groups, role " . 
	         " WHERE groups.source = 'incf' " . 
	         "   AND role.group_id = groups.group_id " . 
	         "   AND role.role_name = 'Member'";
	$res = db_query($query);
	if (!$res) {
		print "error\n";
		report_error("error getting member roles");
		report_error(db_error());
		return NULL;
	}

	$member_roles = array();
	while ($row = pg_fetch_array($res)) {
		$incf_pk = $row["source_id"];
		if (array_key_exists($incf_pk, $member_roles)) {
			print "error\n";
			report_error("multiple \"Member\" roles for INCF PK $incf_pk");
			pg_free_result($res);
			return NULL;
		}
		$member_roles[$incf_pk] = intval($row["role_id"]);
	}

	pg_free_result($res);

	return $member_roles;

} // end read_member_roles()

function read_members($arr, $project_pk) {

	if (!is_array($arr)) {
		print "error\n";
		report_error("unexpected type in read_members() for project $project_pk");
		return NULL;
	}

	$members = array();
	foreach ($arr as $m_arr) {

		if (!check_model_array($m_arr, "site.userprofile", "read_members() for project $project_pk")) {
			return NULL;
		}

		if (strlen($m_arr["fields"]["first_name"]) > 0 || strlen($m_arr["fields"]["last_name"]) > 0) {
			$fullname = $m_arr["fields"]["first_name"] . " " .  $m_arr["fields"]["last_name"];
		} elseif (strlen($m_arr["fields"]["username"]) > 0) {
			$fullname = $m_arr["fields"]["username"];
		} else {
			print "error\n";
			report_error("no usable name for member " . $m_arr["pk"] . " for project $project_pk");
			return NULL;
		}
		$members[$m_arr["pk"]] = array($fullname, $m_arr["fields"]["username"]);

	}

	return $members;

} // end read_members()

function create_group_node($arr, $xml_doc, &$users) {

	global $exclude_ids;
	global $license_translations;
	global $maturity_translations;
	global $keyword_translations;
	global $topic_translations;
	global $member_roles;
	global $groups_by_incf_id;

	if (!check_model_array($arr, "software.project", "create_group_node()")) {
		return NULL;
	}

	print "reading INCF ID " . $arr["pk"] . "\n";

	if (in_array($arr["pk"], $exclude_ids)) {
		print "excluding ID " . $arr["pk"] . "\n";
		return false;
	}

	if (!array_key_exists($arr["pk"], $groups_by_incf_id)) {
		print "error\n";
		report_error("no group for INCF ID " . $arr["pk"]);
		return false;
	}

	$errors = array();

	$group_node = $xml_doc->createElement("group");
	$group_node->setAttribute("source_id", $arr["pk"]);

	$project_slug = NULL;

	$trove_cats = array();
	$extra_trove = array();

	foreach ($arr["fields"] as $key => $value) {

		if ($key == "prerequirements") {
			// ignore
		} elseif ($key == "documentation" && count($value) > 0) {
			$dg_node = $xml_doc->createElement("doc_group");
			$dg_node->setAttribute("source_id", $arr["pk"] . ".documentation");
			$dg_node->appendChild($xml_doc->createElement("name", "Documentation"));
			foreach ($value as $doc) {
				$doc_node = read_document($doc, $xml_doc, $arr["pk"]);
				if (is_null($doc_node)) {
					return NULL;
				}
				$dg_node->appendChild($doc_node);
			}
			$group_node->appendChild($dg_node);
		} elseif ($key == "name") {
			$name_node = $xml_doc->createElement("name", $value);
			$group_node->appendChild($name_node);
		} elseif ($key == "license") {
			if (!array_key_exists($value, $license_translations)) {
				$errors[] = "unmapped license \"$value\"";
			} elseif (!is_null($license_translations[$value])) {
				$trove_cats[$license_translations[$value]] = NULL;
			}
		} elseif ($key == "maturity") {
			if ($value != "") {
				if (!array_key_exists($value, $maturity_translations)) {
					$errors[] = "unmapped maturity \"$value\"";
				} elseif (!is_null($maturity_translations[$value])) {
					$trove_cats[$maturity_translations[$value]] = NULL;
				}
			}
		} elseif ($key == "url") {
			$url = $value;
		} elseif ($key == "topics") {
			foreach ($value as $topic) {
				if (!array_key_exists($topic, $topic_translations)) {
					$extra_trove[] = $topic;
				} elseif (!is_null($topic_translations[$topic])) {
					$trove_cats[$topic_translations[$topic]] = NULL;
				}
			}
		} elseif ($key == "last_modified_date") {
			// ignore
		} elseif ($key == "images") {
			$first_image_flag = true;
			foreach ($value as $image) {
				if ($first_image_flag) {
					$screenshot_node = read_image($image, $xml_doc, $arr["pk"], "1");
				} else {
					$screenshot_node = read_image($image, $xml_doc, $arr["pk"], "0");
				}
				if (is_null($screenshot_node)) {
					return NULL;
				}
				$group_node->appendChild($screenshot_node);
				$first_image_flag = false;
			}
		} elseif ($key == "creation_date") {
			// ignore
		} elseif ($key == "ease_of_use") {
			// ignore
		} elseif ($key == "citations" && count($value) > 0) {
			$dg_node = $xml_doc->createElement("doc_group");
			$dg_node->setAttribute("source_id", $arr["pk"] . ".citations");
			$dg_node->appendChild($xml_doc->createElement("name", "Citations"));
			foreach ($value as $doc) {
				$doc_node = read_citation($doc, $xml_doc, $arr["pk"]);
				if (is_null($doc_node)) {
					return NULL;
				}
				$dg_node->appendChild($doc_node);
			}
			$group_node->appendChild($dg_node);
		} elseif ($key == "purpose") {
			$description_node = $xml_doc->createElement("description", $value);
			$group_node->appendChild($description_node);
		} elseif ($key == "members") {
			$members = read_members($value, $arr["pk"]);
			if (is_null($members)) {
				return NULL;
			}
			if (!array_key_exists($arr["pk"], $member_roles)) {
				$errors[] = "can't find member role for project " . $arr["pk"];
			} else {
				foreach ($members as $source_id => $m_arr) {
					list($fullname, $username) = $m_arr;
					$users[$source_id] = $m_arr;
					$user_node = $xml_doc->createElement("user");
					$user_node->setAttribute("role_id", $member_roles[$arr["pk"]]);
					$user_node->setAttribute("source_id", $source_id);
					$group_node->appendChild($user_node);
				}
			}
		} elseif ($key == "keywords") {
			foreach ($value as $keyword) {
				if (!array_key_exists($keyword, $keyword_translations)) {
					$extra_trove[] = $keyword;
				} elseif (!is_null($keyword_translations[$keyword])) {
					$trove_cats[$keyword_translations[$keyword]] = NULL;
				}
			}
		} elseif ($key == "downloads") {
			$package_node = $xml_doc->createElement("package");
			$package_node->setAttribute("source_id", $arr["pk"]);
			$package_node->appendChild($xml_doc->createElement("name", "Releases"));
			foreach ($value as $download) {
				$release_node = read_release($download, $xml_doc, $arr["pk"]);
				if (is_null($release_node)) {
					return NULL;
				}
				$package_node->appendChild($release_node);
			}
			$group_node->appendChild($package_node);
		} elseif ($key == "description") {
			// ignore
		} elseif ($key == "slug") {
			$project_slug = $value;
		}

	}

	if (is_null($project_slug)) {
		$errors[] = "no slug given";
	}

	$source_url = "http://software.incf.org/software/$project_slug/home";
	$group_node->appendChild($xml_doc->createElement("source_url", $source_url));

	if (strlen($url) == 0) {
		$url = $source_url;
	}
	$url_node = $xml_doc->createElement("url", $url);
	$group_node->appendChild($url_node);

	if (count($errors) > 0) {
		print "error\n";
		foreach ($errors as $error) {
			report_error($arr["pk"] . ": $error");
		}
		return false;
	}

	foreach (array_keys($trove_cats) as $trove_cat) {
		$trove_node = $xml_doc->createElement("trove");
		$trove_node->setAttribute("id", $trove_cat);
		$group_node->appendChild($trove_node);
	}

	foreach ($extra_trove as $cat) {
		$cat_node = $xml_doc->createElement("extra_trove", $cat);
		$group_node->appendChild($cat_node);
	}

	return $group_node;

} // end create_group_node()

if (count($argv) != 2) {
	fprintf($stderr, "usage: $progname <XML output file>\n");
	exit(1);
}

$fname = $argv[1];

$exclude_ids = get_excludes();
if (is_null($exclude_ids)) {
	exit(1);
}

$trove_cat_names = get_trove_categories();
if (is_null($trove_cat_names)) {
	exit(1);
}

$license_translations = get_trove_translation("license.incf_nitrc");
if (is_null($license_translations)) {
	exit(1);
}

$maturity_translations = get_trove_translation("maturity.incf_nitrc");
if (is_null($maturity_translations)) {
	exit(1);
}

$keyword_translations = get_trove_translation("keyword.incf_nitrc");
if (is_null($keyword_translations)) {
	exit(1);
}

$topic_translations = get_trove_translation("topic.incf_nitrc");
if (is_null($topic_translations)) {
	exit(1);
}

$groups_by_incf_id = read_groups_by_incf_id();
if (is_null($groups_by_incf_id)) {
	exit(1);
}

$member_roles = read_member_roles();
if (is_null($member_roles)) {
	exit(1);
}

if (true) {
	// get data from the INCF
	$data = file_get_contents("http://software.incf.org/json/");
} else {
	// get data from a local file (for testing)
	$data = file_get_contents("incf.json");
}
if ($data === false) {
	print "error\n";
	report_error("error reading incf.json");
	exit(1);
}

$arr = json_decode($data, true);
if ($arr === false) {
	print "error\n";
	report_error("error reading JSON");
	exit(1);
}
if (!is_array($arr)) {
	print "error\n";
	report_error("unexpected data in JSON");
	exit(1);
}

$xml_doc = new DOMDocument();
$import_node = $xml_doc->createElement("import");
$import_node->setAttribute("source", "incf");

$users = array();
foreach ($arr as $project) {
	$group_node = create_group_node($project, $xml_doc, $users);
	if (is_null($group_node)) {
		exit(1);
	} elseif ($group_node === false) {
		continue;
	}
	$import_node->appendChild($group_node);
}

foreach ($users as $source_id => $u_arr) {
	list($fullname, $username) = $u_arr;
	$user_node = $xml_doc->createElement("user");
	$user_node->setAttribute("source_id", $source_id);
	$name_node = $xml_doc->createElement("fullname", $fullname);
	$user_node->appendChild($name_node);
	$user_name_node = $xml_doc->createElement("user_name", $username);
	$user_node->appendChild($user_name_node);
	$url_node = $xml_doc->createElement("url", "http://software.incf.org/Members/$username");
	$user_node->appendChild($url_node);
	$import_node->appendChild($user_node);
}

$xml_doc->appendChild($import_node);
$xml_doc->formatOutput = true;

$fp = fopen($fname, "w");
if (!$fp) {
	print "error\n";
	report_error("error opening $fname");
	exit(1);
}
fwrite($fp, $xml_doc->saveXML());
fclose($fp);

exit(0);

?>
