function paddingLeft(paddingValue, string) {
   return String(paddingValue + string).slice(-paddingValue.length);
};

function rtrim(str) {
  return str.replace(/\s*$/g, '');
}

function ltrim(str) {
  return str.replace(/^\s*/g, '');
}

function fieldsLength(fields) {
  var length = 0;
  fields.forEach(function(field) {
    length += field.length();
  });
  return length;
};

Tag = function(value) {
  this.value = value;
};

Tag.prototype.toString = function() {
  return "(" + paddingLeft("0000", this.group().toString(16)) + "," + 
         paddingLeft("0000", this.element().toString(16)) + ")";
}

Tag.prototype.is = function(t) {
  return this.value == t;
}

Tag.prototype.group = function() {
  return this.value >>> 16;
}

Tag.prototype.element = function() {
  return this.value & 0xffff;
}

tagFromNumbers = function(group, element) {
  return new Tag(((group << 16) | element) >>> 0);
}

function readTag(stream) {
  var group = stream.read(C.TYPE_UINT16), 
      element = stream.read(C.TYPE_UINT16);

  var tag = tagFromNumbers(group, element);
  return tag;
}

parseElements = function (stream, syntax) {
  var pairs = {};
  stream.reset();
  while (!stream.end()) {
    var elem = new DataElement();
    elem.setSyntax(syntax);
    elem.readBytes(stream);
    pairs[elem.tag.value] = elem;
  }
  return pairs;
}

ValueRepresentation = function(type) {
  this.type = type;
  this.multi = false;  
}

ValueRepresentation.prototype.read = function(stream, length, syntax) {
  if (this.fixed && this.maxLength) {
    if (!length)
      return this.defaultValue;
    if (this.maxLength != length)
      throw "Invalid length for fixed length tag, vr " + this.type + ", length " + this.maxLength + " != " + length;
  }
  return this.readBytes(stream, length, syntax);
}

ValueRepresentation.prototype.readBytes = function(stream, length) {
  return stream.read(C.TYPE_ASCII, length);
}

ValueRepresentation.prototype.readNullPaddedString = function(stream, length) {
  if (!length) return "";

  var str = stream.read(C.TYPE_ASCII, length - 1);
  if (stream.read(C.TYPE_UINT8) != 0) {
    stream.increment(-1);
    str += stream.read(C.TYPE_ASCII, 1);
  }    
  return str;
}

ValueRepresentation.prototype.getFields = function(fields) {
  var valid = true;
  if (this.checkLength) {
    valid = this.checkLength(fields);
  } else if (this.maxCharLength) {
    var check = this.maxCharLength, length = 0;
    fields.forEach(function(field){
      if (typeof field.value == 'string')
        length += field.value.length;
    });
    valid = length <= check; 
  } else if (this.maxLength) {
    var check = this.maxLength, length = fieldsLength(fields);
    valid = length <= check;
  }
  if (!valid)
    throw "Value exceeds max length";

  //check for odd
  var length = fieldsLength(fields);
  if (length & 1) {
    fields.push(new HexField(this.padByte));
  }

  for (var i = 0;i < fields.length;i++) {
    if (fields[i].isNumeric() && (fields[i].value === "" || fields[i].value === null)) {
      fields[i] = new StringField("");
    }
  }

  return fields;
}

ApplicationEntity = function() {
  ValueRepresentation.call(this, "AE");
  this.maxLength = 16;
  this.padByte = "20";
};
util.inherits(ApplicationEntity, ValueRepresentation);

ApplicationEntity.prototype.readBytes = function(stream, length) {
  return stream.read(C.TYPE_ASCII, length).trim();
}  

ApplicationEntity.prototype.getFields = function(value) {
  return ApplicationEntity.super_.prototype.getFields.call(this, [new FilledField(value, 16)]);
}

CodeString = function() {
  ValueRepresentation.call(this, "CS");
  this.maxLength = 16;
  this.padByte = "20"; 
};
util.inherits(CodeString, ValueRepresentation);

CodeString.prototype.readBytes = function(stream, length) {
  var str = this.readNullPaddedString(stream, length);
  return str.trim();    
}

CodeString.prototype.getFields = function(value) {
  return CodeString.super_.prototype.getFields.call(this, [new StringField(value)]);
}

AgeString = function() {
  ValueRepresentation.call(this, "AS");
  this.maxLength = 4;
  this.padByte = "20";
  this.fixed = true;
  this.defaultValue = "";
};
util.inherits(AgeString, ValueRepresentation);

AgeString.prototype.getFields = function(value) {
  var str = "";
  if (value) {
    if (value.days) {
      str = paddingLeft("000" + value.days) + "D";
    } else if (value.weeks) {
      str = paddingLeft("000" + value.weeks) + "W";
    } else if (value.months) {
      str = paddingLeft("000" + value.months) + "M";
    } else if (value.years) {
      str = paddingLeft("000" + value.years) + "Y";
    } else {
      throw "Invalid age string";
    }    
  }

  return AgeString.super_.prototype.getFields.call(this, [new StringField(str)]);
}

AttributeTag = function() {
  ValueRepresentation.call(this, "AT");
  this.maxLength = 4;
  this.padByte = "00";
  this.fixed = true;
};
util.inherits(AttributeTag, ValueRepresentation);

AttributeTag.prototype.readBytes = function(stream, length) {
  var group = stream.read(C.TYPE_UINT16), element = stream.read(C.TYPE_UINT16);
  return tagFromNumbers(group, element);
}

AttributeTag.prototype.getFields = function(value) {
  return AttributeTag.super_.prototype.getFields.call(this, [new UInt16Field(value.group()), new UInt16Field(value.element())]);
}

DateValue = function() {
  ValueRepresentation.call(this, "DA");
  this.maxLength = 8;
  this.padByte = "20";
  this.fixed = true;
  this.defaultValue = "";
};
util.inherits(DateValue, ValueRepresentation);

DateValue.prototype.readBytes = function(stream, length) {
  var datestr = stream.read(C.TYPE_ASCII, 8);

  var year = parseInt(datestr.substring(0,4)), 
      month = parseInt(datestr.substring(4,6)), 
      day = parseInt(datestr.substring(6,8));
  return datestr;//new Date(year, month, day);
}

DateValue.prototype.getFields = function(date) {
  var str = null;
  if (typeof date == 'object') {
    var year = date.getFullYear(), month = paddingLeft("00", date.getMonth()), day = paddingLeft("00", date.getDate());
    str = year + month + day;
  } else if (date && date.length > 0) {
    this.maxLength = 18;
    this.fixed = false;
    str = date;
  } else {
    str = "";
  }

  return DateValue.super_.prototype.getFields.call(this, [new StringField(str)]);
}

DecimalString = function() {
  ValueRepresentation.call(this, "DS");
  this.maxLength = 16;
  this.padByte = "20";
};
util.inherits(DecimalString, ValueRepresentation);

DecimalString.prototype.readBytes = function(stream, length) {
  var str = this.readNullPaddedString(stream, length);
  return str.trim();
}

DecimalString.prototype.getFields = function(value) {
  var f = parseFloat(value);
  return DecimalString.super_.prototype.getFields.call(this, [new StringField(isNaN(f) ? '' : f.toExponential())]);
}

DateTime = function() {
  ValueRepresentation.call(this, "DT");
  this.maxLength = 26;
  this.padByte = "20";
};
util.inherits(DateTime, ValueRepresentation);

DateTime.prototype.getFields = function(value) {
  var year = date.getUTCFullYear(), month = paddingLeft("00", date.getUTCMonth()), 
      day = paddingLeft("00", date.getUTCDate()), hour = paddingLeft("00", date.getUTCHours()),
      minute = paddingLeft("00", date.getUTCMinutes()), second = paddingLeft("00", date.getUTCSeconds()),
      millisecond = paddingLeft("000", date.getUTCMilliseconds());

  return DateTime.super_.prototype.getFields.call(this, [new StringField(year + month + day + hour + minute + second + "." + millisecond + "+0000")]);
}

FloatingPointSingle = function() {
  ValueRepresentation.call(this, "FL");
  this.maxLength = 4;
  this.padByte = "00";
  this.fixed = true;
  this.defaultValue = 0.0;
};
util.inherits(FloatingPointSingle, ValueRepresentation);

FloatingPointSingle.prototype.readBytes = function(stream, length) {
  return stream.read(C.TYPE_FLOAT);
}

FloatingPointSingle.prototype.getFields = function(value) {
  return FloatingPointSingle.super_.prototype.getFields.call(this, [new FloatField(value)]);
}

FloatingPointDouble = function() {
  ValueRepresentation.call(this, "FD");
  this.maxLength = 8;
  this.padByte = "00";
  this.fixed = true;
  this.defaultValue = 0.0;
};
util.inherits(FloatingPointDouble, ValueRepresentation);

FloatingPointDouble.prototype.readBytes = function(stream, length) {
  return stream.read(C.TYPE_DOUBLE);
}  

FloatingPointDouble.prototype.getFields = function(value) {
  return FloatingPointDouble.super_.prototype.getFields.call(this, [new DoubleField(value)]);
}

IntegerString = function() {
  ValueRepresentation.call(this, "IS");
  this.maxLength = 12;
  this.padByte = "20";
};
util.inherits(IntegerString, ValueRepresentation);

IntegerString.prototype.readBytes = function(stream, length) {
  var str = this.readNullPaddedString(stream, length);
  return str.trim();
}

IntegerString.prototype.getFields = function(value) {
  return IntegerString.super_.prototype.getFields.call(this, [new StringField(value.toString())]);
}

LongString = function() {
  ValueRepresentation.call(this, "LO");
  this.maxCharLength = 64;
  this.padByte = "20";
};
util.inherits(LongString, ValueRepresentation);

LongString.prototype.readBytes = function(stream, length) {
  var str = this.readNullPaddedString(stream, length);
  return str.trim();
}  

LongString.prototype.getFields = function(value) {
  return LongString.super_.prototype.getFields.call(this, [new StringField(value ? value : "")]);
}

LongText = function() {
  ValueRepresentation.call(this, "LT");
  this.maxCharLength = 10240;
  this.padByte = "20";
};
util.inherits(LongText, ValueRepresentation);

LongText.prototype.readBytes = function(stream, length) {
  var str = this.readNullPaddedString(stream, length);
  return rtrim(str);
}  

LongText.prototype.getFields = function(value) {
  return LongText.super_.prototype.getFields.call(this, [new StringField(value)]);
}

PersonName = function() {
  ValueRepresentation.call(this, "PN");
  this.maxLength = null;
  this.padByte = "20";
};
util.inherits(PersonName, ValueRepresentation);

PersonName.prototype.checkLength = function(field) {
  var cmps = field[0].value.split(/\^/);
  for (var i in cmps) {
    var cmp = cmps[i];
    if (cmp.length > 64) return false;
  }
  return true;
}

PersonName.prototype.readBytes = function(stream, length) {
  var str = this.readNullPaddedString(stream, length);
  return rtrim(str);
}

PersonName.prototype.getFields = function(value) {
  var str = null;
  if (typeof value == 'string') {
    str = value;
  } else if (value) {
    var fName = value.family || "", gName = value.given || "", 
        middle = value.middle || "", prefix = value.prefix || "", suffix = value.suffix || "";

    str = [fName, gName, middle, prefix, suffix].join("^");      
  } else str = '';

  return PersonName.super_.prototype.getFields.call(this, [new StringField(str)]);
}

ShortString = function() {
  ValueRepresentation.call(this, "SH");
  this.maxCharLength = 16;
  this.padByte = "20";
};
util.inherits(ShortString, ValueRepresentation);

ShortString.prototype.readBytes = function(stream, length) {
  var str = this.readNullPaddedString(stream, length);
  return str.trim();
}  

ShortString.prototype.getFields = function(value) {
  return ShortString.super_.prototype.getFields.call(this, [new StringField(value)]);
}

SignedLong = function() {
  ValueRepresentation.call(this, "SL");
  this.maxLength = 4;
  this.padByte = "00";
  this.fixed = true;
  this.defaultValue = 0;
};
util.inherits(SignedLong, ValueRepresentation);

SignedLong.prototype.readBytes = function(stream, length) {
  return stream.read(C.TYPE_INT32);
}

SignedLong.prototype.getFields = function(value) {
  return SignedLong.super_.prototype.getFields.call(this, [new Int32Field(value)]);
}

SequenceOfItems = function() {
  ValueRepresentation.call(this, "SQ");
  this.maxLength = null;
  this.padByte = "00";
};
util.inherits(SequenceOfItems, ValueRepresentation);

SequenceOfItems.prototype.readBytes = function(stream, sqlength, syntax) {
  if (sqlength == 0x0) {
    return []; //contains no dataset
  } else {
    var undefLength = sqlength == 0xffffffff, elements = [], read = 0;

    while (true) {
      var tag = readTag(stream), length = null;
      read += 4;

      if (tag.is(0xfffee0dd)) {
        stream.read(C.TYPE_UINT32)
        break;
      } else if (!undefLength && (read == sqlength)) {
        break;
      } else if (tag.is(0xfffee000)) {
        length = stream.read(C.TYPE_UINT32);
        read += 4;
        var itemStream = null, toRead = 0, undef = length == 0xffffffff;

        if (undef) {
          var stack = 0;
          while (1) {
            var g = stream.read(C.TYPE_UINT16);
            if (g == 0xfffe) {
              var ge = stream.read(C.TYPE_UINT16);
              if (ge == 0xe00d) {
                stack--;
                if (stack < 0) {
                  stream.increment(4);
                  read += 8;
                  break;
                } else {
                  toRead += 4;
                }
              } else if (ge == 0xe000) {
                stack++;
                toRead += 4;
              } else {
                toRead += 2;
                stream.increment(-2);
              }
            } else {
              toRead += 2;
            }
          }
        } else {
          toRead = length;
        }

        if (toRead) {
          stream.increment(undef ? (-toRead-8) : 0);
          itemStream = stream.more(toRead);//parseElements
          read += toRead;
          if (undef)
            stream.increment(8);

          elements.push(parseElements(itemStream, syntax));
        }
        if (!undefLength && (read == sqlength)) {
          break;
        }
      }
    }
    return elements;
  }
}

SequenceOfItems.prototype.getFields = function(value, syntax) {
  var fields = [];
  if (value) {
    value.forEach(function(message) {
      fields.push(new UInt16Field(0xfffe));
      fields.push(new UInt16Field(0xe000));
      fields.push(new UInt32Field(0xffffffff));

      message.forEach(function(element) {
        element.setSyntax(syntax);
        fields = fields.concat(element.getFields());
      });

      fields.push(new UInt16Field(0xfffe));
      fields.push(new UInt16Field(0xe00d));
      fields.push(new UInt32Field(0x00000000));
    });    
  }

  fields.push(new UInt16Field(0xfffe));
  fields.push(new UInt16Field(0xe0dd));
  fields.push(new UInt32Field(0x00000000));    

  return SequenceOfItems.super_.prototype.getFields.call(this, fields);
}

SignedShort = function() {
  ValueRepresentation.call(this, "SS");
  this.maxLength = 2;
  this.padByte = "00";
  this.fixed = true;
  this.defaultValue = 0;
};
util.inherits(SignedShort, ValueRepresentation);

SignedShort.prototype.readBytes = function(stream, length) {
  return stream.read(C.TYPE_INT16);
}

SignedShort.prototype.getFields = function(value) {
  return SignedShort.super_.prototype.getFields.call(this, [new Int16Field(value)]);
}

ShortText = function() {
  ValueRepresentation.call(this, "ST");
  this.maxCharLength = 1024;
  this.padByte = "20";
};
util.inherits(ShortText, ValueRepresentation);

ShortText.prototype.readBytes = function(stream, length) {
  var str = this.readNullPaddedString(stream, length);
  return rtrim(str);
}

ShortText.prototype.getFields = function(value) {
  return ShortText.super_.prototype.getFields.call(this, [new StringField(value)]);
}

TimeValue = function() {
  ValueRepresentation.call(this, "TM");
  this.maxLength = 14;
  this.padByte = "20";
};
util.inherits(TimeValue, ValueRepresentation);

TimeValue.prototype.readBytes = function(stream, length) {
  return rtrim(stream.read(C.TYPE_ASCII, length));
}  

TimeValue.prototype.getFields = function(date) {
  var dateStr = '';
  if (date)  {
    var hour = paddingLeft("00", date.getHours()),
        minute = paddingLeft("00", date.getMinutes()), second = paddingLeft("00", date.getSeconds()),
        millisecond = paddingLeft("000", date.getMilliseconds());    
    dateStr = hour + minute + second + "." + millisecond;
  }

  return TimeValue.super_.prototype.getFields.call(this, [new StringField(dateStr)]);
}

UnlimitedCharacters = function() {
  ValueRepresentation.call(this, "UC");
  this.maxLength = null;
  this.multi = true;
  this.padByte = "20";
};
util.inherits(UnlimitedCharacters, ValueRepresentation);

UnlimitedCharacters.prototype.readBytes = function(stream, length) {
  return rtrim(stream.read(C.TYPE_ASCII, length));
}  

UnlimitedCharacters.prototype.getFields = function(value) {
  return UnlimitedCharacters.super_.prototype.getFields.call(this, [new StringField(value)]);
}

UnlimitedText = function() {
  ValueRepresentation.call(this, "UT");
  this.maxLength = null;
  this.padByte = "20";
};
util.inherits(UnlimitedText, ValueRepresentation);

UnlimitedText.prototype.readBytes = function(stream, length) {
  return this.readNullPaddedString(stream, length);
}  

UnlimitedText.prototype.getFields = function(value) {
  return UnlimitedText.super_.prototype.getFields.call(this, [new StringField(value)]);
}

UnsignedShort = function() {
  ValueRepresentation.call(this, "US");
  this.maxLength = 2;
  this.padByte = "00";
  this.fixed = true;
  this.defaultValue = 0;
};
util.inherits(UnsignedShort, ValueRepresentation);

UnsignedShort.prototype.readBytes = function(stream, length) {
  return stream.read(C.TYPE_UINT16);
}

UnsignedShort.prototype.getFields = function(value) {
  return UnsignedShort.super_.prototype.getFields.call(this, [new UInt16Field(value)]);
}

UnsignedLong = function() {
  ValueRepresentation.call(this, "UL");
  this.maxLength = 4;
  this.padByte = "00";
  this.fixed = true;
  this.defaultValue = 0;
};
util.inherits(UnsignedLong, ValueRepresentation);

UnsignedLong.prototype.readBytes = function(stream, length) {
  return stream.read(C.TYPE_UINT32);
}  

UnsignedLong.prototype.getFields = function(value) {
  return UnsignedLong.super_.prototype.getFields.call(this, [new UInt32Field(value)]);
}

UniqueIdentifier = function() {
  ValueRepresentation.call(this, "UI");
  this.maxLength = 64;
  this.padByte = "00";
};
util.inherits(UniqueIdentifier, ValueRepresentation);

UniqueIdentifier.prototype.readBytes = function(stream, length) {
  return this.readNullPaddedString(stream, length);
}   

UniqueIdentifier.prototype.getFields = function(value) {
  return UniqueIdentifier.super_.prototype.getFields.call(this, [new StringField(value)]);
}

UniversalResource = function() {
  ValueRepresentation.call(this, "UR");
  this.maxLength = null;
  this.padByte = "20";
};
util.inherits(UniversalResource, ValueRepresentation);

UniversalResource.prototype.readBytes = function(stream, length) {
  return rtrim(stream.read(C.TYPE_ASCII, length));
}

UniversalResource.prototype.getFields = function(value) {
  return UniversalResource.super_.prototype.getFields.call(this, [new StringField(value)]);
}

UnknownValue = function() {
  ValueRepresentation.call(this, "UN");
  this.maxLength = null;
  this.padByte = "00";
};
util.inherits(UnknownValue, ValueRepresentation);

UnknownValue.prototype.readBytes = function(stream, length) {
  return stream.read(C.TYPE_ASCII, length);
}  

UnknownValue.prototype.getFields = function(value) {
  return UnknownValue.super_.prototype.getFields.call(this, [new StringField(value)]);
}

OtherWordString = function() {
  ValueRepresentation.call(this, "OW");
  this.maxLength = null;
  this.padByte = "00";
};
util.inherits(OtherWordString, ValueRepresentation);

OtherWordString.prototype.readBytes = function(stream, length) {
  return stream.read(C.TYPE_ASCII, length);
}  

OtherWordString.prototype.getFields = function(value) {
  return OtherWordString.super_.prototype.getFields.call(this, [new StringField(value)]);
} 


elementByType = function(type, value, syntax) {
  var elem = null, nk = DicomElements.dicomNDict[type];
  if (nk) {
    if (nk.vr == 'SQ') {
      var sq = [];
      if (value)
        value.forEach(function(el) {
          var values = [];
          for (var tag in el) {
            values.push(elementByType(tag, el[tag], syntax));
          }
          sq.push(values);
        });
      elem = new DataElement(type, nk.vr, nk.vm, sq, false, syntax);
    } else {
      elem = new DataElement(type, nk.vr, nk.vm, value, false, syntax);
    }
  } else {
    throw "Unrecognized element type";
  }
  return elem;
}

elementDataByTag = function(tag) {
  var nk = DicomElements.dicomNDict[tag];
  if (nk) {
    return nk;
  }
  throw ("Unrecognized tag " + (tag >>> 0).toString(16));
}

elementKeywordByTag = function(tag) {
  var nk = elementDataByTag(tag);
  return nk.keyword;
}

vrByType = function(type) {
  var vr = null;
  if (type == "AE") vr = new ApplicationEntity();
  else if (type == "AS") vr = new AgeString();
  else if (type == "AT") vr = new AttributeTag();
  else if (type == "CS") vr = new CodeString();
  else if (type == "DA") vr = new DateValue();
  else if (type == "DS") vr = new DecimalString();
  else if (type == "DT") vr = new DateTime();
  else if (type == "FL") vr = new FloatingPointSingle();
  else if (type == "FD") vr = new FloatingPointDouble();
  else if (type == "IS") vr = new IntegerString();
  else if (type == "LO") vr = new LongString();
  else if (type == "LT") vr = new LongText();
  else if (type == "OB") vr = new OtherByteString();
  else if (type == "OD") vr = new OtherDoubleString();
  else if (type == "OF") vr = new OtherFloatString();
  else if (type == "OW") vr = new OtherWordString();
  else if (type == "PN") vr = new PersonName();
  else if (type == "SH") vr = new ShortString();
  else if (type == "SL") vr = new SignedLong();
  else if (type == "SQ") vr = new SequenceOfItems();
  else if (type == "SS") vr = new SignedShort();
  else if (type == "ST") vr = new ShortText();
  else if (type == "TM") vr = new TimeValue();
  else if (type == "UC") vr = new UnlimitedCharacters();
  else if (type == "UI") vr = new UniqueIdentifier();
  else if (type == "UL") vr = new UnsignedLong();
  else if (type == "UN") vr = new UnknownValue();
  else if (type == "UR") vr = new UniversalResource();
  else if (type == "US") vr = new UnsignedShort();
  else if (type == "UT") vr = new UnlimitedText();
  else throw "Invalid vr type " + type;

  return vr;
}

readElements = function(stream, syntax) {
  if (stream.end()) return false;

  var oldEndian = stream.endian;
  stream.setEndian(this.endian);

  var group = stream.read(C.TYPE_UINT16), 
      element = stream.read(C.TYPE_UINT16),
      tag = new Tag((group << 16) | element),
      length = stream.read(C.TYPE_UINT32);

  stream.setEndian(oldEndian);
}

var explicitVRList = ["OB", "OW", "OF", "SQ", "UC", "UR", "UT", "UN"], 
    binaryVRs = ["FL", "FD", "SL", "SS", "UL", "US"];

DataElement = function(tag, vr, vm, value, vvr, syntax, options) {
  this.vr = vr ? vrByType(vr) : null;
  this.tag = !vvr ? new Tag(tag) : tag;
  this.value = value;
  this.vm = vm;
  this.vvr = vvr ? true : false;
  this.setOptions(options);
  this.setSyntax(syntax ? syntax : C.IMPLICIT_LITTLE_ENDIAN);
};

DataElement.prototype.setOptions = function(options) {
  this.options = Object.assign({split : true}, options);
}

DataElement.prototype.setSyntax = function(syn) {
  this.syntax = syn;
  this.implicit = this.syntax == C.IMPLICIT_LITTLE_ENDIAN ? true : false;
  this.endian = (this.syntax == C.IMPLICIT_LITTLE_ENDIAN || this.syntax == C.EXPLICIT_LITTLE_ENDIAN) ? C.LITTLE_ENDIAN : C.BIG_ENDIAN;
}

DataElement.prototype.getValue = function() {
  if (!this.singleValue() && !this.isBinaryNumber()) {
    return this.options.split ? this.value.split(String.fromCharCode(0x5c)) : this.value;
  } else {
    return this.value;
  }
}

DataElement.prototype.singleValue = function() {
  return this.vm == C.VM_SINGLE ? true : false;
}

DataElement.prototype.getVMNum = function() {
  var num = 1;
  switch(this.vm) {
    case C.VM_SINGLE : num = 1; break;
    case C.VM_TWO : num = 2; break;
    case C.VM_THREE : num = 3; break;
    case C.VM_FOUR : num = 4; break;
    case C.VM_16 : num = 16; break;
    default : break;
  }
  return num;
}

DataElement.prototype.isBinaryNumber = function() {
  return binaryVRs.indexOf(this.vr.type) != -1;
}

DataElement.prototype.length = function(fields) {
  //let fields = this.vr.getFields(this.value);
  return fieldsLength(fields);
}

DataElement.prototype.readBytes = function(stream) {
  var oldEndian = stream.endian;
  stream.setEndian(this.endian);

  var group = stream.read(C.TYPE_UINT16), 
      element = stream.read(C.TYPE_UINT16),
      tag = tagFromNumbers(group, element);

  var length = null, vr = null, edata = elementDataByTag(tag.value),
      vm = edata.vm;

  if (this.implicit) {
    length = stream.read(C.TYPE_UINT32);
    vr = edata.vr;
  } else {
    vr = stream.read(C.TYPE_ASCII, 2);
    if (explicitVRList.indexOf(vr) != -1) {
      stream.increment(2);
      length = stream.read(C.TYPE_UINT32);
    } else {
      length = stream.read(C.TYPE_UINT16);
    }
  }

  this.vr = vrByType(vr);
  this.tag = tag;
  this.vm = vm;
  //try {
  if (this.isBinaryNumber() && length > this.vr.maxLength) {
    var times = length / this.vr.maxLength, i = 0;
    this.value = [];//console.log(times, length, this.vr.maxLength);return;
    //try {
    while (i++ < times) {
      this.value.push(this.vr.read(stream, this.vr.maxLength));
    }
    //} catch (e) {  }
  } else {
    this.value = this.vr.read(stream, length, this.syntax);
  }
  //} catch (e) { console.log('error', vr, length); }

  stream.setEndian(oldEndian);
}

DataElement.prototype.write = function(stream) {
  var oldEndian = stream.endian;
  stream.setEndian(this.endian);

  var fields = this.getFields();
  fields.forEach(function(field){
    field.write(stream);
  });

  stream.setEndian(oldEndian);
}

DataElement.prototype.getFields = function() {
  var fields = [new UInt16Field(this.tag.group()), new UInt16Field(this.tag.element())], 
      valueFields = this.vr.getFields(this.value, this.syntax), valueLength = fieldsLength(valueFields), vrType = this.vr.type;    

  if (vrType == "SQ") {
    valueLength = 0xffffffff;
  }

  if (this.implicit) {
    fields.push(new UInt32Field(valueLength));
  } else {
    if (explicitVRList.indexOf(vrType) != -1) {
      fields.push(new StringField(vrType), new ReservedField(2), new UInt32Field(valueLength));
    } else {
      fields.push(new StringField(vrType), new UInt16Field(valueLength));
    } 
  }

  fields = fields.concat(valueFields);
  return fields;
}


