var assert = require('chai').assert;
var fs = require("fs");
var vm = require("vm");

let InputProcessor = require('../../build/index.bundled.js');

// Required initialization setup.
global.com = InputProcessor.com; // exports all keyboard-processor namespacing.
global.keyman = {}; // So that keyboard-based checks against the global `keyman` succeed.
                    // 10.0+ dependent keyboards, like khmer_angkor, will otherwise fail to load.

let device = {
  formFactor: 'phone',
  OS: 'ios',
  browser: 'safari'
};

// Initialize supplementary plane string extensions
String.kmwEnableSupplementaryPlane(false);

// Test the KeyboardProcessor interface.
describe('InputProcessor', function() {
  describe('[[constructor]]', function () {
    it('should initialize without errors', function () {
      let core = new InputProcessor(device);
      assert.isNotNull(core);
    });

    it('has expected default values after initialization', function () {
      let core = new InputProcessor(device);

      assert.isOk(core.keyboardProcessor);
      assert.isDefined(core.keyboardProcessor.contextDevice);
      assert.isOk(core.languageProcessor);
      assert.isOk(core.keyboardInterface);
      assert.isUndefined(core.activeKeyboard); // No keyboard should be loaded yet.
      assert.isUndefined(core.activeModel);    // Same for the model.

      // These checks are lifted from the keyboard-processor init checks found in
      // common/web/keyboard-processor/tests/cases/basic-init.js.
      assert.equal('us', core.keyboardProcessor.baseLayout, 'KeyboardProcessor has unexpected base layout')
      assert.isNotNull(global.KeymanWeb, 'KeymanWeb global was not automatically installed');
      assert.equal('default', core.keyboardProcessor.layerId, 'Default layer is not set to "default"');
      assert.isUndefined(core.keyboardProcessor.activeKeyboard, 'Initialized with already-active keyboard');

      // Lifted from languageProcessor.js - the core should not be changing these with its init.
      assert.isUndefined(core.languageProcessor.activeModel);
      assert.isFalse(core.languageProcessor.isActive);
      assert.isTrue(core.languageProcessor.mayPredict);
    });
  });

  describe('efficiency tests', function() {
    let testDistribution = [];
    var keyboard;

    // Easy peasy long context:  use the input processor's full source!
    let coreSourceCode = fs.readFileSync('build/index.bundled.js', 'utf-8');

    // At the time this test block was written...  810485 chars.
    // Let's force it to the same order of magnitude, even if the codebase grows.
    if(coreSourceCode.length > 1000000) {
      coreSourceCode = coreSourceCode.substring(0, 1000000);
    }

    this.beforeAll(function() {
      testDistribution = [];

      for(let c = 'A'.charCodeAt(0); c <= 'Z'.charCodeAt(0); c++) {
        let char = String.fromCharCode(c);

        testDistribution.push({
          keyId: "K_" + char,
          p: 1 / 26
        });
      }

      // Load the keyboard.  We'll need an InputProcessor instance as an intermediary.
      let core = new InputProcessor(device);

      // These two lines will load a keyboard from its file; headless-mode `registerKeyboard` will
      // automatically set the keyboard as active.
      let kbdScript = new vm.Script(fs.readFileSync('../../test/resources/keyboards/test_chirality.js'));
      kbdScript.runInThisContext();

      keyboard = core.activeKeyboard;
    });

    describe('without fat-fingering', function() {
      it('with minimal context (no fat-fingers)', function() {
        this.timeout(32); // ms
        let core = new InputProcessor(device);
        let context = new com.keyman.text.Mock("", 0);

        core.activeKeyboard = keyboard;
        let layout = keyboard.layout(com.keyman.utils.FormFactor.Phone);
        let key = layout.getLayer('default').getKey('K_A');
        let event = key.constructKeyEvent(core.keyboardProcessor, device);

        let behavior = core.processKeyEvent(event, context);
        assert.isNotNull(behavior);
      });

      it('with extremely long context (' + coreSourceCode._kmwLength() + ' chars, no fat-fingers)', function() {
        // Assumes no SMP chars in the source, which is fine.
        let context = new com.keyman.text.Mock(coreSourceCode, coreSourceCode._kmwLength());

        this.timeout(500);                // 500 ms, excluding text import.
                                          // These often run on VMs, so we'll be a bit generous.

        let core = new InputProcessor(device);  // I mean, it IS long context, and time
                                          // thresholding is disabled within Node.

        core.activeKeyboard = keyboard;
        let layout = keyboard.layout(com.keyman.utils.FormFactor.Phone);
        let key = layout.getLayer('default').getKey('K_A');
        let event = key.constructKeyEvent(core.keyboardProcessor, device);

        let behavior = core.processKeyEvent(event, context);
        assert.isNotNull(behavior);
      });
    });

    describe('with fat-fingering', function() {
      it('with minimal context (with fat-fingers)', function() {
        this.timeout(32); // ms
        let core = new InputProcessor(device);
        let context = new com.keyman.text.Mock("", 0);

        core.activeKeyboard = keyboard;
        let layout = keyboard.layout(com.keyman.utils.FormFactor.Phone);
        let key = layout.getLayer('default').getKey('K_A');
        key.keyDistribution = testDistribution;
        let event = key.constructKeyEvent(core.keyboardProcessor, device);

        let behavior = core.processKeyEvent(event, context);
        assert.isNotNull(behavior);
      });

      it('with extremely long context (' + coreSourceCode._kmwLength() + ' chars, with fat-fingers)', function() {
        // Assumes no SMP chars in the source, which is fine.
        let context = new com.keyman.text.Mock(coreSourceCode, coreSourceCode._kmwLength());

        this.timeout(500);                // 500 ms, excluding text import.
                                          // These often run on VMs, so we'll be a bit generous.
                                          //
                                          // Keep at the same 'order of magnitude' as the
                                          // 'without fat-fingers' test.

        let core = new InputProcessor(device);  // It IS long context, and time
                                          // thresholding is disabled within Node.

        core.activeKeyboard = keyboard;
        let layout = keyboard.layout(com.keyman.utils.FormFactor.Phone);
        let key = layout.getLayer('default').getKey('K_A');
        key.keyDistribution = testDistribution;
        let event = key.constructKeyEvent(core.keyboardProcessor, device);

        let behavior = core.processKeyEvent(event, context);
        assert.isNotNull(behavior);
      });
    });
  });
});