/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */
/* Bug 653427 */

var tempScope = {};
ChromeUtils.import("resource://gre/modules/NetUtil.jsm", tempScope);
ChromeUtils.import("resource://gre/modules/FileUtils.jsm", tempScope);
var NetUtil = tempScope.NetUtil;
var FileUtils = tempScope.FileUtils;

// only finish() when correct number of tests are done
const expected = 9;
var count = 0;
function done() {
  if (++count == expected) {
    cleanup();
    finish();
  }
}

var gFile;

var oldPrompt = Services.prompt;
var promptButton = -1;

function test() {
  waitForExplicitFinish();

  gFile = createTempFile("fileForBug653427.tmp");
  writeFile(gFile, "text", testUnsaved.call(this));

  Services.prompt = {
    confirmEx: function() {
      return promptButton;
    }
  };

  testNew();
  testSavedFile();

  gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
  content.location = "data:text/html,<p>test scratchpad save file prompt on closing";
}

function testNew() {
  openScratchpad(function(win) {
    win.Scratchpad.close(function() {
      ok(win.closed, "new scratchpad window should close without prompting");
      done();
    });
  }, {noFocus: true});
}

function testSavedFile() {
  openScratchpad(function(win) {
    win.Scratchpad.filename = "test.js";
    win.Scratchpad.editor.dirty = false;
    win.Scratchpad.close(function() {
      ok(win.closed, "scratchpad from file with no changes should close");
      done();
    });
  }, {noFocus: true});
}

function testUnsaved() {
  function setFilename(aScratchpad, aFile) {
    aScratchpad.setFilename(aFile);
  }

  testUnsavedFileCancel(setFilename);
  testUnsavedFileSave(setFilename);
  testUnsavedFileDontSave(setFilename);
  testCancelAfterLoad();

  function mockSaveFile(aScratchpad) {
    const SaveFileStub = function(aCallback) {
      /*
       * An argument for aCallback must pass Components.isSuccessCode
       *
       * A version of isSuccessCode in JavaScript:
       *  function isSuccessCode(returnCode) {
       *    return (returnCode & 0x80000000) == 0;
       *  }
       */
      aCallback(1);
    };

    aScratchpad.saveFile = SaveFileStub;
  }

  // Run these tests again but this time without setting a filename to
  // test that Scratchpad always asks for confirmation on dirty editor.
  testUnsavedFileCancel(mockSaveFile);
  testUnsavedFileSave(mockSaveFile);
  testUnsavedFileDontSave();
}

function testUnsavedFileCancel(aCallback = function() {}) {
  openScratchpad(function(win) {
    aCallback(win.Scratchpad, "test.js");
    win.Scratchpad.editor.dirty = true;

    promptButton = win.BUTTON_POSITION_CANCEL;

    win.Scratchpad.close(function() {
      ok(!win.closed, "cancelling dialog shouldn't close scratchpad");
      win.close();
      done();
    });
  }, {noFocus: true});
}

// Test a regression where our confirmation dialog wasn't appearing
// after openFile calls. See bug 801982.
function testCancelAfterLoad() {
  openScratchpad(function(win) {
    win.Scratchpad.setRecentFile(gFile);
    win.Scratchpad.openFile(0);
    win.Scratchpad.editor.dirty = true;
    promptButton = win.BUTTON_POSITION_CANCEL;

    const EventStub = {
      called: false,
      preventDefault: function() {
        EventStub.called = true;
      }
    };

    win.Scratchpad.onClose(EventStub, function() {
      ok(!win.closed, "cancelling dialog shouldn't close scratchpad");
      ok(EventStub.called, "aEvent.preventDefault was called");

      win.Scratchpad.editor.dirty = false;
      win.close();
      done();
    });
  }, {noFocus: true});
}

function testUnsavedFileSave(aCallback = function() {}) {
  openScratchpad(function(win) {
    win.Scratchpad.importFromFile(gFile, true, function(status, content) {
      aCallback(win.Scratchpad, gFile.path);

      const text = "new text";
      win.Scratchpad.setText(text);

      promptButton = win.BUTTON_POSITION_SAVE;

      win.Scratchpad.close(function() {
        ok(win.closed, 'pressing "Save" in dialog should close scratchpad');
        readFile(gFile, function(savedContent) {
          is(savedContent, text, 'prompted "Save" worked when closing scratchpad');
          done();
        });
      });
    });
  }, {noFocus: true});
}

function testUnsavedFileDontSave(aCallback = function() {}) {
  openScratchpad(function(win) {
    aCallback(win.Scratchpad, gFile.path);
    win.Scratchpad.editor.dirty = true;

    promptButton = win.BUTTON_POSITION_DONT_SAVE;

    win.Scratchpad.close(function() {
      ok(win.closed, 'pressing "Don\'t Save" in dialog should close scratchpad');
      done();
    });
  }, {noFocus: true});
}

function cleanup() {
  Services.prompt = oldPrompt;
  gFile.remove(false);
  gFile = null;
}

function createTempFile(name) {
  const file = FileUtils.getFile("TmpD", [name]);
  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
  file.QueryInterface(Ci.nsIFile);
  return file;
}

function writeFile(file, content, callback) {
  const fout = Cc["@mozilla.org/network/file-output-stream;1"]
             .createInstance(Ci.nsIFileOutputStream);
  fout.init(file.QueryInterface(Ci.nsIFile), 0x02 | 0x08 | 0x20,
            0o644, fout.DEFER_OPEN);

  const converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                  .createInstance(Ci.nsIScriptableUnicodeConverter);
  converter.charset = "UTF-8";
  const fileContentStream = converter.convertToInputStream(content);

  NetUtil.asyncCopy(fileContentStream, fout, callback);
}

function readFile(file, callback) {
  const channel = NetUtil.newChannel({
    uri: NetUtil.newURI(file),
    loadUsingSystemPrincipal: true});
  channel.contentType = "application/javascript";

  NetUtil.asyncFetch(channel, function(inputStream, status) {
    ok(Components.isSuccessCode(status),
       "file was read successfully");

    const content = NetUtil.readInputStreamToString(inputStream,
                                                  inputStream.available());
    callback(content);
  });
}
