"use strict";

var term,
    protocol,
    socketURL,
    socket,
    pid;

Terminal.applyAddon(fit);
Terminal.applyAddon(attach);
Terminal.applyAddon(zmodem);
Terminal.applyAddon(search);

var terminalContainer = document.getElementById('terminal-container'),
    actionElements = {
      findNext: document.querySelector('#find-next'),
      findPrevious: document.querySelector('#find-previous')
    },
    optionElements = {
      cursorBlink: document.querySelector('#option-cursor-blink'),
      cursorStyle: document.querySelector('#option-cursor-style'),
      scrollback: document.querySelector('#option-scrollback'),
      tabstopwidth: document.querySelector('#option-tabstopwidth'),
      bellStyle: document.querySelector('#option-bell-style')
    },
    colsElement = document.getElementById('cols'),
    rowsElement = document.getElementById('rows');

function setTerminalSize() {
  var cols = parseInt(colsElement.value, 10);
  var rows = parseInt(rowsElement.value, 10);
  var viewportElement = document.querySelector('.xterm-viewport');
  var scrollBarWidth = viewportElement.offsetWidth - viewportElement.clientWidth;
  var width = (cols * term.charMeasure.width + 20 /*room for scrollbar*/).toString() + 'px';
  var height = (rows * term.charMeasure.height).toString() + 'px';

  terminalContainer.style.width = width;
  terminalContainer.style.height = height;
  term.resize(cols, rows);
}

colsElement.addEventListener('change', setTerminalSize);
rowsElement.addEventListener('change', setTerminalSize);

actionElements.findNext.addEventListener('keypress', function (e) {
  if (e.key === "Enter") {
    e.preventDefault();
    term.findNext(actionElements.findNext.value);
  }
});
actionElements.findPrevious.addEventListener('keypress', function (e) {
  if (e.key === "Enter") {
    e.preventDefault();
    term.findPrevious(actionElements.findPrevious.value);
  }
});

optionElements.cursorBlink.addEventListener('change', function () {
  term.setOption('cursorBlink', optionElements.cursorBlink.checked);
});
optionElements.cursorStyle.addEventListener('change', function () {
  term.setOption('cursorStyle', optionElements.cursorStyle.value);
});
optionElements.bellStyle.addEventListener('change', function () {
  term.setOption('bellStyle', optionElements.bellStyle.value);
});
optionElements.scrollback.addEventListener('change', function () {
  term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10));
});
optionElements.tabstopwidth.addEventListener('change', function () {
  term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10));
});

createTerminal();

function createTerminal() {
  // Clean terminal
  while (terminalContainer.children.length) {
    terminalContainer.removeChild(terminalContainer.children[0]);
  }
  term = new Terminal({
    cursorBlink: optionElements.cursorBlink.checked,
    scrollback: parseInt(optionElements.scrollback.value, 10),
    tabStopWidth: parseInt(optionElements.tabstopwidth.value, 10)
  });
  term.on('resize', function (size) {
    if (!pid) {
      return;
    }
    var cols = size.cols,
        rows = size.rows,
        url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;

    fetch(url, {method: 'POST'});
  });
  protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
  socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/';

  term.open(terminalContainer);
  term.fit();

  // fit is called within a setTimeout, cols and rows need this.
  setTimeout(function () {
    colsElement.value = term.cols;
    rowsElement.value = term.rows;

    // Set terminal size again to set the specific dimensions on the demo
    setTerminalSize();

    fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {

      res.text().then(function (pid) {
        window.pid = pid;
        socketURL += pid;
        socket = new WebSocket(socketURL);
        socket.onopen = runRealTerminal;
        socket.onclose = runFakeTerminal;
        socket.onerror = runFakeTerminal;

        term.zmodemAttach(socket, {
            noTerminalWriteOutsideSession: true,
        } );

        term.on("zmodemRetract", () => {
            start_form.style.display = "none";
            start_form.onsubmit = null;
        });

        term.on("zmodemDetect", (detection) => {
            function do_zmodem() {
                term.detach();
                let zsession = detection.confirm();

                var promise;

                if (zsession.type === "receive") {
                    promise = _handle_receive_session(zsession);
                }
                else {
                    promise = _handle_send_session(zsession);
                }

                promise.catch( console.error.bind(console) ).then( () => {
                    term.attach(socket);
                } );
            }

            if (_auto_zmodem()) {
                do_zmodem();
            }
            else {
                start_form.style.display = "";
                start_form.onsubmit = function(e) {
                    start_form.style.display = "none";

                    if (document.getElementById("zmstart_yes").checked) {
                        do_zmodem();
                    }
                    else {
                        detection.deny();
                    }
                };
            }
        });
      });
    });
  }, 0);
}

//----------------------------------------------------------------------
// UI STUFF

function _show_file_info(xfer) {
    var file_info = xfer.get_details();

    document.getElementById("name").textContent = file_info.name;
    document.getElementById("size").textContent = file_info.size;
    document.getElementById("mtime").textContent = file_info.mtime;
    document.getElementById("files_remaining").textContent = file_info.files_remaining;
    document.getElementById("bytes_remaining").textContent = file_info.bytes_remaining;

    document.getElementById("mode").textContent = "0" + file_info.mode.toString(8);

    var xfer_opts = xfer.get_options();
    ["conversion", "management", "transport", "sparse"].forEach( (lbl) => {
        document.getElementById(`zfile_${lbl}`).textContent = xfer_opts[lbl];
    } );

    document.getElementById("zm_file").style.display = "";
}
function _hide_file_info() {
    document.getElementById("zm_file").style.display = "none";
}

function _save_to_disk(xfer, buffer) {
    return Zmodem.Browser.save_to_disk(buffer, xfer.get_details().name);
}

var skipper_button = document.getElementById("zm_progress_skipper");
var skipper_button_orig_text = skipper_button.textContent;

function _show_progress() {
    skipper_button.disabled = false;
    skipper_button.textContent = skipper_button_orig_text;

    document.getElementById("bytes_received").textContent = 0;
    document.getElementById("percent_received").textContent = 0;

    document.getElementById("zm_progress").style.display = "";
}

function _update_progress(xfer) {
    var total_in = xfer.get_offset();

    document.getElementById("bytes_received").textContent = total_in;

    var percent_received = 100 * total_in / xfer.get_details().size;
    document.getElementById("percent_received").textContent = percent_received.toFixed(2);
}

function _hide_progress() {
    document.getElementById("zm_progress").style.display = "none";
}

var start_form = document.getElementById("zm_start");

function _auto_zmodem() {
    return document.getElementById("zmodem-auto").checked;
}

// END UI STUFF
//----------------------------------------------------------------------

function _handle_receive_session(zsession) {
    zsession.on("offer", function(xfer) {
        current_receive_xfer = xfer;

        _show_file_info(xfer);

        var offer_form = document.getElementById("zm_offer");

        function on_form_submit() {
            offer_form.style.display = "none";

            //START
            //if (offer_form.zmaccept.value) {
            if (_auto_zmodem() || document.getElementById("zmaccept_yes").checked) {
                _show_progress();

                var FILE_BUFFER = [];
                xfer.on("input", (payload) => {
                    _update_progress(xfer);
                    FILE_BUFFER.push( new Uint8Array(payload) );
                });
                xfer.accept().then(
                    () => {
                        _save_to_disk(xfer, FILE_BUFFER);
                    },
                    console.error.bind(console)
                );
            }
            else {
                xfer.skip();
            }
            //END
        }

        if (_auto_zmodem()) {
            on_form_submit();
        }
        else {
            offer_form.onsubmit = on_form_submit;
            offer_form.style.display = "";
        }
    } );

    var promise = new Promise( (res) => {
        zsession.on("session_end", () => {
            _hide_file_info();
            _hide_progress();
            res();
        } );
    } );

    zsession.start();

    return promise;
}

function _handle_send_session(zsession) {
    var choose_form = document.getElementById("zm_choose");
    choose_form.style.display = "";

    var file_el = document.getElementById("zm_files");

    var promise = new Promise( (res) => {
        file_el.onchange = function(e) {
            choose_form.style.display = "none";

            var files_obj = file_el.files;

            Zmodem.Browser.send_files(
                zsession,
                files_obj,
                {
                    on_offer_response(obj, xfer) {
                        if (xfer) _show_progress();
                        //console.log("offer", xfer ? "accepted" : "skipped");
                    },
                    on_progress(obj, xfer) {
                        _update_progress(xfer);
                    },
                    on_file_complete(obj) {
                        //console.log("COMPLETE", obj);
                        _hide_progress();
                    },
                }
            ).then(_hide_progress).then(
                zsession.close.bind(zsession),
                console.error.bind(console)
            ).then( () => {
                _hide_file_info();
                _hide_progress();
                res();
            } );
        };
    } );

    return promise;
}

//This is here to allow canceling of an in-progress ZMODEM transfer.
var current_receive_xfer;

//Called from HTML directly.
function skip_current_file() {
    current_receive_xfer.skip();

    skipper_button.disabled = true;
    skipper_button.textContent = "Waiting for server to acknowledge skip …";
}

function runRealTerminal() {
    term.attach(socket);

    term._initialized = true;
}

function runFakeTerminal() {
  if (term._initialized) {
    return;
  }

  term._initialized = true;

  var shellprompt = '$ ';

  term.prompt = function () {
    term.write('\r\n' + shellprompt);
  };

  term.writeln('Welcome to xterm.js');
  term.writeln('This is a local terminal emulation, without a real terminal in the back-end.');
  term.writeln('Type some keys and commands to play around.');
  term.writeln('');
  term.prompt();

  term.on('key', function (key, ev) {
    var printable = (
      !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey
    );

    if (ev.keyCode == 13) {
      term.prompt();
    } else if (ev.keyCode == 8) {
     // Do not delete the prompt
      if (term.x > 2) {
        term.write('\b \b');
      }
    } else if (printable) {
      term.write(key);
    }
  });

  term.on('paste', function (data, ev) {
    term.write(data);
  });
}
