'use strict';

var HEADER = [66, 77, 70];

var parseBmfontBinary = function readBMFontBinary(buf) {
  if (buf.length < 6)
    throw new Error('invalid buffer length for BMFont')

  var header = HEADER.every(function(byte, i) {
    return buf.readUInt8(i) === byte
  });

  if (!header)
    throw new Error('BMFont missing BMF byte header')

  var i = 3;
  var vers = buf.readUInt8(i++);
  if (vers > 3)
    throw new Error('Only supports BMFont Binary v3 (BMFont App v1.10)')
  
  var target = { kernings: [], chars: [] };
  for (var b=0; b<5; b++)
    i += readBlock(target, buf, i);
  return target
};

function readBlock(target, buf, i) {
  if (i > buf.length-1)
    return 0

  var blockID = buf.readUInt8(i++);
  var blockSize = buf.readInt32LE(i);
  i += 4;

  switch(blockID) {
    case 1: 
      target.info = readInfo(buf, i);
      break
    case 2:
      target.common = readCommon(buf, i);
      break
    case 3:
      target.pages = readPages(buf, i, blockSize);
      break
    case 4:
      target.chars = readChars(buf, i, blockSize);
      break
    case 5:
      target.kernings = readKernings(buf, i, blockSize);
      break
  }
  return 5 + blockSize
}

function readInfo(buf, i) {
  var info = {};
  info.size = buf.readInt16LE(i);

  var bitField = buf.readUInt8(i+2);
  info.smooth = (bitField >> 7) & 1;
  info.unicode = (bitField >> 6) & 1;
  info.italic = (bitField >> 5) & 1;
  info.bold = (bitField >> 4) & 1;
  
  //fixedHeight is only mentioned in binary spec 
  if ((bitField >> 3) & 1)
    info.fixedHeight = 1;
  
  info.charset = buf.readUInt8(i+3) || '';
  info.stretchH = buf.readUInt16LE(i+4);
  info.aa = buf.readUInt8(i+6);
  info.padding = [
    buf.readInt8(i+7),
    buf.readInt8(i+8),
    buf.readInt8(i+9),
    buf.readInt8(i+10)
  ];
  info.spacing = [
    buf.readInt8(i+11),
    buf.readInt8(i+12)
  ];
  info.outline = buf.readUInt8(i+13);
  info.face = readStringNT(buf, i+14);
  return info
}

function readCommon(buf, i) {
  var common = {};
  common.lineHeight = buf.readUInt16LE(i);
  common.base = buf.readUInt16LE(i+2);
  common.scaleW = buf.readUInt16LE(i+4);
  common.scaleH = buf.readUInt16LE(i+6);
  common.pages = buf.readUInt16LE(i+8);
  buf.readUInt8(i+10);
  common.packed = 0;
  common.alphaChnl = buf.readUInt8(i+11);
  common.redChnl = buf.readUInt8(i+12);
  common.greenChnl = buf.readUInt8(i+13);
  common.blueChnl = buf.readUInt8(i+14);
  return common
}

function readPages(buf, i, size) {
  var pages = [];
  var text = readNameNT(buf, i);
  var len = text.length+1;
  var count = size / len;
  for (var c=0; c<count; c++) {
    pages[c] = buf.slice(i, i+text.length).toString('utf8');
    i += len;
  }
  return pages
}

function readChars(buf, i, blockSize) {
  var chars = [];

  var count = blockSize / 20;
  for (var c=0; c<count; c++) {
    var char = {};
    var off = c*20;
    char.id = buf.readUInt32LE(i + 0 + off);
    char.x = buf.readUInt16LE(i + 4 + off);
    char.y = buf.readUInt16LE(i + 6 + off);
    char.width = buf.readUInt16LE(i + 8 + off);
    char.height = buf.readUInt16LE(i + 10 + off);
    char.xoffset = buf.readInt16LE(i + 12 + off);
    char.yoffset = buf.readInt16LE(i + 14 + off);
    char.xadvance = buf.readInt16LE(i + 16 + off);
    char.page = buf.readUInt8(i + 18 + off);
    char.chnl = buf.readUInt8(i + 19 + off);
    chars[c] = char;
  }
  return chars
}

function readKernings(buf, i, blockSize) {
  var kernings = [];
  var count = blockSize / 10;
  for (var c=0; c<count; c++) {
    var kern = {};
    var off = c*10;
    kern.first = buf.readUInt32LE(i + 0 + off);
    kern.second = buf.readUInt32LE(i + 4 + off);
    kern.amount = buf.readInt16LE(i + 8 + off);
    kernings[c] = kern;
  }
  return kernings
}

function readNameNT(buf, offset) {
  var pos=offset;
  for (; pos<buf.length; pos++) {
    if (buf[pos] === 0x00) 
      break
  }
  return buf.slice(offset, pos)
}

function readStringNT(buf, offset) {
  return readNameNT(buf, offset).toString('utf8')
}

exports.parseBmfontBinary = parseBmfontBinary;
