'use strict';

var reAnsi        = require('ansi-regex')
  , stringifiable = require('es5-ext/object/validate-stringifiable')
  , length        = require('./get-stripped-length');

module.exports = function (str, begin, end) {
	str = stringifiable(str);

	var len = length(str);

	if (begin == null) {
		begin = 0;
	}
	if (end == null) {
		end = len;
	}
	if (begin < 0) {
		begin = len + begin;
	}
	if (end < 0) {
		end = len + end;
	}

	var seq = tokenize(str);
	seq = sliceSeq(seq, begin, end);

	return seq.map(function (chunk) {
		if (chunk instanceof Token) {
			return chunk.token;
		}

		return chunk;
	}).join('');
};

function tokenize(str) {
	var match = reAnsi().exec(str);

	if (!match) {
		return [ str ];
	}

	var index = match.index;
	if (index === 0) {
		var head = match[0];
		var tail = str.slice(head.length);

		return [ new Token(head) ].concat(tokenize(tail));
	}

	var prehead = str.slice(0, index);
	var head = match[0];
	var tail = str.slice(index + head.length);

	return [ prehead, new Token(head) ].concat(tokenize(tail));
}

function Token(token) {
	this.token = token;
}

function sliceSeq(seq, begin, end) {
	return seq.reduce(function (state, chunk) {
		if (!(chunk instanceof Token)) {
			var index = state.index;
			var nextChunk = '';

			if (isChunkInSlice(chunk, index, begin, end)) {
				var relBegin = Math.max(begin - index, 0);
				var relEnd = Math.min(end - index, chunk.length);

				nextChunk = chunk.slice(relBegin, relEnd);
			}

			state.seq.push(nextChunk);
			state.index = index + chunk.length;
		} else {
			state.seq.push(chunk);
		}

		return state;
	}, {
		index: 0,
		seq: []
	}).seq;
}

function isChunkInSlice(chunk, index, begin, end) {
	var endIndex = chunk.length + index;

	if (begin > endIndex) return false;
	if (end < index) return false;
	return true;
}

@@ -742,16 +742,39 @@
 .index;%0A
+%09var head;%0A%09var tail;%0A%0A
 %09if (ind
@@ -787,20 +787,16 @@
  0) %7B%0A%09%09
-var 
 head = m
@@ -806,20 +806,16 @@
 h%5B0%5D;%0A%09%09
-var 
 tail = s
@@ -932,20 +932,16 @@
 ndex);%0A%09
-var 
 head = m
@@ -950,20 +950,16 @@
 ch%5B0%5D;%0A%09
-var 
 tail = s

fix var re-definitions.