//    This file does not contain additional comment.
//    Comment is provided in the distribution files under ./scripts

string g_confPath;
string g_home = getenv("HOME")[1] + "/.icmake";
string g_skelPath = "/usr/share/icmake";
string g_program = "icmstart";
string g_defaultCommand;
string g_defaultCommandArg;
list g_defaultCommands = [ "program", "library" ];
string g_destPath;
string g_destSpec;
string g_cwd = chdir("");
string g_icmconf;
list g_mkdir;
list g_actions = ["b", "P", "L", "D", "PD", "Pb", "bP", "DP", "pL", "Lp", 
                  "LD", "DL"];
  
int g_confirmInstall = 0;
int g_skeletons = 1;
int g_replace = 0;
int g_debug = 0;
int g_askReplace = 1;
int g_version = 1;
int g_basic = 0;
int g_modIcmconf = 1;
int g_fileStat = 0;
void usage()
{
    printf("Usage: ", g_program, " [Options] dir [program|library]\n"
        "Where:\n"
        "   Options:\n"
        "     -b: basic: the files usage.cc and version.cc are not "
                                                                "installed\n"
        "     -c confpath: Use the configuration files (icmstart.rc, "
                                                                "AUTHOR,\n"
        "                  VERSION, YEARS found in `confpath' rather than\n"
        "                  if found in $HOME/.icmake or /etc/icmake\n"
        "     -d: debug: do not execute any commands, but show commands\n"
        "         that would have been executed\n"
        "     -I: do NOT install any files.\n"
        "     -r: replace existing files/directories\n"
        "     -s skelPath: Read the skeleton information from the directory\n"
        "                  `skelPath' rather than /usr/share/icmake\n"
        "   dir: the directory to install the files into\n"
        "   program, library: command passed by default to the icmbuild "
                                                                    "script\n"
        "\n");
    exit(0);
}
void quit(string str)
{
    printf(g_program, ": ", str, '\n');
    exit(1);
}
int isFileOrDir(string entry)
{
    return g_fileStat = (int)stat(P_NOCHECK, entry)[0] & (S_IFREG | S_IFDIR);
}
int isFile(string s)
{
    return (int)stat(P_NOCHECK, s)[0] & S_IFREG;
}
void syscall(string command)
{
    if (g_debug)
        printf(command, '\n');
    else
        system(command);
}
void md(string dir)
{
    syscall("mkdir -p " + dir);    
}
string readlink(string name)
{                                   
    return eval("readlink -mn " + name)[0]; 
}
string absPath(string arg)
{
    if (arg[0] != "/")
        arg = g_cwd + arg;
    return readlink(arg) + "/";
}
string absSource(string source)
{
    if (strfind(source, "~/") == 0)
        source = readlink(source);
    else if (source[0] != '/')
        source = g_skelPath + source;   
    if (!isFileOrDir(source))
        quit("Can't find source `" + source + "'");
    return source;
}
string absDest(string dest)
{
    int offset =
        strfind(dest, "~/") == 0 ?  2 :
        strfind(dest, "/")  == 0 ?  1 :
                                    0;
    if (offset != 0)               
        dest = substr(dest, offset, 999);
    dest = readlink(g_destPath + dest);
    if (strfind(dest, g_destPath) != 0)
        quit("dest: `" + dest + "' doesn't start with  `" + g_destPath + "'");
    
    return dest;
}
void arguments(int argc, list argv)
{
    list icm = getenv("ICM");
    if ((int)icm[0] == 1)                
        g_skelPath = icm[1];
    int cmdidx = 1;
    while (cmdidx < argc)
    {
        string arg = argv[cmdidx];
        if (arg[0] != "-")
            break;
        if (arg[1] == "b")
        {
            g_basic = 1;
            g_version = 0;
        }
        else if (arg[1] == "c")
        {
            arg = substr(arg, 2, 999);
            if (arg == "")
            {
                if (cmdidx == argc)
                    quit("-c lacks configuration file specification");
                arg = argv[++cmdidx];
            }
            g_confPath = absPath(arg);
        }
        else if (arg[1] == "d")
            g_debug = 1;
        else if (arg[1] == "I")
            g_skeletons = 0;
        else if (arg[1] == "r")
        {
            g_askReplace = 0;
            g_replace = 1;
        }
        else if (arg[1] == "s")
        {
            arg = substr(arg, 2, 999);
            if (arg == "")
            {
                if (cmdidx == argc)
                    quit("-s lacks skeleton dir specification");
                arg = argv[++cmdidx];
            }
            g_skelPath = arg;
        }
        else
            quit("Option `" + arg + "' not supported\n");
        ++cmdidx;
    }
    g_skelPath = absPath(g_skelPath);
    g_destSpec = argv[cmdidx];
    g_destPath = absPath(g_destSpec);
    g_icmconf = g_destPath + "icmconf";
    if (++cmdidx < argc)
    {
        g_defaultCommandArg = argv[cmdidx];
        if (listfind(g_defaultCommands, g_defaultCommandArg) == -1)
        {
            printf("Initial command `", g_defaultCommandArg, 
                                                        "' not supported\n");
            exit(1);
        }
        if (g_defaultCommandArg == "library")
            g_version = 0;
        g_defaultCommand = 
                "\n"
                "#define DEFCOM     \"" + g_defaultCommandArg + "\"\n";
    }
    md(g_destPath);
}
int replace(string target)
{
    while (g_askReplace)
    {
        printf("`", target, "' exists.\n"
               "Replace [?akNqy] ? ");
        string answer = getch();
        printf('\n');
        if (answer == "a")
        {
            g_replace = 1;
            g_askReplace = 0;
            break;
        }
        if (answer == "k")
        {
            g_askReplace = 0;
            g_modIcmconf = 0;
            break;
        }
        if (answer == "y")
            return 1;
        if (answer == "q")
            exit(0);
        if (answer == "n" || answer == "\n")
        {
            if (target == "icmconf")
                g_modIcmconf = 0;
            return 0;
        }
        printf("Press `a' : replace ", target, " and ALL remaining files,\n"
               "      `k' : KEEP ", target, " and all remaining files\n"
               "      `n' : (or press Enter) do NOT replace ", target, 
                                                                " (default)\n"
               "      `q' : QUIT (do NOT replace ", target, 
                                                ", and END icmstart NOW)\n"
               "      'y' : REPLACE ", target, "\n"
               "      `?' : show this help\n");
    }
    return g_replace;
}
int installOK(string dest, string target)
{
    return !exists(target) || replace(dest);
}
void install_entry(string realSource, string dest, string realDest)
{
    if (!installOK(dest, realDest))
        return;
    md(get_path(realDest));
    if ((int)stat(realSource)[0] & S_IFDIR)     
    {
        list parts = strtok(realDest, "/");
        int end = listlen(parts) - 1;
        string dest = parts[ end ];
        string dir;
        for (int idx = 0; idx != end; ++idx)
            dir += '/' + parts[idx];
        printf << "add " << dest << " to " << dir << "/CLASSES [yN] ? ";
        if (getch() == 'y')
            fprintf << dir + "/CLASSES" << dest << '\n';
        printf << '\n';
    }
    syscall("cp -rd " + realSource + " " + realDest);
    if (string icmconf = realDest + "/icmconf"; exists(icmconf))
    {
        syscall("cat " + icmconf + " >> " + g_icmconf);
        syscall("rm " + icmconf);
    }
}
string findIn(string path, string entry)
{
    string ret = path + "/" + entry;
    return exists(ret) ? ret : "";
}
string findFile(string file)
{
    string ret;
    if (g_confPath != "")
        ret = findIn(g_confPath, file);
    if (!ret)
        ret = findIn(g_home, file);
    if (!ret)
        ret = findIn("/etc/icmake", file);
    if (!isFile(ret))
        quit("Can't find configuration file `" + file + "'");
    
    return ret;
}
void version_field(string file, string default)
{
    if (g_skeletons)
    {
        string ret = findFile(file);
        if (ret != "")
            system("cat " + ret + " >> " + g_destPath + "VERSION");
        else
            fprintf(g_destPath + "VERSION", 
                        "#define ", file, " \"", default, "\"\n");
    }
}    
void install_version()
{
    if (!g_version)
        return;
    string str = g_destPath + "VERSION";
    if (installOK("VERSION", str))
    {
        system(P_NOCHECK, "rm -f " + str);
        version_field("AUTHOR",  "");
        version_field("VERSION", "0.00.00");
        str = "date '+%Y'";
        str = `str`[0];
        version_field("YEARS", resize(str, strlen(str) - 1));
    }
}
void checkDEFCOM()
{
    if (g_modIcmconf == 0)
        return;
    if (g_defaultCommand != "")
        fprintf(g_icmconf, g_defaultCommand);
    if (strfind(g_defaultCommand, "library") != -1)
        syscall("sed -i '\n"
            "s?^//\\(#define LIBRARY\\)?\\1?\n"
            "s?^\\(#define MAIN\\)?//\\1?\n"
            "' " + g_icmconf);
    if (!g_version)
        syscall("sed -i '\n"
                "s?^\\(#define USE_VERSION\\)?//\\1?\n"
            "' " + g_icmconf);
}
int confirmInstall(string source, string dest)
{
    if (!g_confirmInstall)
        return 1;
    if (source == dest)
        printf("Install `", dest, "' [yN] ? ");
    else
        printf("Install `", source, "' as `", dest, "' [yN] ? ");
    int ret = getch() == "y";
    printf('\n');
    return ret;
}
list shift(list lst)
{
    list ret;
    for (int idx = 1, end = listlen(lst); idx != end; ++idx)
        ret += (list)lst[idx];
    return ret;
}
int skip(string flags)
{
    return
        (int)
        (
            g_basic && strchr(flags, "b") != -1
            ||
            strchr(flags, "D") == -1
            &&
            (
                g_defaultCommandArg == "library" && strchr(flags, "P") != -1
                ||
                g_defaultCommandArg == "program" && strchr(flags, "L") != -1
            )
        );
}
list getSourceDest(string confLine)
{
    list nop;
    list fields = strtok(confLine, " \t\n");
    string source = fields[0];
    if (listlen(fields) == 0 || source[0] == "#")
        return nop;
    string flags = source;
    if (listfind(g_actions, flags) == -1)
        flags = "";                      
    else
    {
        fields = shift(fields);
        source = fields[0];
    }
    g_confirmInstall = source == "?";
    if (g_confirmInstall)
        fields = shift(fields);
    return skip(flags) ?
                nop
            :
                fields;
}
void install_line(string confline)
{
    list sourceDest = getSourceDest(confline);
    if (!sourceDest)
        return;
    string realSource = absSource(sourceDest[0]);
    string dest = sourceDest[listlen(sourceDest) > 1];
    string realDest = absDest(dest);
    if (!g_replace && !g_askReplace && exists(realDest))
        return;
    if (confirmInstall(sourceDest[0], dest))
        install_entry(realSource, dest, realDest);
}
void install_rc()
{
    string conffile = findFile("icmstart.rc");  
    while (list line = fgets(conffile, line))
        install_line(line[0]);
    install_version();
    checkDEFCOM();
}
void main(int argc, list argv)
{
    echo(OFF);
    if (argc == 1)
        usage();
    arguments(argc, argv);
    install_rc();
    printf("Done. ");
    if (g_modIcmconf)
        printf("Don't forget to inspect the #defines in "
            "'", g_destSpec, "/icmconf'\n\n");
    else
        printf("'", g_destSpec, "/icmconf' not modified\n\n");
}
