/*
 * Copyright 2007 Kevin Roe
 * This file is part of makeroom.
 *
 * makeroom 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 3 of the License, or
 * (at your option) any later version.
 *
 * Jriver 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, see <http://www.gnu.org/licenses/>.
 */

// makeroom.c
// 
//      Author: Zanz@Xyddikon-4 (XK4)
// 
// Description: Makeroom is a menu-based program to build
//		simple rooms without writting any code.
//
// Although makeroom was designed specifically for our lib,
// it should be easy to modify to work with most MUDs running
// the LDMud 3.3.* or 3.4.* driver (might work on earlier versions).
//
// XK4: roia.servegame.org 4000
// Nov07

#define USER  this_player()->query_real_name()

#define HEADER sprintf("\
// %s\n\
// Created %s\n\
// by %s [RoomMaker]\n\
\n\
#include \"%s\"\n\
\n\
inherit ROOM;\n\
\n\
void create() {\n",\
    users[USER]["filename"],\
    ctime(time()),\
    capitalize(USER),\
    users[USER]["hfilename"])


#include <input_to.h>
#include <regexp.h>

// these are the default values used to init each user
private mapping exits = ([ ]);   /* exits   = ([ "north" : "/path/to/file"         ]); */
private mapping details = ([ ]); /* details = ([ "detail": "description of detail" ]); */
private string filename = "";
private string hfilename = "defs.h";
private string planet = "urth";
private string short_desc = "An empty room";
private string long_desc = "This is an empty room yo!\n";
private string color = "c";
// you may need to tailor this or remove color altogether if your
// rooms do not support a color for exits.
private mapping real_colors = ([
    "b": "blue",
    "B": "b_blue",
    "c": "cyan",
    "C": "b_cyan",
    "g": "green",
    "G": "b_green",
    "m": "magenta",
    "M": "b_magenta",
    "r": "red",
    "R": "b_red",
    "w": "reset",
    "W": "bold",
    "y": "brown",
    "Y": "yellow",
    "K": "b_black",
    ]);

private string *colors = ({ "b","B","c","C","g","G","m","M","r","R","w","W","y","Y","K" });
private int inside = 1;
private int light = 0;

private mapping users = ([ ]);

private string macro_name();
private void shell_loop(string input);
private void exit_submenu(string str);
private void detail_submenu(string str);
private void get_exit_name(string str);
private void mod_exit(string str);
private void display_menu();
private void display_exit_menu();
private void display_detail_menu();
private string generate_code();

private void editor(string str) {

    mapping tmp;

    tmp = users[USER];
    if(!tmp["buffer"])
        tmp["buffer"] = ({ });
    switch(str) {
    case "~q":
        printf("Editor aborted.\n");
        tmp["buffer"] = ({ });
        apply(#'display_menu);
        apply(#'shell_loop);
        return;
        break;
    case ".":
    case "**":
        switch(tmp["editor"]) {
          case "long_desc":
            tmp["long_desc"] = sprintf("%s\n", implode(tmp["buffer"],"\n"));
            apply(#'display_menu);
            apply(#'shell_loop);
            break;
          case "detail":
            tmp["details"] += ([ users[USER]["detail_name"] :
              sprintf("%s\n", implode(tmp["buffer"], "\n")) ]);
            apply(#'display_detail_menu);
            input_to(#'detail_submenu, INPUT_PROMPT, "Detail] ");
            break;
          default:
            printf("Unknown option.\n");
            break;
        }
        tmp["buffer"] = ({ });
        return;
        break;
    default:
        tmp["buffer"] += ({ str });
        break;
    }
    input_to(#'editor, INPUT_PROMPT, "] ");
    return;
}

private string exit_string() {
    int x;
    string ret = "";
    string *names;

    if(!sizeof(users[USER]["exits"])) return "none";
    names = m_indices(users[USER]["exits"]);
    for(x = 0; x < sizeof(names); x++) {
     if(x > 0)
      ret += ", "+names[x];
     else
      ret += names[x];
    }
    ret = sprintf("&%s%s", users[USER]["color"], ret);
    return ret;
}

private string detail_string() {
    int x;
    string ret = "";
    string *names;

    if(!sizeof(users[USER]["details"])) return "none";
    names = m_indices(users[USER]["details"]);
    for(x = 0; x < sizeof(names); x++) {
     if(x > 0)
      ret += ", "+names[x];
     else
      ret += names[x];
    }
    ret = sprintf("&%s%s", users[USER]["color"], ret);
    return ret;
}

private void delete_exit(string str) {
    string *enames = m_indices(users[USER]["exits"]);
    int choice = to_int(str);
    if(!choice) {
      printf("No exit deleted\n");
      apply(#'display_exit_menu);
      input_to(#'exit_submenu, INPUT_PROMPT, "Exit] ");
      return;
    }
    users[USER]["exits"] -= ([ enames[choice-1] ]);
    apply(#'display_exit_menu);
    input_to(#'exit_submenu, INPUT_PROMPT, "Exit] ");
    return;
}

private void delete_detail(string str) {
    string *dnames = m_indices(users[USER]["details"]);
    int choice = to_int(str);
    if(!choice) {
      printf("No detail deleted\n");
      apply(#'display_detail_menu);
      input_to(#'detail_submenu, INPUT_PROMPT, "Exit] ");
      return;
    }
    users[USER]["details"] -= ([ dnames[choice-1] ]);
    apply(#'display_detail_menu);
    input_to(#'detail_submenu, INPUT_PROMPT, "Exit] ");
    return;
}

private void mod_which_exit(string str) {
    int choice = to_int(str);
    if(!choice) {
      printf("No exit modified\n");
      apply(#'display_exit_menu);
      input_to(#'exit_submenu, INPUT_PROMPT, "Exit] ");
      return;
    }
    users[USER]["exit_to_mod"] = choice;
    users[USER]["old_exit_name"] = m_indices(users[USER]["exits"])[choice-1];
    printf("Modify %s how?\n", users[USER]["old_exit_name"]);
    printf("1  : Change name\n");
    printf("2  : Change destination\n");
    input_to(#'mod_exit, INPUT_PROMPT, "] ");
    return;
}

private void display_exit_menu() {
    printf("----=Exit Submenu=----\n");
    printf("Current exits: %s\n", exit_string());
    printf("----------------------\n");
    printf("1: Add an exit\n");
    printf("2: Delete an exit\n");
    printf("3: Modify an exit\n");
    printf("x: Return to main menu\n");
    return;
}

private void display_detail_menu() {
    printf("----=Detail Submenu=----\n");
    printf("Current details: %s\n", detail_string());
    printf("------------------------\n");
    printf("1: Add a detail\n");
    printf("2: Delete a detail\n");
    printf("3: Modify a detail\n");
    printf("x: Return to main menu\n");
    return;
}

private void display_menu() {
    mapping map = users[USER];
    printf("1  : Set filename [%s]\n", map["filename"]);
    printf("2  : Set header filename [%s]\n", map["hfilename"]);
    printf("3  : Set short description [%s]\n", map["short_desc"]);
    printf("4  : Display long description\n");
    printf("5  : Set long description\n");
    printf("6  : Set planet [%s]\n", map["planet"]);
    printf("7  : Set inside [%O]\n", map["inside"]);
    if(users[USER]["inside"] == 1)
      printf("8  : Set light [%s%O]\n",
      (map["light"] > 0 ? "" : ""), map["light"]); // the first "" contains an ansi
      						   // code for yellow and the second ""
						   // contains an ansi code for 
						   // grey/bold_black
    printf("9  : Exits [%s]\n", exit_string());
    printf("10 : Details [%s]\n", detail_string());
    printf("11 : Set room color [&%s%s]\n",
      users[USER]["color"],
      users[USER]["color"]);
    printf("w  : Generate and write code to file\n");
    printf("x  : Exit\n");
    return;
}

private void get_exit_path(string str) {
    if(str == "") {
      apply(#'display_exit_menu);
      input_to(#'exit_submenu, INPUT_PROMPT, "Exit] ");
      return;
    }
    users[USER]["exits"] += ([ users[USER]["exit_name"]: str ]);
    apply(#'display_exit_menu);
    input_to(#'exit_submenu, INPUT_PROMPT, "Exit] ");
    return;
}

private void mod_exit(string str) {
    int choice = to_int(str);
    if(!choice) {
      apply(#'display_exit_menu);
      apply(#'exit_submenu, "3");
      return;
    }
    if(choice == 1) {
      input_to(#'get_exit_name, INPUT_PROMPT, "Enter new exit name: ");
    }
    if(choice == 2) {
      input_to(#'get_exit_path, INPUT_PROMPT, "Enter new destination: ");
    }
    return;
}

private void get_detail_name(string str) {
    if(str == "") {
      apply(#'display_detail_menu);
      input_to(#'detail_submenu, INPUT_PROMPT, "Exit] ");
      return;
    }
    users[USER]["detail_name"] = str;
    users[USER]["editor"] = "detail";
    printf("Enter description of &%s%s:\n",
      users[USER]["color"],
      users[USER]["detail_name"]);
    printf("Type . or ** to save, or ~q to abort.\n");
    input_to(#'editor, INPUT_PROMPT, "] ");
    return;
}

private void get_exit_name(string str) {
    if(str == "") {
      apply(#'display_exit_menu);
      input_to(#'exit_submenu, INPUT_PROMPT, "Exit] ");
      return;
    }
    users[USER]["exit_name"] = str;
    if(users[USER]["get_exit_path"]) {
      input_to(#'get_exit_path, INPUT_PROMPT, "Enter destination: ");
      users[USER]["get_exit_path"] = 0;
    }
    else {
      mapping temp = users[USER]["exits"];
      temp += ([ users[USER]["exit_name"] : temp[users[USER]["old_exit_name"]] ]);
      temp -= ([ users[USER]["old_exit_name"] ]);
      users[USER] -= ([ "old_exit_name" ]);
      apply(#'display_exit_menu);
      input_to(#'exit_submenu, INPUT_PROMPT, "Exit] ");
      return;
    }
    return;
}

private void exit_submenu(string str) {
      string *enames = m_indices(users[USER]["exits"]);
      switch(str) {
        case "1":
          users[USER]["get_exit_path"] = 1;
          input_to(#'get_exit_name, INPUT_PROMPT, "Enter exit name: ");
          break;
        case "2":
          if(sizeof(enames))
            printf("Delete which exit?\n");
          else {
            printf("There are no exits defined.\n");
            break;
          }
          for(int x = 0; x < sizeof(users[USER]["exits"]); x++) {
            printf("%-=3O : &%s%s\n",
              x+1,
              users[USER]["color"],
              enames[x]);
          }
          input_to(#'delete_exit, INPUT_PROMPT, "] ");
          break;
        case "3":
          if(sizeof(enames))
            printf("Modify which exit?\n");
          else {
            printf("There are no exits defined.\n");
            break;
          }
          for(int x = 0; x < sizeof(users[USER]["exits"]); x++) {
            printf("%-=3O : &%s%s\n",
              x+1,
              users[USER]["color"],
              enames[x]);
          }
          input_to(#'mod_which_exit, INPUT_PROMPT, "] ");
          break;
        case "x":
          apply(#'display_menu);
          input_to(#'shell_loop, INPUT_PROMPT, "] ");
          return;
          break;
        default:
          printf("Unknown option.\n");
          break;
      }
      input_to(#'exit_submenu, INPUT_PROMPT, "Exit] ");
}

private void detail_submenu(string str) {
      string *dnames = m_indices(users[USER]["details"]);
      switch(str) {
        case "1":
          input_to(#'get_detail_name, INPUT_PROMPT, "Enter detail name: ");
          break;
        case "2":
          if(sizeof(dnames))
            printf("Delete which detail?\n");
          else {
            printf("There are no details defined.\n");
            break;
          }
          for(int x = 0; x < sizeof(users[USER]["details"]); x++) {
            printf("%-=3O : &%s%s\n",
              x+1,
              users[USER]["color"],
              dnames[x]);
          }
          input_to(#'delete_detail, INPUT_PROMPT, "] ");
          break;
        case "3":
          printf("Not implemented yet\n");
          break;
          if(sizeof(dnames))
            printf("Modify which detail?\n");
          else {
            printf("There are no details defined.\n");
            break;
          }
          for(int x = 0; x < sizeof(users[USER]["details"]); x++) {
            printf("%-=3O : &%s%s\n",
              x+1,
              users[USER]["color"],
              dnames[x]);
          }
          input_to(#'mod_which_exit, INPUT_PROMPT, "] ");
          break;
          printf("Delete a detail\n");
          break;
        case "x":
          apply(#'display_menu);
          input_to(#'shell_loop, INPUT_PROMPT, "] ");
          return;
          break;
        default:
          printf("Unknown option.\n");
          break;
      }
      input_to(#'detail_submenu, INPUT_PROMPT, "Detail] ");
}

private void get_filename(string input) {
    input = regreplace(input, " ", "_", RE_GLOBAL);
    if(input[<2..] != ".c") input += ".c";
    users[USER]["filename"] = input;
    apply(#'display_menu);
    input_to(#'shell_loop, INPUT_PROMPT, "] ");
    return;
}

private void get_hfilename(string input) {
    input = regreplace(input, " ", "_", RE_GLOBAL);
    if(input[<2..] != ".h") input += ".h";
    users[USER]["hfilename"] = input;
    apply(#'display_menu);
    input_to(#'shell_loop, INPUT_PROMPT, "] ");
    return;
}

private void get_short(string input) {
    if(input != "")
      users[USER]["short_desc"] = input;
    apply(#'display_menu);
    input_to(#'shell_loop, INPUT_PROMPT, "] ");
    return;
}

private void get_planet(string input) {
    if(input != "")
      users[USER]["planet"] = input;
    apply(#'display_menu);
    input_to(#'shell_loop, INPUT_PROMPT, "] ");
    return;
}

private void get_light(string input) {
    if(input != "") {
      if(input != "0" && !to_int(input))
        printf("You must enter an integer value!\n");
      else
        users[USER]["light"] = to_int(input);
    }
    apply(#'display_menu);
    input_to(#'shell_loop, INPUT_PROMPT, "] ");
    return;
}

private void get_color(string input) {
    if(input != "" && member(colors, input) != -1)
      users[USER]["color"] = input;
    else printf("Error: invalid color code\n");
    apply(#'display_menu);
    input_to(#'shell_loop, INPUT_PROMPT, "] ");
    return;
}

private void get_inside(string input) {
    if(input != "0" && input != "1") {
      printf("Invalid input, enter 1 for inside, 0 for outside.\n");
      input_to(#'get_inside, INPUT_PROMPT, "Enter inside: ");
      return;
    }
    users[USER]["inside"] = to_int(input);
    apply(#'display_menu);
    input_to(#'shell_loop, INPUT_PROMPT, "] ");
    return;
}

private void verify_write(string input) {
    if(input == "" || input == "y" || input == "Y") {
      users[USER]["quit_after_save"] = 1;
      apply(#'shell_loop, "w");
      apply(#'shell_loop, "x");
      return;
    }
    return;
}

private void verify_overwrite(string input) {
    if(input != "y") {
      printf("Write aborted.\n");
      if(users[USER]["quit_after_save"] == 1) {
        printf("Goodbye.\n");
        return;
      }
      apply(#'display_menu);
      input_to(#'shell_loop, INPUT_PROMPT, "] ");
      return;
    }
    users[USER]["overwrite"] = 1;
    apply(#'shell_loop, "w");
    return;
}

private void write_header() {
    string hfile;
    hfile = "";
    hfile += sprintf("// %s\n", users[USER]["hfilename"]);
    hfile += sprintf("// Created %s\n", ctime(time()));
    hfile += sprintf("// by %s [RoomMaker]\n",
      capitalize(this_player()->query_real_name()));
    hfile += "\n";
    hfile += sprintf("#define %s(x) sprintf(\"%s/%%s\",x)\n",
      macro_name(), this_player()->query_path());
    write_file(sprintf("%s/%s", this_player()->query_path(),
      users[USER]["hfilename"]), hfile);
    printf("Header file [%s] written\n", users[USER]["hfilename"]);
    return;
}

private void shell_loop(string input) {

      switch(input) {
        case "x":
          if(!users[USER]["written"] && users[USER]["filename"] != "") {
            input_to(#'verify_write, INPUT_PROMPT, "Save before exit? (Y/n) ");
            return;
            break;
          }
          printf("Goodbye.\n");
          return;
          break;
        case "1":
          input_to(#'get_filename, INPUT_PROMPT, "Enter filename: ");
          break;
        case "2":
          input_to(#'get_hfilename, INPUT_PROMPT, "Enter header filename: ");
          break;
        case "3":
          input_to(#'get_short, INPUT_PROMPT, "Enter short description: ");
          break;
        case "5":
          printf("Enter new long description:\n");
          printf("Type . or ** to save, ~q to abort.\n");
          users[USER]["editor"] = "long_desc";
          input_to(#'editor, INPUT_PROMPT, "] ");
          break;
        case "4":
          printf("Current long description:\n");
          printf("------------------------\n");
          printf("%s", users[USER]["long_desc"]);
          printf("------------------------\n");
          apply(#'display_menu);
          break;
        case "6":
          input_to(#'get_planet, INPUT_PROMPT, "Enter planet: ");
          break;
        case "7":
          input_to(#'get_inside, INPUT_PROMPT, "Set inside (0 = outside, 1 = inside): ");
          break;
        case "8":
          if(users[USER]["inside"] == 0) {
            printf("Error: you must set inside to 1 first.\n");
            apply(#'display_menu);
            break;
          }
          input_to(#'get_light, INPUT_PROMPT, "Enter light: ");
          break;
        case "9":
          apply(#'display_exit_menu);
          input_to(#'exit_submenu, INPUT_PROMPT, "Exit] ");
          break;
        case "10":
          apply(#'display_detail_menu);
          input_to(#'detail_submenu, INPUT_PROMPT, "Detail] ");
          break;
        case "11":
          printf("Valid color codes = b B c C g G m M r R w W y Y K\n");
          input_to(#'get_color, INPUT_PROMPT, "Enter color code: ");
          break;
        case "w":
          string current_path = this_player()->query_path();
          string final = sprintf("%s/%s", current_path, users[USER]["filename"]);
          if(!users[USER]["overwrite"])
          if(file_size(final) != -1) {
            printf("Warning: file already exists.\n");
            input_to(#'verify_overwrite, INPUT_PROMPT, "Overwrite? (y/N) ");
            return;
            break;
          }
          string file = generate_code();
          if(file == "") {
            printf("Error generating code.\n");
            break;
          }
          if(file_size(this_player()->query_path()+"/"+
            users[USER]["hfilename"]) == -1)
              write_header();
          printf("Writing %s\n", final);
          if(users[USER]["overwrite"])
            write_file(final, file, 1);
          else
            write_file(final, file);
          printf("Write completed.\n");
          users[USER]["overwrite"] = 0;
          users[USER]["written"] = 1;
          if(users[USER]["quit_after_save"] == 1) {
            return;
            break;
          }
          apply(#'display_menu);
          break;
        default:
            printf("Unknown option.\n");
          break;
      }
    input_to(#'shell_loop, INPUT_PROMPT, "] ");
    return;
}

private void init_user() {

    users -= ([ USER ]);
    users += ([ USER : ([
      "filename": filename,
      "hfilename": hfilename,
      "planet": planet,
      "inside": inside,
      "light": light,
      "short_desc": short_desc,
      "long_desc": long_desc,
      "color": color,
      "exits": ([ ]),
      "details": ([ ]),
      "overwrite": 0,
    ]) ]);
}

private string macro_name() {
    string current = this_player()->query_path();
    string *split = explode(current, "/");
    return upper_case(split[sizeof(split)-1]);
}

// This is where you would make sure the code that is generated
// works for your lib.
private string generate_code() {
    string file = "";
    string *long = explode(users[USER]["long_desc"],"\n")-({""});
    string current_path = this_player()->query_path();
    string final = sprintf("%s/%s", current_path, users[USER]["filename"]);
    if(users[USER]["filename"] == "") return
      printf("Invalid filename!\n"),"";
    file += sprintf("%s\n", HEADER);
    file += sprintf("    set_short(\"%s\");\n", users[USER]["short_desc"]);
    file += "    set_long(";
    for(int x = 0; x < sizeof(long); x++) {
      long[x] += "\\n";
      if(x == sizeof(long)-1)
        file += sprintf("%s%O);\n",
          (x == 0 ? "" : "             "), /* controls indentation */
          long[x]);
      else {
        file += sprintf("%s%O+\n",
          (x == 0 ? "" : "             "),
          long[x]);
      }
    }
    if(users[USER]["inside"] == 1) {
       file += sprintf("    set_property(\"inside\",1);\n");
       file += sprintf("    set_light(%O);\n", users[USER]["light"]);
    }
    file += sprintf("    set_planet(\"%s\");\n", users[USER]["planet"]);
    file += sprintf("    short_color = \"%s\";",
      real_colors[users[USER]["color"]]);
    file += "\n\n    exits = ([\n";
    for(int x = 0; x < sizeof(users[USER]["exits"]); x++) {
      string *enames = m_indices(users[USER]["exits"]);
      file += sprintf("      %O : %s(%O),\n",
        enames[x],
        macro_name(),
        users[USER]["exits"][enames[x]]);
    }
    file += "    ]);\n";
    file += "\n    details = ([\n";
    for(int x = 0; x < sizeof(users[USER]["details"]); x++) {
      string *dnames = m_indices(users[USER]["details"]);
      string *desc = explode(users[USER]["details"][dnames[x]],"\n")-({""});
      file += sprintf("      %O : ", dnames[x]);
      for(int y = 0; y < sizeof(desc); y++) {
        if(y != sizeof(desc)-1)
          desc[y] += "\\n";
        if(y == sizeof(desc)-1)
          file += sprintf("%s%O,\n",
            (y == 0 ? "" : "        "), /* indentation again */
            desc[y]);
      else
        file += sprintf("%s%O+\n",
          (y == 0 ? "" : "        "),
          desc[y]);
      }
    }
    file += "    ]);\n";
    file += "\n    return;\n";
    file += "}\n";
    return file;
}

// this is the function that gets called to start the program
public status main(string str) {
    string current = this_player()->query_path()+"/";
    if(!this_player()->valid_write(current, 1)) return
      printf("You do not have write access to %s\n", current),1;
    printf("[=-----RoomMakerV1-----=]\n");
    apply(#'init_user);
    apply(#'display_menu);
    input_to(#'shell_loop, INPUT_PROMPT, "] ");
    return 1;
}