text: 'use strict';
  
  var Mixin = require('../../utils/mixin'),
      Tokenizer = require('../../tokenizer'),
      PositionTrackingPreprocessorMixin = require('../position_tracking/preprocessor_mixin'),
      inherits = require('inherits');
  
  var LocationInfoTokenizerMixin = module.exports = function (tokenizer) {
      Mixin.call(this, tokenizer);
  
      this.tokenizer = tokenizer;
      this.posTracker = new PositionTrackingPreprocessorMixin(tokenizer.preprocessor);
      this.currentAttrLocation = null;
      this.currentTokenLocation = null;
  };
  
  inherits(LocationInfoTokenizerMixin, Mixin);
  
  LocationInfoTokenizerMixin.prototype._getCurrentLocation = function () {
      return {
          line: this.posTracker.line,
          col: this.posTracker.col,
          startOffset: this.posTracker.offset,
          endOffset: -1
      };
  };
  
  LocationInfoTokenizerMixin.prototype._attachCurrentAttrLocationInfo = function () {
      this.currentAttrLocation.endOffset = this.posTracker.offset;
  
      var currentToken = this.tokenizer.currentToken,
          currentAttr = this.tokenizer.currentAttr;
  
      if (!currentToken.location.attrs)
          currentToken.location.attrs = Object.create(null);
  
      currentToken.location.attrs[currentAttr.name] = this.currentAttrLocation;
  };
  
  LocationInfoTokenizerMixin.prototype._getOverriddenMethods = function (mxn, orig) {
      var methods = {
          _createStartTagToken: function () {
              orig._createStartTagToken.call(this);
              this.currentToken.location = mxn.currentTokenLocation;
          },
  
          _createEndTagToken: function () {
              orig._createEndTagToken.call(this);
              this.currentToken.location = mxn.currentTokenLocation;
          },
  
          _createCommentToken: function () {
              orig._createCommentToken.call(this);
              this.currentToken.location = mxn.currentTokenLocation;
          },
  
          _createDoctypeToken: function (initialName) {
              orig._createDoctypeToken.call(this, initialName);
              this.currentToken.location = mxn.currentTokenLocation;
          },
  
          _createCharacterToken: function (type, ch) {
              orig._createCharacterToken.call(this, type, ch);
              this.currentCharacterToken.location = mxn.currentTokenLocation;
          },
  
          _createAttr: function (attrNameFirstCh) {
              orig._createAttr.call(this, attrNameFirstCh);
              mxn.currentAttrLocation = mxn._getCurrentLocation();
          },
  
          _leaveAttrName: function (toState) {
              orig._leaveAttrName.call(this, toState);
              mxn._attachCurrentAttrLocationInfo();
          },
  
          _leaveAttrValue: function (toState) {
              orig._leaveAttrValue.call(this, toState);
              mxn._attachCurrentAttrLocationInfo();
          },
  
          _emitCurrentToken: function () {
              //NOTE: if we have pending character token make it's end location equal to the
              //current token's start location.
              if (this.currentCharacterToken)
                  this.currentCharacterToken.location.endOffset = this.currentToken.location.startOffset;
  
              this.currentToken.location.endOffset = mxn.posTracker.offset + 1;
              orig._emitCurrentToken.call(this);
          },
  
          _emitCurrentCharacterToken: function () {
              //NOTE: if we have character token and it's location wasn't set in the _emitCurrentToken(),
              //then set it's location at the current preprocessor position.
              //We don't need to increment preprocessor position, since character token
              //emission is always forced by the start of the next character token here.
              //So, we already have advanced position.
              if (this.currentCharacterToken && this.currentCharacterToken.location.endOffset === -1)
                  this.currentCharacterToken.location.endOffset = mxn.posTracker.offset;
  
              orig._emitCurrentCharacterToken.call(this);
          }
      };
  
      //NOTE: patch initial states for each mode to obtain token start position
      Object.keys(Tokenizer.MODE).forEach(function (modeName) {
          var state = Tokenizer.MODE[modeName];
  
          methods[state] = function (cp) {
              mxn.currentTokenLocation = mxn._getCurrentLocation();
              orig[state].call(this, cp);
          };
      });
  
      return methods;
  };
  
  