const wx2my = require('../wx2my');
const Behavior = require('../Behavior');
var CHAR_TILDE = 126;
var CODE_FNC1 = 102;
var SET_STARTA = 103;
var SET_STARTB = 104;
var SET_STARTC = 105;
var SET_SHIFT = 98;
var SET_CODEA = 101;
var SET_CODEB = 100;
var SET_STOP = 106;
var REPLACE_CODES = {
  CHAR_TILDE: CODE_FNC1 //~ corresponds to FNC1 in GS1-128 standard

};
var CODESET = {
  ANY: 1,
  AB: 2,
  A: 3,
  B: 4,
  C: 5
};

function getBytes(str) {
  var bytes = [];

  for (var i = 0; i < str.length; i++) {
    bytes.push(str.charCodeAt(i));
  }

  return bytes;
}

exports.code128 = function (ctx, text, width, height) {
  width = parseInt(width);
  height = parseInt(height);
  var codes = stringToCode128(text);
  var g = new Graphics(ctx, width, height);
  var barWeight = g.area.width / ((codes.length - 3) * 11 + 35);
  var x = g.area.left;
  var y = g.area.top;

  for (var i = 0; i < codes.length; i++) {
    var c = codes[i]; //two bars at a time: 1 black and 1 white

    for (var bar = 0; bar < 8; bar += 2) {
      var barW = PATTERNS[c][bar] * barWeight; // var barH = height - y - this.border;

      var barH = height - y;
      var spcW = PATTERNS[c][bar + 1] * barWeight; //no need to draw if 0 width

      if (barW > 0) {
        g.fillFgRect(x, y, barW, barH);
      }

      x += barW + spcW;
    }
  }

  ctx.draw();
};

function stringToCode128(text) {
  var barc = {
    currcs: CODESET.C
  };
  var bytes = getBytes(text); //decide starting codeset

  var index = bytes[0] == CHAR_TILDE ? 1 : 0;
  var csa1 = bytes.length > 0 ? codeSetAllowedFor(bytes[index++]) : CODESET.AB;
  var csa2 = bytes.length > 0 ? codeSetAllowedFor(bytes[index++]) : CODESET.AB;
  barc.currcs = getBestStartSet(csa1, csa2);
  barc.currcs = perhapsCodeC(bytes, barc.currcs); //if no codeset changes this will end up with bytes.length+3
  //start, checksum and stop

  var codes = new Array();

  switch (barc.currcs) {
    case CODESET.A:
      codes.push(SET_STARTA);
      break;

    case CODESET.B:
      codes.push(SET_STARTB);
      break;

    default:
      codes.push(SET_STARTC);
      break;
  }

  for (var i = 0; i < bytes.length; i++) {
    var b1 = bytes[i]; //get the first of a pair
    //should we translate/replace

    if (b1 in REPLACE_CODES) {
      codes.push(REPLACE_CODES[b1]);
      i++; //jump to next

      b1 = bytes[i];
    } //get the next in the pair if possible


    var b2 = bytes.length > i + 1 ? bytes[i + 1] : -1;
    codes = codes.concat(codesForChar(b1, b2, barc.currcs)); //code C takes 2 chars each time

    if (barc.currcs == CODESET.C) i++;
  } //calculate checksum according to Code 128 standards


  var checksum = codes[0];

  for (var weight = 1; weight < codes.length; weight++) {
    checksum += weight * codes[weight];
  }

  codes.push(checksum % 103);
  codes.push(SET_STOP); //encoding should now be complete

  return codes;

  function getBestStartSet(csa1, csa2) {
    //tries to figure out the best codeset
    //to start with to get the most compact code
    var vote = 0;
    vote += csa1 == CODESET.A ? 1 : 0;
    vote += csa1 == CODESET.B ? -1 : 0;
    vote += csa2 == CODESET.A ? 1 : 0;
    vote += csa2 == CODESET.B ? -1 : 0; //tie goes to B due to my own predudices

    return vote > 0 ? CODESET.A : CODESET.B;
  }

  function perhapsCodeC(bytes, codeset) {
    for (var i = 0; i < bytes.length; i++) {
      var b = bytes[i];
      if ((b < 48 || b > 57) && b != CHAR_TILDE) return codeset;
    }

    return CODESET.C;
  } //chr1 is current byte
  //chr2 is the next byte to process. looks ahead.


  function codesForChar(chr1, chr2, currcs) {
    var result = [];
    var shifter = -1;

    if (charCompatible(chr1, currcs)) {
      if (currcs == CODESET.C) {
        if (chr2 == -1) {
          shifter = SET_CODEB;
          currcs = CODESET.B;
        } else if (chr2 != -1 && !charCompatible(chr2, currcs)) {
          //need to check ahead as well
          if (charCompatible(chr2, CODESET.A)) {
            shifter = SET_CODEA;
            currcs = CODESET.A;
          } else {
            shifter = SET_CODEB;
            currcs = CODESET.B;
          }
        }
      }
    } else {
      //if there is a next char AND that next char is also not compatible
      if (chr2 != -1 && !charCompatible(chr2, currcs)) {
        //need to switch code sets
        switch (currcs) {
          case CODESET.A:
            shifter = SET_CODEB;
            currcs = CODESET.B;
            break;

          case CODESET.B:
            shifter = SET_CODEA;
            currcs = CODESET.A;
            break;
        }
      } else {
        //no need to shift code sets, a temporary SHIFT will suffice
        shifter = SET_SHIFT;
      }
    } //ok some type of shift is nessecary


    if (shifter != -1) {
      result.push(shifter);
      result.push(codeValue(chr2));
    } else {
      if (currcs == CODESET.C) {
        //include next as well
        result.push(codeValue(chr1, chr2));
      } else {
        result.push(codeValue(chr1));
      }
    }

    barc.currcs = currcs;
    return result;
  }
} //reduce the ascii code to fit into the Code128 char table


function codeValue(chr1, chr2) {
  if (typeof chr2 == "undefined") {
    return chr1 >= 32 ? chr1 - 32 : chr1 + 64;
  } else {
    return parseInt(String.fromCharCode(chr1) + String.fromCharCode(chr2));
  }
}

function charCompatible(chr, codeset) {
  var csa = codeSetAllowedFor(chr);
  if (csa == CODESET.ANY) return true; //if we need to change from current

  if (csa == CODESET.AB) return true;
  if (csa == CODESET.A && codeset == CODESET.A) return true;
  if (csa == CODESET.B && codeset == CODESET.B) return true;
  return false;
}

function codeSetAllowedFor(chr) {
  if (chr >= 48 && chr <= 57) {
    //0-9
    return CODESET.ANY;
  } else if (chr >= 32 && chr <= 95) {
    //0-9 A-Z
    return CODESET.AB;
  } else {
    //if non printable
    return chr < 32 ? CODESET.A : CODESET.B;
  }
}

var Graphics = function (ctx, width, height) {
  this.width = width;
  this.height = height;
  this.quiet = Math.round(this.width / 40);
  this.border_size = 0;
  this.padding_width = 0;
  this.area = {
    width: width - this.padding_width * 2 - this.quiet * 2,
    height: height - this.border_size * 2,
    top: this.border_size - 4,
    left: this.padding_width + this.quiet
  };
  this.ctx = ctx;
  this.fg = "#000000";
  this.bg = "#ffffff"; // fill background

  this.fillBgRect(0, 0, width, height); // fill center to create border

  this.fillBgRect(0, this.border_size, width, height - this.border_size * 2);
}; //use native color


Graphics.prototype._fillRect = function (x, y, width, height, color) {
  this.ctx.setFillStyle(color);
  this.ctx.fillRect(x, y, width, height);
};

Graphics.prototype.fillFgRect = function (x, y, width, height) {
  this._fillRect(x, y, width, height, this.fg);
};

Graphics.prototype.fillBgRect = function (x, y, width, height) {
  this._fillRect(x, y, width, height, this.bg);
};

var PATTERNS = [[2, 1, 2, 2, 2, 2, 0, 0], // 0
[2, 2, 2, 1, 2, 2, 0, 0], // 1
[2, 2, 2, 2, 2, 1, 0, 0], // 2
[1, 2, 1, 2, 2, 3, 0, 0], // 3
[1, 2, 1, 3, 2, 2, 0, 0], // 4
[1, 3, 1, 2, 2, 2, 0, 0], // 5
[1, 2, 2, 2, 1, 3, 0, 0], // 6
[1, 2, 2, 3, 1, 2, 0, 0], // 7
[1, 3, 2, 2, 1, 2, 0, 0], // 8
[2, 2, 1, 2, 1, 3, 0, 0], // 9
[2, 2, 1, 3, 1, 2, 0, 0], // 10
[2, 3, 1, 2, 1, 2, 0, 0], // 11
[1, 1, 2, 2, 3, 2, 0, 0], // 12
[1, 2, 2, 1, 3, 2, 0, 0], // 13
[1, 2, 2, 2, 3, 1, 0, 0], // 14
[1, 1, 3, 2, 2, 2, 0, 0], // 15
[1, 2, 3, 1, 2, 2, 0, 0], // 16
[1, 2, 3, 2, 2, 1, 0, 0], // 17
[2, 2, 3, 2, 1, 1, 0, 0], // 18
[2, 2, 1, 1, 3, 2, 0, 0], // 19
[2, 2, 1, 2, 3, 1, 0, 0], // 20
[2, 1, 3, 2, 1, 2, 0, 0], // 21
[2, 2, 3, 1, 1, 2, 0, 0], // 22
[3, 1, 2, 1, 3, 1, 0, 0], // 23
[3, 1, 1, 2, 2, 2, 0, 0], // 24
[3, 2, 1, 1, 2, 2, 0, 0], // 25
[3, 2, 1, 2, 2, 1, 0, 0], // 26
[3, 1, 2, 2, 1, 2, 0, 0], // 27
[3, 2, 2, 1, 1, 2, 0, 0], // 28
[3, 2, 2, 2, 1, 1, 0, 0], // 29
[2, 1, 2, 1, 2, 3, 0, 0], // 30
[2, 1, 2, 3, 2, 1, 0, 0], // 31
[2, 3, 2, 1, 2, 1, 0, 0], // 32
[1, 1, 1, 3, 2, 3, 0, 0], // 33
[1, 3, 1, 1, 2, 3, 0, 0], // 34
[1, 3, 1, 3, 2, 1, 0, 0], // 35
[1, 1, 2, 3, 1, 3, 0, 0], // 36
[1, 3, 2, 1, 1, 3, 0, 0], // 37
[1, 3, 2, 3, 1, 1, 0, 0], // 38
[2, 1, 1, 3, 1, 3, 0, 0], // 39
[2, 3, 1, 1, 1, 3, 0, 0], // 40
[2, 3, 1, 3, 1, 1, 0, 0], // 41
[1, 1, 2, 1, 3, 3, 0, 0], // 42
[1, 1, 2, 3, 3, 1, 0, 0], // 43
[1, 3, 2, 1, 3, 1, 0, 0], // 44
[1, 1, 3, 1, 2, 3, 0, 0], // 45
[1, 1, 3, 3, 2, 1, 0, 0], // 46
[1, 3, 3, 1, 2, 1, 0, 0], // 47
[3, 1, 3, 1, 2, 1, 0, 0], // 48
[2, 1, 1, 3, 3, 1, 0, 0], // 49
[2, 3, 1, 1, 3, 1, 0, 0], // 50
[2, 1, 3, 1, 1, 3, 0, 0], // 51
[2, 1, 3, 3, 1, 1, 0, 0], // 52
[2, 1, 3, 1, 3, 1, 0, 0], // 53
[3, 1, 1, 1, 2, 3, 0, 0], // 54
[3, 1, 1, 3, 2, 1, 0, 0], // 55
[3, 3, 1, 1, 2, 1, 0, 0], // 56
[3, 1, 2, 1, 1, 3, 0, 0], // 57
[3, 1, 2, 3, 1, 1, 0, 0], // 58
[3, 3, 2, 1, 1, 1, 0, 0], // 59
[3, 1, 4, 1, 1, 1, 0, 0], // 60
[2, 2, 1, 4, 1, 1, 0, 0], // 61
[4, 3, 1, 1, 1, 1, 0, 0], // 62
[1, 1, 1, 2, 2, 4, 0, 0], // 63
[1, 1, 1, 4, 2, 2, 0, 0], // 64
[1, 2, 1, 1, 2, 4, 0, 0], // 65
[1, 2, 1, 4, 2, 1, 0, 0], // 66
[1, 4, 1, 1, 2, 2, 0, 0], // 67
[1, 4, 1, 2, 2, 1, 0, 0], // 68
[1, 1, 2, 2, 1, 4, 0, 0], // 69
[1, 1, 2, 4, 1, 2, 0, 0], // 70
[1, 2, 2, 1, 1, 4, 0, 0], // 71
[1, 2, 2, 4, 1, 1, 0, 0], // 72
[1, 4, 2, 1, 1, 2, 0, 0], // 73
[1, 4, 2, 2, 1, 1, 0, 0], // 74
[2, 4, 1, 2, 1, 1, 0, 0], // 75
[2, 2, 1, 1, 1, 4, 0, 0], // 76
[4, 1, 3, 1, 1, 1, 0, 0], // 77
[2, 4, 1, 1, 1, 2, 0, 0], // 78
[1, 3, 4, 1, 1, 1, 0, 0], // 79
[1, 1, 1, 2, 4, 2, 0, 0], // 80
[1, 2, 1, 1, 4, 2, 0, 0], // 81
[1, 2, 1, 2, 4, 1, 0, 0], // 82
[1, 1, 4, 2, 1, 2, 0, 0], // 83
[1, 2, 4, 1, 1, 2, 0, 0], // 84
[1, 2, 4, 2, 1, 1, 0, 0], // 85
[4, 1, 1, 2, 1, 2, 0, 0], // 86
[4, 2, 1, 1, 1, 2, 0, 0], // 87
[4, 2, 1, 2, 1, 1, 0, 0], // 88
[2, 1, 2, 1, 4, 1, 0, 0], // 89
[2, 1, 4, 1, 2, 1, 0, 0], // 90
[4, 1, 2, 1, 2, 1, 0, 0], // 91
[1, 1, 1, 1, 4, 3, 0, 0], // 92
[1, 1, 1, 3, 4, 1, 0, 0], // 93
[1, 3, 1, 1, 4, 1, 0, 0], // 94
[1, 1, 4, 1, 1, 3, 0, 0], // 95
[1, 1, 4, 3, 1, 1, 0, 0], // 96
[4, 1, 1, 1, 1, 3, 0, 0], // 97
[4, 1, 1, 3, 1, 1, 0, 0], // 98
[1, 1, 3, 1, 4, 1, 0, 0], // 99
[1, 1, 4, 1, 3, 1, 0, 0], // 100
[3, 1, 1, 1, 4, 1, 0, 0], // 101
[4, 1, 1, 1, 3, 1, 0, 0], // 102
[2, 1, 1, 4, 1, 2, 0, 0], // 103
[2, 1, 1, 2, 1, 4, 0, 0], // 104
[2, 1, 1, 2, 3, 2, 0, 0], // 105
[2, 3, 3, 1, 1, 1, 2, 0] // 106
];