/*
 * OpenComal -- a free Comal implementation
 *
 * This file is part of the OpenComal package.
 * (c) Copyright 1992-2024 Jos Visser <josvisser66@gmail.com>
 *
 * The OpenComal package is covered by the GNU General Public
 * License. See doc/LICENSE for more information.
 */

/* OpenComal program management functions */

#include "pdcglob.h"
#include "pdcsqash.h"
#include "pdcsym.h"
#include "pdcprog.h"
#include "pdcscan.h"
#include "pdcfree.h"
#include "pdcmisc.h"
#include "pdcenv.h"
#include "pdcexec.h"
#include "pdcmod.h"
#include "pdccmd.h"


PUBLIC void prog_addline(struct comal_line *line) {
    struct comal_line *work = curenv->progroot;
    struct comal_line *last = NULL;
    int scan = 0;

    while (work && work->ld->lineno < line->ld->lineno) {
        last = work;
        work = work->ld->next;
    }

    if (!work || work->ld->lineno > line->ld->lineno)
        line->ld->next = work;
    else {
        line->ld->next = work->ld->next;
        scan = assess_scan(work);
        line_free(work, 1);
    }

    if (last) {
        last->ld->next = line;

        if (scan_nescessary(last) == STRUCTURE_START)
            line->ld->indent = last->ld->indent + INDENTION;
        else
            line->ld->indent = last->ld->indent;
    } else
        curenv->progroot = line;

    curenv->changed = 1;

    if (!scan)
        scan = assess_scan(line);

    if (scan)
        prog_structure_scan();
}

PUBLIC int prog_del(struct comal_line **root, long from, long to, int mainprog) {
    struct comal_line *work = *root;
    struct comal_line *last = NULL;
    struct comal_line *next;
    int scan = 0;

    while (work && work->ld->lineno < from) {
        last = work;
        work = work->ld->next;
    }

    while (work && work->ld->lineno <= to) {
        next = work->ld->next;

        if (!scan)
            scan = assess_scan(work);

        line_free(work, mainprog);
        work = next;
    }

    if (last)
        last->ld->next = work;
    else
        *root = work;

    return scan;
}


PUBLIC long prog_highest_line() {
    struct comal_line *work = curenv->progroot;
    struct comal_line *last = NULL;

    if (!work)
        return 0L;

    while (work) {
        last = work;
        work = work->ld->next;
    }

    return last->ld->lineno;
}

PUBLIC void prog_total_scan() {
    char errtxt[MAX_LINELEN];
    struct comal_line *errline = NULL;

    if (comal_debug)
        my_printf(MSG_DEBUG, 1, "Total scanning...");

    curenv->scan_ok = scan_scan(NULL, errtxt, &errline);

    if (!curenv->scan_ok) {
        if (errline)
            puts_line(MSG_ERROR, errline);

        my_printf(MSG_ERROR, 1, errtxt);
    }
}


PUBLIC void prog_new() {
    clear_env(curenv);
    mem_freepool_private(curenv->program_pool);
}


PUBLIC void prog_load(char *fn) {
    if (comal_debug)
        my_printf(MSG_DEBUG, 1, "LOADing %s", fn);

    prog_new();
    curenv->progroot = expand_fromfile(fn);
    curenv->changed = 0;
}


PUBLIC void prog_run() {
    clean_runenv(curenv);

    if (runfilename) {
        prog_load(runfilename);
        prog_structure_scan();
        mem_free(runfilename);
        runfilename = NULL;
    }

    curenv->curenv = ROOTENV;

    prog_total_scan();

    if (curenv->scan_ok) {
        mod_initall();
        exec_seq(curenv->progroot);
    }
}

PRIVATE void obfus() {
    struct comal_line *work = curenv->progroot;
    struct comal_line *last = 0;
    struct comal_line *next = 0;

    if (!work)
        return;

    /* Remove all comments and blank lines */
    while (work) {
        /* Remove comment */
        if (work->ld->rem) {
            mem_free(work->ld->rem);
            work->ld->rem = 0;
        }

        /* Is this line empty? Remove it */
        if (work->cmd == 0) {
            if (!last)
                curenv->progroot = work->ld->next;
            else
                last->ld->next = work->ld->next;

            next = work->ld->next;
            line_free(work, 1);
        } else {
            last = work;
            next = work->ld->next;
        }

        work = next;
    }

}

PUBLIC void prog_obfuscate() {
    if (check_changed()) {
        my_printf(MSG_DIALOG, 1, "Obfuscating source code...");
        prog_total_scan();
        obfus();
        renum_horse(1, 1);
        my_printf(MSG_DIALOG, 1, "Done. Now SAVE!");
        curenv->save_obfus = 1;
    }
}
