// define TVDOS
var _TVDOS = {};
_TVDOS.VERSION = "1.0";
_TVDOS.DRIVES = {}; // Object where key-value pair is <drive-letter> : [serial-port, drive-number]
// actually figure out the drive letter association
// Drive A is always the device we're currently on
_TVDOS.DRIVES["A"] = _BIOS.FIRST_BOOTABLE_PORT;
//TODO


_TVDOS.getPath = function() {
    return _TVDOS.variables.PATH.split(';');
};
// initial values
_TVDOS.variables = {
    DOSDIR: "\\tvdos",
    LANG: "EN",
    PATH: "\\tvdos\\bin",
    PATHEXT: ".com;.bat;.js",
    HELPPATH: "\\tvdos\\help",
    OS_NAME: "Terrarum Virtual DOS",
    OS_VERSION: _TVDOS.VERSION
};
Object.freeze(_TVDOS);

///////////////////////////////////////////////////////////////////////////////

var filesystem = {};
filesystem._toPorts = function(driveLetter) {
    if (driveLetter.toUpperCase === undefined) {
        throw Error("'"+driveLetter+"' (type: "+typeof driveLetter+") is not a valid drive letter");
    }
    let port = _TVDOS.DRIVES[driveLetter.toUpperCase()];
    if (port === undefined) {
        throw Error("Drive letter '" + driveLetter.toUpperCase() + "' does not exist");
    }
    return port
};
filesystem._close = function(portNo) {
    com.sendMessage(portNo, "CLOSE");
};
filesystem._flush = function(portNo) {
    com.sendMessage(portNo, "FLUSH");
};
// @return true if operation committed successfully, false if:
//             - opening file with R-mode and target file does not exists
//         throws if:
//             - java.lang.NullPointerException if path is null
//             - Error if operation mode is not "R", "W" nor "A"
filesystem.open = function(driveLetter, path, operationMode) {
    let port = filesystem._toPorts(driveLetter);

    filesystem._flush(port[0]); filesystem._close(port[0]);

    let mode = operationMode.toUpperCase();
    if (mode != "R" && mode != "W" && mode != "A") {
        throw Error("Unknown file opening mode: " + mode);
    }

    com.sendMessage(port[0], "OPEN"+mode+'"'+path+'",'+port[1]);
    let response = com.getStatusCode(port[0]);
    return (response == 0);
};
// @return the entire contents of the file in String
filesystem.readAll = function(driveLetter) {
    let port = filesystem._toPorts(driveLetter);
    com.sendMessage(port[0], "READ");
    let response = com.getStatusCode(port[0]);
    if (135 == response) {
        throw Error("File not opened");
    }
    if (response < 0 || response >= 128) {
        throw Error("Reading a file failed with "+response);
    }
    return com.pullMessage(port[0]);
};
filesystem.write = function(driveLetter, string) {
    let port = filesystem._toPorts(driveLetter);
    com.sendMessage(port[0], "WRITE"+string.length);
    let response = com.getStatusCode(port[0]);
    if (135 == response) {
        throw Error("File not opened");
    }
    if (response < 0 || response >= 128) {
        throw Error("Writing a file failed with "+response);
    }
    com.sendMessage(port[0], string);
    filesystem._flush(port[0]); filesystem._close(port[0]);
};
filesystem.isDirectory = function(driveLetter) {
    let port = filesystem._toPorts(driveLetter);
    com.sendMessage(port[0], "LISTFILES");
    let response = com.getStatusCode(port[0]);
    return (response === 0);
};
filesystem.mkDir = function(driveLetter) {
    let port = filesystem._toPorts(driveLetter);
    com.sendMessage(port[0], "MKDIR");
    let response = com.getStatusCode(port[0]);

    if (response < 0 || response >= 128) {
        let status = com.getDeviceStatus(port[0]);
        throw Error("Creating a directory failed with ("+response+"): "+status.message+"\n");
    }
    return (response === 0); // possible status codes: 0 (success), 1 (fail)
};
filesystem.touch = function(driveLetter) {
    let port = filesystem._toPorts(driveLetter);
    com.sendMessage(port[0], "TOUCH");
    let response = com.getStatusCode(port[0]);
    return (response === 0);
};
filesystem.mkFile = function(driveLetter) {
    let port = filesystem._toPorts(driveLetter);
    com.sendMessage(port[0], "MKFILE");
    let response = com.getStatusCode(port[0]);
    return (response === 0);
};
Object.freeze(filesystem);

///////////////////////////////////////////////////////////////////////////////

// install other stuffs
filesystem.open("A", "tvdos/gl.js", "R");
var GL = eval(filesystem.readAll("A"));

// @param cmdsrc JS source code
// @param args arguments for the program, must be Array, and args[0] is always the name of the program, e.g.
//        for command line 'echo foo bar', args[0] must be 'echo'
var execApp = function(cmdsrc, args) {
    let prg = eval("let _appStub=function(exec_args){"+cmdsrc+"};_appStub;"); // making 'exec_args' a app-level global
    return prg(args);
}

///////////////////////////////////////////////////////////////////////////////

// Boot script
serial.println("TVDOS.SYS initialised, running boot script...");
var _G = {};
filesystem.open("A", "tvdos/bin/command.js", "R");
execApp(filesystem.readAll("A"), ["", "/c", "\\AUTOEXEC.BAT"]);
