text: var Font = require('../font');
  var Segment = require('./segment');
  var CharacterDescriptor = require('./characterDescriptor');
  var charset = require('../../charset/roman9');
  
  var trueType = function(document, id, fontBuffer, family) {
    // Inherit properties
    Font.call(this, document, id, fontBuffer, family);
    var self = this;
  
    this.fontDescriptorSize = 72;
    this.headerFormat = 15;
    this.fontType = 2;
  
    // Style
    if (this.font['OS/2']) {
      var fsSelection = this.font['OS/2'].fsSelection;
  
      // Posture
      if (fsSelection.regular) {
        this.style.posture = 0; // Upright
      } else if (fsSelection.italic) {
        this.style.posture = 1; // Italic
      } else if (fsSelection.oblique) {
        this.style.posture = 2; // Alternate Italic
      } else {
        this.style.posture = 0; // Upright
      }
  
      // Width
      switch(this.font['OS/2'].usWidthClass) {
        // Ultra-condensed
        case 1:
          this.style.width = 4;
          break;
        // Extra-condensed
        case 2:
          this.style.width = 3;
          break;
        // Condensed
        case 3:
          this.style.width = 2;
          break;
        // Semi-condensed
        case 4:
          this.style.width = 1;
          break;
        // Medium (normal)
        case 5:
          this.style.width = 0;
          break;
        // Semi-expanded
        case 6:
        // Expanded
        case 7:
          this.style.width = 6;
          break;
        // Extra-expanded
        case 8:
        // Ultra-expanded
        case 9:
          this.style.width = 7;
          break;
      }
  
      // Structure
      if (fsSelection.outlined) {
        this.style.structure = 1; // Outline
      } else {
        this.style.structure = 0; // Solid
      }
  
    } else {
      this.style = {
        posture: 0,
        width: 0,
        structure: 0
      };
    }
  
    this.baselinePosition = 0;
    this.cellWidth = this.font.head.xMax - this.font.head.xMin;
    this.cellHeight = this.font.head.yMax - this.font.head.yMin;
    this.orientation = 0;
  
    //  Spacing
    // 0 (Fixed-Pitch) if the appropriate Monospaced flag is set in the OS/2.panose classification fields
    // 1 (Proportionally-Spaced) otherwise
    this.spacing = 1;
  
    if (this.font['OS/2'] && this.font['OS/2'].panose[3] === 9) {
      this.spacing = 0;
    }
  
    // Symbol Set Type
    // 2 (Bound 8-bit) usually
    // other value as required
    if (this.font.PCLT) {
      this.symbolSet = this.font.PCLT.symbolSet;
    } else {
      this.symbolSet = 277;
    }
  
    // Pitch
    // PCLT.Pitch if PCLT table present;
    // advance width for space character (codePoint 0x20) otherwise.
    // this.pitch
    if (this.font.PCLT) {
      this.pitch = this.font.PCLT.pitch
    } else {
      this.pitch = this.font.glyphForCodePoint(0x20).advanceWidth;
    }
  
    this.height = 0;
  
    //  x-Height
    // PCLT.xHeight if PCLT table present
    // OS/2.sxHeight otherwise
    if (this.font.PCLT) {
      this.xHeight = this.font.PCLT.xHeight;
    } else if (this.font['OS/2'] && this.font['OS/2'].xHeight) {
      this.xHeight = this.font['OS/2'].xHeight;
    } else {
      // calulate x height
      var xBbox = this.font.glyphForCodePoint(0x78).bbox;
      this.xHeight = xBbox.maxY - xBbox.minY;
    }
  
    // Width Type
    // PCLT.WidthType if PCLT table present
    // OS/2.usWidthClass otherwise (modified as there is not a 1:1 mapping)
    if (this.font.PCLT) {
      this.widthType = this.font.PCLT.widthType;
    } else if (this.font['OS/2']) {
      switch(this.font['OS/2'].usWidthClass) {
        // Ultra-condensed
        case 1:
          this.widthType = -5;
          break;
        // Extra-condensed
        case 2:
          this.widthType = -4;
          break;
        // Condensed
        case 3:
        // Semi-condensed
        case 4:
          this.widthType = -2;
          break;
        // Medium (normal)
        case 5:
          this.widthType = 0;
          break;
        // Semi-expanded
        case 6:
        // Expanded
        case 7:
          this.widthType = 2;
          break;
        // Extra-expanded
        case 8:
        // Ultra-expanded
        case 9:
          this.widthType = 3;
          break;
      }
    } else {
      this.widthType = 0;
    }
  
    // Stroke Weight
    // PCLT.StrokeWeight if PCLT table present
    // OS/2.usWeightClass otherwise (modified as there is not  a 1:1 mapping)
    if (this.font.PCLT) {
      this.strokeWeight = this.font.PCLT.strokeWeight;
    } else if (this.font['OS/2']) {
      this.strokeWeight = Math.round(this.font['OS/2'].usWeightClass * (14 / 1000) - 7)
    } else {
      this.strokeWeight = 0;
    }
  
    // Serif Style
    // PCLT.SerifStyle if PCLT table present
    // 0 as default(?) otherwise
    // this.serifStyle
    if (this.font.PCLT) {
      this.serifStyle = this.font.PCLT.serifStyle;
    } else if (this.font['OS/2']) {
      // 64 Sans Serif
      // 128 Serif
      this.serifStyle = this.font['OS/2'].sFamilyClass === 8 ? 64 : 128;
    } else {
      this.serifStyle = 64;
    }
  
    this.quality = 2;
    this.placement = 0;
    this.underlinePosition = 0;
    this.underlineThickness = 0;
  
    // Text Height
    // hhea.yAscender – hhea.yDescender + hhea.yLineGap
    this.textHeight = this.font.hhea.ascent - this.font.hhea.descent + this.font.hhea.lineGap;
  
    // Text Width
    // OS/2.xAvgCharWidth
    if (this.font['OS/2']) {
      this.textWidth = this.font['OS/2'].xAvgCharWidth;
    } else {
      // compute char width average
      var sum = 0;
      var count = 0;
  
      this.font.characterSet.forEach(function(codePoint) {
        if (codePoint === 0xffff || codePoint < self.firstCode || codePoint > self.lastCode) {
          return;
        }
        sum += self.font.glyphForCodePoint(0x20).advanceWidth;
        count++;
      });
  
      this.textWidth = Math.round(sum / count);
    }
  
    // First Code
    // 0x0020 for Symbol Set Type 2 usually(?)
    // other value for specific font
    this.firstCode = 0x0020;
  
    // Last Code
    // 0x00ff for Symbol Set Type 2 usually(?)
    // other value for specific font
    this.lastCode = 0x00ff;
  
    this.pitchExtended = 0;
    this.heightExtended = 0;
  
    // Cap Height
    // PCLT.CapHeight if PCLT table present
    // OS/2.sCapHeight otherwise
    // this.capHeight
    if (this.font.PCLT) {
      this.capHeight = this.font.PCLT.capHeight;
    } else if (this.font['OS/2'] && this.font['OS/2'].capHeight) {
      this.capHeight = this.font['OS/2'].capHeight;
    } else {
      // calulate H height
      var HBbox = this.font.glyphForCodePoint(0x48).bbox;
      this.capHeight = HBbox.maxY - HBbox.minY;
    }
  
    // Font Number
    // PCLT.FontNumber if PCLT table present
    // 0 otherwise
    if (this.font.PCLT) {
      this.fontNumber = this.font.PCLT.fontNumber;
    } else {
      //this.fontNumber = 0;
    }
  
    // Font Name
    // PCLT.Typeface if PCLT table present
    // name.ID4 (for language = 0x0409) otherwise; value is usually (Big-Endian) Unicode, so must be converted to ANSI
    if (this.font.PCLT) {
      this.fontName = this.font.PCLT.typeface.toString('ascii');
    } else {
      this.fontName = this.font.fullName.toString('ascii');
    }
  
    // Scale Factor
    // head.unitsPerEm
    this.scaleFactor = this.font.head.unitsPerEm;
  
    // Master Underline Position
    // -(head.unitsPerEm * 20%)
    this.masterUnderlinePosition = -(this.font.head.unitsPerEm * 0.2);
  
    // Master Underline Thickness
    // (head.unitsPerEm * 5%)
    this.masterUnderlineThickness = this.font.head.unitsPerEm * 0.05;
  
    this.fontScalingTechnology = 1;
    this.variety = 0;
  
    if (this.font['OS/2']) {
      this.PanoseSegment = (new Segment(this)).Panose();
    } else {
      this.PanoseSegment = null;
    }
    this.GlobalTrueTypeSegment = (new Segment(this)).GlobalTrueType();
    this.NullSegment = (new Segment(this)).Null();
  };
  
  // Inherit Readable Stream prototype
  trueType.prototype = Object.create(Font.prototype);
  trueType.prototype.constructor = Font;
  
  trueType.prototype.getHeader = function() {
    var style = this.computeStyle();
    var typeface = this.computeTypeface();
    var fontNumber = this.computeFontNumber();
  
    var header = Buffer.alloc(72);
    header.writeUInt16BE(this.fontDescriptorSize, 0);
    header.writeUInt8(this.headerFormat, 2);
    header.writeUInt8(this.fontType, 3);
    // style MSB
    style.copy(header, 4, 0, 1);
    header.writeUInt16BE(this.baselinePosition, 6);
    header.writeUInt16BE(this.cellWidth, 8);
    header.writeUInt16BE(this.cellHeight, 10);
    header.writeUInt8(this.orientation, 12);
    header.writeUInt8(this.spacing, 13);
    header.writeUInt16BE(this.symbolSet, 14);
    header.writeUInt16BE(this.pitch, 16);
    header.writeUInt16BE(this.height, 18);
    header.writeUInt16BE(this.xHeight, 20);
    header.writeInt8(this.widthType, 22);
    // style LSB
    style.copy(header, 23, 1, 2);
    header.writeInt8(this.strokeWeight, 24);
    // typeface LSB
    typeface.copy(header, 25, 1, 2);
    // typeface MSB
    typeface.copy(header, 26, 0, 1);
    header.writeUInt8(this.serifStyle, 27);
    header.writeUInt8(this.quality, 28);
    header.writeInt8(this.placement, 29);
    header.writeInt8(this.underlinePosition , 30);
    header.writeInt8(this.underlineThickness, 31);
    header.writeUInt16BE(this.textHeight, 32);
    header.writeUInt16BE(this.textWidth, 34);
    header.writeUInt16BE(this.firstCode, 36);
    header.writeUInt16BE(this.lastCode, 38);
    header.writeUInt8(this.pitchExtended, 40);
    header.writeUInt8(this.heightExtended, 41);
    header.writeUInt16BE(this.capHeight, 42);
    fontNumber.copy(header, 44, 0, 4);
    header.write(this.fontName, 48, 'ascii');
    header.writeUInt16BE(this.scaleFactor, 64);
    header.writeInt16BE(this.masterUnderlinePosition, 66);
    header.writeInt16BE(this.masterUnderlineThickness, 68);
    header.writeUInt8(this.fontScalingTechnology, 70);
    header.writeUInt8(this.variety, 71);
    
    if (this.PanoseSegment) {
      header = Buffer.concat([header, this.PanoseSegment.toBuffer(), this.GlobalTrueTypeSegment.toBuffer(), this.NullSegment.toBuffer(), Buffer.alloc(2)]);
    } else {
      header = Buffer.concat([header, this.GlobalTrueTypeSegment.toBuffer(), this.NullSegment.toBuffer(), Buffer.alloc(2)]);
    }
  
    // CheckSum
    // The value of this byte, when added to the sum of all of the bytes from byte 64 of the descriptor
    // through the Reserved byte, should equal 0 in modulo 256 arithmetic
    var sum = 0;
    for (var i = 64; i < header.length - 2; i++) {
      sum += header.readUInt8(i);
    }
    this.checksum = 256 - (sum % 256);
    header.writeUInt8(this.checksum, header.length - 1);
  
    return header;
  };
  
  trueType.prototype.toPCL = function() {
    var self = this;
    var header = this.getHeader();
    var characterDescriptors = [];
    // get glyf table
    var glyfDir = this.font.directory.tables.glyf;
    var glyf = this._buffer.slice(glyfDir.offset, glyfDir.offset + glyfDir.length);
    var self = this;
  
    this.font.characterSet.forEach(function(codePoint) {
      var char = String.fromCharCode(codePoint);
      var encoded = charset[char];
      if (!encoded || encoded < self.firstCode || encoded > self.lastCode) {
        return;
      }
      var glyphID = self.font._cmapProcessor.lookup(codePoint);
      var offset = self.font.loca.offsets[glyphID];
      var end = self.font.loca.offsets[glyphID + 1];
      if (typeof end === "undefined") {
        end = glyf.length;
      }
      characterDescriptors.push({
        codePoint: encoded,
        data: (new CharacterDescriptor(glyphID, glyf.slice(offset, end))).toBuffers()
      });
    });
  
    this.document._writePCL('\x1b*c' + this.id + 'D');        // Assign Font ID Number
    this.document._writePCL('\x1b)s' + header.length + 'W');  // Download Font Header
    this.document._writeBinary(header);
    this.document._writePCL('\x1b(s' + this.size + 'V');      // Primary Font: Height (11 points)
    this.document._writePCL('\x1b(' + this.id + 'X');         // Primary Font: Select by ID
    this.document._writePCL('\x1b&d@');                       // Underline Disable
    characterDescriptors.forEach(function(cd) {
      self.document._writePCL('\x1b*c' + cd.codePoint + 'E');   // Character Code
      cd.data.forEach(function(block) {
        self.document._writePCL('\x1b(s' + block.length + 'W'); // Download Character
        self.document._writeBinary(block);
      });
    });
  };
  
  module.exports = trueType;
  