# Pretty-print Meal-Master recipe to .RTF format
# Usage: awk -f mmrtf.awk <recipe.mmf >recipe.rtf
# Only works with single-column Meal-Master format
#
# RTF hints:
#
# * https://metacpan.org/dist/RTF-Writer/view/lib/RTF/Cookbook.pod
# * https://en.wikipedia.org/wiki/Rich_Text_Format
#
# RTF uses the ANSI character set aka Windows-1252.
# To convert a recipe from UTF-8 on DOS or Linux:
#
# DOS:
# C:\> copy recipe.mmf recipe.bak
# C:\> utf8tocp.exe 1252 recipe.mmf
#
# Linux:
# $ cp recipe.mmf recipe.bak
# $ iconv -f UTF-8 -t WINDOWS-1252 <recipe.bak >recipe.mmf

function amount_format(amt) {
   retval = trim(amt)
   if (retval ~ /^[0-9]+$/) {
       retval = retval "    "
   }
   return retval
}

function array_size(a) {
   retval = 0
   for (i in a) {
       retval++
   }
   return retval
}

function heading_fontsize(size) {
   retval = fontsize + (7 - size)
   return retval
}

function inches(value) {
   # 1440 twips per inch
   retval = value * 1440
   return retval
}

function ingredient_parse(line) {
   new_amount = substr(line, 1, 7)
   new_unit = substr(line, 9, 2)
   new_ingredient = substr(line, 12)
   if (new_amount == "       " && new_unit == "  " && match(new_ingredient, /^[ \t]*- */)) {
       ingredient = ingredient " " substr(new_ingredient, RLENGTH+1)
       is_continuation = 1
   } else {
       amount = new_amount
       unit = new_unit
       ingredient = new_ingredient
       is_continuation = 0
   }
   return
}

function rtf() {
   fontsize = 24
   heading_bottom = inches(1/4)
   par_bottom = inches(1/8)

   printf "{\\rtf1\\ansi\\deff0\\deflang1033\\windowctrl\n"
   printf "{\\*\\comment generator: mmrtf.awk}\n"
   printf "{\\fonttbl\n"
   printf "{\\f0 \\froman Times New Roman;}\n"
   printf "{\\f1 \\fmodern Line Printer;}\n"
   printf "{\\f2 \\froman Symbol;}\n"
   printf "{\\f3 \\fswiss Ariel;}\n"
   printf "}\n"

   heading_top = 0
   rtf_heading(2, title)
   heading_top = inches(1/4)

   table_gap = 0
   table_left = 0
   cell1 = inches(1.6)
   cell2 = inches(7)

   # beginning of table
   printf "{\\pard\n"
   printf "\\trowd\\trgaph%d\\trleft%d\\cellx%d\\cellx%d\n",
       table_gap, table_left, cell1, cell2

   if (length(categories) > 0) {
       printf "\\pard\\intbl{\\b{Categories:}}\\cell\n"
       printf "\\pard\\intbl{%s}\\cell\n", rtf_encode(categories)
       printf "\\row\n"
   }

   if (length(yield) > 0) {
       printf "\\pard\\intbl{\\b{Yield:}}\\cell\n"
       printf "\\pard\\intbl{%s}\\cell\n", rtf_encode(yield)
       printf "\\row\n"
   }

   # end of table
   printf "}\n"

   if (array_size(ingredients) > 0) {
       rtf_heading(3, "Ingredients")

       # beginning of table
       cell1 = inches(1.25)
       cell2 = inches(2.5)
       cell3 = inches(7)
       printf "{\\pard\n"
       printf "\\trowd\\trgaph%d\\trleft%d\\cellx%d\\cellx%d\\cellx%d\n",
           table_gap, table_left, cell1, cell2, cell3
       printf "\\pard\\intbl{\\b{Amt}}\\cell\n"
       printf "\\pard\\intbl{\\b{Unit}}\\cell\n"
       printf "\\pard\\intbl{\\b{Ingredient}}\\cell\n"
       printf "\\row\n"

       for (gsection in gsections) {
           printf "\\trowd\\trgaph%d\\trleft%d\n", table_gap, table_left
           printf "\\clmgf\\cellx%d\n", cell1
           printf "\\clmrg\\cellx%d\n", cell2
           printf "\\clmrg\\cellx%d\n", cell3
           printf "\\pard\\intbl{\\i{%s}}\\cell\n", rtf_encode(gsection)
           printf "\\pard\\intbl{}\\cell\n"
           printf "\\pard\\intbl{}\\cell\n"
           printf "\\row\n"
           ingredient_count = gsections[gsection]
           for (i = 1; i <= ingredient_count; i++) {
               amount = amounts[gsection, i]
               unit = units[gsection, i]
               ingredient = ingredients[gsection, i]
               printf "\\trowd\\trgaph%d\\trleft%d\\cellx%d\\cellx%d\\cellx%d\n",
                   table_gap, table_left, cell1, cell2, cell3
               printf "\\pard\\intbl{%7s}\\cell\n",
                   rtf_encode(amount_format(amount))
               printf "\\pard\\intbl{%s}\\cell\n", unit_name(unit)
               printf "\\pard\\intbl{%s}\\cell\n", rtf_encode(ingredient)
               printf "\\row\n"
           }
       }

       # end of table
       printf "}\n"
   }
   if (array_size(instructions) > 0) {
       rtf_heading(3, "Instructions")
       list_open = 0
       par_open = 0
       for (tsection in tsections) {
           rtf_close()
           if (length(tsection) > 0) {
               rtf_heading(4, tsection)
           }
           instruction_count = tsections[tsection]
           for (i = 1; i <= instruction_count; i++) {
               line = instructions[tsection,i]
               if (line ~ /^[ \t]*$/) {
                   if (list_open || par_open) {
                       rtf_close()
                   }
               } else if (match(line, /^[ \t]*\* /)) {
                   if (par_open == 1) {
                       rtf_close()
                   }
                   if (list_open == 0) {
                       list_open = 1
                       printf "{\n"
                   }
                   text = substr(line, RLENGTH+1)
                   printf "{\\pard\\sa%d\\bullet  %s\\par}\n",
                       par_bottom, rtf_encode(text)
               } else {
                   if (list_open) {
                       rtf_close()
                   }
                   if (par_open == 0) {
                       printf "{\\pard\\sa%d\n", par_bottom
                       par_open = 1
                   }
                   printf "{%s} \n", rtf_encode(trim(line))
               }
           }
       }
       rtf_close()
   }

   printf "\n}\n"
   return
}

function rtf_close() {
   if (list_open) {
       printf "}\n"
       list_open = 0
   } else if (par_open) {
       printf "\\par}\n"
       par_open = 0
   }
   return
}

function rtf_encode(str) {
   gsub(/\\/, "\\\\", str)
   gsub(/{/, "\\{", str)
   gsub(/}/, "\\}", str)
   return str
}

# rtf_heading(2, txt) is like HTML: printf "<h2>%s</h2>", txt

function rtf_heading(lvl, str) {
   printf "{\\pard\\sa%d\\sb%d\\fs%d\\b{%s}\\par}\n",
       heading_bottom,
       heading_top,
       heading_fontsize(lvl),
       rtf_encode(str)
   return
}

function trim(str) {
   retval = str
   gsub(/^[ \t]+/, "", retval)
   gsub(/[ \t]+$/, "", retval)
   return retval
}

function unit_name(unit) {
   if (unit in names) {
       retval = names[unit]
   } else {
       retval = unit
   }
   return retval
}

function unit_names_init() {
   names["x "] = "per serving"
   names["sm"] = "small"
   names["md"] = "medium"
   names["lg"] = "large"
   names["cn"] = "can"
   names["pk"] = "package"
   names["pn"] = "pinch"
   names["dr"] = "drop"
   names["ds"] = "dash"
   names["ct"] = "carton"
   names["bn"] = "bunch"
   names["sl"] = "slice"
   names["ea"] = "each"
   names["t "] = "teaspoon"
   names["ts"] = "teaspoon"
   names["T "] = "tablespoon"
   names["tb"] = "tablespoon"
   names["fl"] = "fluid ounce"
   names["c "] = "cup"
   names["pt"] = "pint"
   names["qt"] = "quart"
   names["ga"] = "gallon"
   names["oz"] = "ounce"
   names["lb"] = "pound"
   names["ml"] = "milliliter"
   names["cb"] = "cubic cm"
   names["cl"] = "centiliter"
   names["dl"] = "deciliter"
   names["l "] = "liter"
   names["mg"] = "milligram"
   names["cg"] = "centigram"
   names["dg"] = "decigram"
   names["g "] = "gram"
   names["kg"] = "kilogram"
   return
}

BEGIN {
   after_ingredients = 0
   at_end = 0
   in_gsection = 0
   in_heading = 0
   in_ingredients = 0
   in_instructions = 0
   in_list = 0
   unit_names_init()
}

{
   gsub(/\r/, "")
   if (NR == 1) {
       if (/^(MMMMM|-----)----- Recipe via Meal-Master/) {
           in_heading = 1
       } else {
           print "Error: Not in Meal-Master format\n"
           exit 0
       }
   } else if (NR == 2) {
       # ignore second line
   } else if (in_heading) {
       if (match($0, /^[ \t]+Title: /)) {
           title = substr($0, RLENGTH+1)
       } else if (match($0, /^[ \t]+Categories: /)) {
           categories = substr($0, RLENGTH+1)
       } else if (match($0, /^[ \t]+Yield: /)) {
           yield = substr($0, RLENGTH+1)
       } else if (/^[ \t]*$/) {
           in_heading = 0
           in_ingredients = 1
       }
   } else if (in_ingredients) {
       if (match($0, /^(MMMMM|-----)-+/)) {
           in_ingredients = 0
           in_gsection = 1
           gsection = substr($0, RLENGTH+1)
           gsub(/-+$/, "", gsection)
       } else if (/^[ \t]*$/) {
           in_ingredients = 0
           after_ingredients = 1
       } else {
           gsection = ""
           i = gsections[gsection]
           ingredient_parse($0)
           if (is_continuation == 0) {
               i++
               gsections[gsection] = i
           }
           amounts[gsection,i] = amount
           units[gsection,i] = unit
           ingredients[gsection,i] = ingredient
       }
   } else if (after_ingredients) {
       if (match($0, /^(MMMMM|-----)-+/)) {
           after_ingredients = 0
           in_gsection = 1
           gsection = substr($0, RLENGTH+1)
           gsub(/-+$/, "", gsection)
       } else {
           after_ingredients = 0
           in_instructions = 1
       }
   } else if (in_gsection) {
       if (match($0, /^(MMMMM|-----)-+/)) {
           in_gsection = 1
           gsection = substr($0, RLENGTH+1)
           gsub(/-+$/, "", gsection)
       } else if (/^[ \t]*$/) {
           in_gsection = 0
           after_gsection = 1
       } else {
           i = gsections[gsection]
           ingredient_parse($0)
           if (is_continuation == 0) {
               i++
               gsections[gsection] = i
           }
           amounts[gsection,i] = amount
           units[gsection,i] = unit
           ingredients[gsection,i] = ingredient
       }
   } else if (after_gsection) {
       if (match($0, /^(MMMMM|-----)-+/)) {
           after_gsection = 0
           in_gsection = 1
           gsection = substr($0, RLENGTH+1)
           gsub(/-+$/, "", gsection)
       } else {
           after_gsection = 0
           in_instructions = 1
       }
   }
   if (in_instructions) {
       if (match($0, /^(MMMMM|-----)-+/)) {
           tsection = substr($0, RLENGTH+1)
           gsub(/-+$/, "", tsection)
       } else if (/^(MMMMM|-----)$/) {
           in_instructions = 0
           at_end = 1
       } else if (/^[ \t]*\* /) {
           in_instructions = 0
           in_list = 1
       } else {
           tsections[tsection]++
           i = tsections[tsection]
           instructions[tsection,i] = $0
       }
   }
   if (in_list) {
       if (/^[ \t]*$/) {
           in_list = 0
           in_instructions = 1
           tsections[tsection]++
           i = tsections[tsection]
           instructions[tsection,i] = $0
       } else if (/^[ \t]*\* /) {
           tsections[tsection]++
           i = tsections[tsection]
           instructions[tsection,i] = $0
       } else {
           i = tsections[tsection]
           line = trim($0)
           instructions[tsection,i] = instructions[tsection,i] " " line
       }
   }
}

END {
   rtf()
}