#include <stdlib.h>
#include "types.h"
#include "util.h"
recipe *
new_recipe()
{
recipe * r;
r = (recipe *) malloc(sizeof(recipe));
if (!r)
die("Couldn't allocate memory for recipe");
r->n = 1;
r->i = NULL;
r->in = 0;
r->s = NULL;
r->sn = 0;
r->r = NULL;
r->rn = 0;
r->filename = NULL;
r->path = NULL;
r->title = NULL;
r->sha1[0] = '\0';
return r;
}
void
new_subrecipe(recipe * r, recipe * src)
{
r->r = (recipe **)realloc(r->r, (r->rn + 1) * sizeof(recipe *));
if (!r->r)
die("Couldn't allocate memory for subricepie");
r->r[r->rn] = src;
r->rn++;
}
void
new_item(recipe * r)
{
r->i = (item **)realloc(r->i, (r->in + 1) * sizeof(item *));
if (!r->i)
die("Couldn't allocate memory for item");
r->i[r->in] = (item *)malloc(sizeof(item));
if (!r->i[r->in])
die("Couldn't allocate memory for item");
r->i[r->in]->name = NULL;
r->i[r->in]->qty = NULL;
r->in++;
}
void
free_item(item * i)
{
if (!i)
return;
if (i->name)
free(i->name);
if (i->qty)
free(i->qty);
free(i);
}
void
new_step(recipe * r)
{
r->s = (step **)realloc(r->s, (r->sn + 1) * sizeof(step *));
if (!r->s)
die("Couldn't allocate memory for step");
r->s[r->sn] = (step *)malloc(sizeof(step));
if (!r->s[r->sn])
die("Couldn't allocate memory for step");
r->s[r->sn]->inst = NULL;
r->s[r->sn]->txt = NULL;
r->s[r->sn]->duration = NULL;
r->s[r->sn]->result = NULL;
r->s[r->sn]->type = 0;
r->sn++;
}
void
free_step(step * s)
{
if (!s)
return;
if (s->inst)
free(s->inst);
if (s->txt)
free(s->txt);
if (s->duration)
free(s->duration);
if (s->result)
free(s->result);
free(s);
}
void
free_recipe(recipe * r)
{
if (!r)
return;
if (r->i) {
for (int i = 0; i < r->in; i++)
free_item(r->i[i]);
free(r->i);
}
if (r->s) {
for (int i = 0; i < r->sn; i++)
free_step(r->s[i]);
free(r->s);
}
if (r->r) {
for (int i = 0; i < r->rn; i++)
free_recipe(r->r[i]);
free(r->r);
}
if (r->filename)
free(r->filename);
if (r->path)
free(r->path);
if (r->title)
free(r->title);
free(r);
}
void
pprint_items(recipe * r)
{
printf("Ingredients for %s:\n", r->title);
for (int i = 0; i < r->in; i++) {
if (strlen(r->i[i]->qty)) {
printf("%s: %s\n", r->i[i]->name, r->i[i]->qty);
}
else {
printf("%s\n", r->i[i]->name);
}
}
printf("\n");
}
char * last_n(const char * s, size_t n)
{
size_t length = strlen( s );
return ( char * )( length < n ? s : s + length - n );
}
void
listing(recipe * r, const char * fmt)
{
if (!strcmp(fmt, "title") || !strcmp(fmt, "t")) {
printf("%s\n", r->title);
}
else if (!strcmp(fmt, "path") || !strcmp(fmt, "p")) {
printf("%s/%s\n", r->path, r->filename);
}
else if (!strcmp(fmt, "hash") || !strcmp(fmt, "h")) {
printf("%s\n", strcmp(r->sha1, "") ? r->sha1 : "neval");
}
else if (!strcmp(fmt, "shorthash") || !strcmp(fmt, "s")) {
printf("%.*s\n", 8, strcmp(r->sha1, "") ? r->sha1 : "neval");
}
else {
printf("%.*s %10.10s %d:%d\t%d\t%s\n",
8, (strcmp(r->sha1, "") ? r->sha1 : "neval"),
basename(r->path),
r->in, r->sn,
r->rn,
r->title);
}
}
void
show(recipe * r)
{
printf("Filename\t%s\n", r->filename);
printf("Dirname\t\t%s\n", r->path);
printf("Title\t\t%s\n\n", r->title);
for (int i = 0; i < r->in; i++)
printf("I:%s\t%s\n", r->i[i]->name, r->i[i]->qty);
if (r->sn) {
printf("\nSteps:\n~~~~~~\n\n");
for (int i = 0; i < r->sn; i++)
printf("%s: %s\n", r->s[i]->type == PREP ? "PREP" : "COOK", r->s[i]->inst);
}
}
void
tojson(recipe * r)
{
printf("{\"filename\":\"%s\",", r->filename);
printf("\"path\":\"%s\",", r->path);
printf("\"title\":\"%s\",", r->title);
printf("\"n\":\"%d\",", r->n);
printf("\"sha1_short\":\"%.*s\",", 8, r->sha1);
printf("\"sha1\":\"%s\"", r->sha1);
if (r->rn) {
printf(",\"subrecipes\":[");
int i = 0;
for (; i < r->rn - 1; i++) {
tojson(r->r[i]);
printf(",");
}
tojson(r->r[i]);
printf("]");
}
if (r->in) {
printf(",\"ingredients\":{");
int i = 0;
for (; i < r->in - 1; i++)
printf("\"%s\":\"%s\",", r->i[i]->name, r->i[i]->qty);
printf("\"%s\":\"%s\"}", r->i[i]->name, r->i[i]->qty);
}
if (r->sn) {
printf(",\"steps\":[");
int i = 0;
for (; i < r->sn - 1; i++)
printf("{\"inst\":\"%s\",\"txt\":\"%s\",\"duration\":\"%s\",\"result\":\"%s\",\"type\":\"%s\"},", r->s[i]->inst, r->s[i]->txt, r->s[i]->duration, r->s[i]->result, r->s[i]->type == 0 ? "prep" : (r->s[i]->type == 1 ? "cook" : "serve") );
printf("{\"inst\":\"%s\",\"txt\":\"%s\",\"duration\":\"%s\",\"result\":\"%s\",\"type\":\"%s\"}]", r->s[i]->inst, r->s[i]->txt, r->s[i]->duration, r->s[i]->result, r->s[i]->type == 0 ? "prep" : (r->s[i]->type == 1 ? "cook" : "serve") );
}
printf("}");
}
void
tohtml(recipe * r)
{
printf("<div class=\"recipe\">\n");
printf("<!-- %s/%s -->\n", r->path, r->filename);
printf("<h1>%s</h1>\n\n", r->title);
if (r->rn) {
printf("<ul class=\"list items subrecipes\">\n");
for (int i = 0; i < r->rn; i++) {
printf("<li>!%s/%s</li>\n", r->r[i]->path, r->r[i]->filename);
}
printf("</ul>\n");
}
if (r->in) {
printf("<ul class=\"list items\">\n");
for (int i = 0; i < r->in; i++) {
printf("<li>%s = %s</li>\n", r->i[i]->name, r->i[i]->qty);
}
printf("</ul>\n");
}
if (r->sn) {
printf("<br><p>---</p><br>\n");
printf("<ul class=\"list steps\">\n");
for (int i = 0; i < r->sn; i++) {
char c;
if (r->s[i]->type == PREP)
c = '-';
else if (r->s[i]->type == COOK)
c = '>';
else
c = '+';
printf("<li>%c %s</li>\n", c, r->s[i]->inst);
}
printf("</ul>\n");
}
printf("<hr>\n</div>\n");
}
void
torcp(recipe * r)
{
printf("# %s/%s\n\n", r->path, r->filename);
if (r->sha1[0] != '\0') {
printf("# %s\n\n", r->sha1);
}
printf("@%s\n\n", r->title);
for (int i = 0; i < r->rn; i++) {
printf("%s!%s/%s\n", (r->sha1[0] != '\0') ? "# ": "", r->r[i]->path, r->r[i]->filename);
}
for (int i = 0; i < r->in; i++) {
if (strlen(r->i[i]->qty)) {
printf("%s = %s\n", r->i[i]->name, r->i[i]->qty);
}
else {
printf("%s\n", r->i[i]->name);
}
}
if (r->sn) {
printf("\n---\n\n");
for (int i = 0; i < r->sn; i++) {
char c;
if (r->s[i]->type == PREP)
c = '-';
else if (r->s[i]->type == COOK)
c = '>';
else
c = '+';
printf("%c %s\n", c, r->s[i]->inst);
}
}
}
void
copy_metadata(recipe * dst, recipe * src)
{
if (!dst || !src) return;
if (src->filename)
dst->filename = strdup(src->filename);
if (src->path)
dst->path = strdup(src->path);
if (src->title)
dst->title = strdup(src->title);
}
void
copy_items(recipe * dst, recipe * src)
{
if (!dst || !src) return;
for (int i = 0; i < src->in; i++) {
new_item(dst);
dst->i[dst->in - 1]->name = strdup(src->i[i]->name);
dst->i[dst->in - 1]->qty = strdup(src->i[i]->qty);
}
}
void
copy_steps(recipe * dst, recipe * src)
{
if (!dst || !src) return;
for (int i = 0; i < src->sn; i++) {
new_step(dst);
dst->s[dst->sn - 1]->inst = strdup(src->s[i]->inst);
dst->s[dst->sn - 1]->type = src->s[i]->type;
}
}
void
copy_subrecipes(recipe * dst, recipe * src, int shallow_copy)
{
if (!dst || !src) return;
for (int i = 0; i < src->rn; i++) {
recipe * r = new_recipe();
r->title = strdup(src->r[i]->title);
r->path = strdup(src->r[i]->path);
r->filename = strdup(src->r[i]->filename);
r->n = src->r[i]->n;
if (!shallow_copy) {
copy_items(r, src->r[i]);
copy_steps(r, src->r[i]);
}
new_subrecipe(dst, r);
}
}
static void
join_subrecipe_items(recipe * dst, recipe * src)
{
if (!src || !dst) return;
for (int i = 0; i < src->rn; i++) {
join_subrecipe_items(dst, src->r[i]);
for (int j = 0; j < src->r[i]->in; j++) {
new_item(dst);
dst->i[dst->in - 1]->name = strdup(src->r[i]->i[j]->name);
if (src->r[i]->n > 1) {
if (strcmp(src->r[i]->i[j]->qty, "")) {
char tmp[2048] = "";
sprintf(tmp, "%d X ( %s )", src->r[i]->n, src->r[i]->i[j]->qty);
dst->i[dst->in - 1]->qty = strdup(tmp);
} else {
dst->i[dst->in - 1]->qty = strdup("");
}
}
else {
dst->i[dst->in - 1]->qty = strdup(src->r[i]->i[j]->qty);
}
}
}
}
static void
join_subrecipe_steps(recipe * dst, recipe * src)
{
if (!src || !dst) return;
for (int i = 0; i < src->rn; i++) {
join_subrecipe_steps(dst, src->r[i]);
for (int j = 0; j < src->r[i]->sn; j++) {
new_step(dst);
dst->s[dst->sn - 1]->inst = strdup(src->r[i]->s[j]->inst);
dst->s[dst->sn - 1]->type = src->r[i]->s[j]->type;
}
}
}
void
merge_steps(recipe * dst, recipe * src)
{
/* Join all steps in src's subrecipes to dst */
join_subrecipe_steps(dst, src);
/* Copy src steps as well to dst */
copy_steps(dst, src);
}
static int
item_exists(const char * name, const recipe * r)
{
for (int i = 0; i < r->in; i++)
if (!strcmp(r->i[i]->name, name))
return i;
return -1;
}
/**
* Sum all top level item qtys in `src` and add them to `dst`
* making sure each item name exists only once.
*/
static void
distinct_sum_items(recipe * dst, recipe * src)
{
if (!dst || !src) return;
for (int i = 0; i < src->in; i++) {
int n = item_exists(src->i[i]->name, dst);
if (n != -1) {
if (!strcmp(dst->i[n]->qty, "")
&& !strcmp(src->i[i]->qty, "")) {
// noop
} else if (!strcmp(dst->i[n]->qty, "")
|| !strcmp(src->i[i]->qty, "")) {
char tmp[FOOD_MAX_ARRAY] = "";
strcat(tmp, dst->i[n]->qty);
strcat(tmp, src->i[i]->qty);
free(dst->i[n]->qty);
dst->i[n]->qty = strdup(tmp);
} else {
char tmp[FOOD_MAX_ARRAY] = "";
strcat(tmp, dst->i[n]->qty);
strcat(tmp, " + ");
strcat(tmp, src->i[i]->qty);
free(dst->i[n]->qty);
dst->i[n]->qty = strdup(tmp);
}
}
else {
new_item(dst);
dst->i[dst->in - 1]->name = strdup(src->i[i]->name);
dst->i[dst->in - 1]->qty = strdup(src->i[i]->qty);
}
}
}
void
merge_items(recipe * dst, recipe * src)
{
recipe * _r = new_recipe();
/* Join all items in src's subrecipes to tmp */
join_subrecipe_items(_r, src);
/* Copy src items as well to tmp */
copy_items(_r, src);
/* disticnt items to dst */
distinct_sum_items(dst, _r);
free_recipe(_r);
}