// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

module.exports.extractParts = extractParts;

function extractParts(str) {
    var parts = [];
    forEachPart(str, function eachPart(part) {
        parts.push(part);
    });
    return new Parts(parts);
}

function forEachPart(str, each) {
    var i = 0;
    var pat = /\{(.+?)\}/g;
    var match = pat.exec(str);
    while (match) {
        var j = match.index;
        var string = str.slice(i, j);
        var choice = match[1].split(',');
        each(new Part(string, choice));
        i = j + match[0].length;
        match = pat.exec(str);
    }
    each(new Part(str.slice(i), null));
}

function Parts(parts) {
    this.parts = parts;
}

Parts.prototype.inspect =
Parts.prototype.toString =
function partsToString() {
    return 'Parts(' + this.parts.toString() + ')';
};

Parts.prototype.each =
function each(it) {
    var self = this;

    buildPart('', 0, it);

    function buildPart(prefix, i, emit) {
        var part = self.parts[i];
        if (!part) {
            emit(prefix);
            return;
        }
        part.each(function eachSubPart(str) {
            buildPart(prefix + str, i + 1, emit);
        });
    }
};

function Part(string, choice) {
    this.string = string || '';
    this.choice = choice || null;
}

Part.prototype.inspect =
Part.prototype.toString =
function partToString() {
    return 'Part(' + this.string + ', ' + this.choice + ')';
};

Part.prototype.each =
function each(it) {
    if (!this.choice) {
        it(this.string);
        return;
    }
    for (var i = 0; i < this.choice.length; i++) {
        it(this.string + this.choice[i]);
    }
};

Parts.prototype.all =
Part.prototype.all =
allEach;

function allEach() {
    var items = [];
    this.each(function each(item) {
        items.push(item);
    });
    return items;
}

@@ -1173,16 +1173,68 @@
 ctParts;
+%0Amodule.exports.extractPartsList = extractPartsList;
 %0A%0Afuncti
@@ -1385,24 +1385,952 @@
 (parts);%0A%7D%0A%0A
+function extractPartsList(str) %7B%0A    var list = %5B%5D;%0A    forEachSubstr(',', str, function eachComponent(component) %7B%0A        var parts = extractParts(component);%0A        list.push(parts);%0A    %7D);%0A    return new PartsList(list);%0A%7D%0A%0Afunction forEachSubstr(sep, str, each) %7B%0A    var start = 0;%0A    var level = 0;%0A    for (var i = 0; i %3C str.length; i++) %7B%0A        switch (str%5Bi%5D) %7B%0A            case '%7B':%0A                level++;%0A                break;%0A            case '%7D':%0A                if (--level %3C 0) %7B%0A                    level = 0;%0A                %7D%0A                break;%0A            case sep:%0A                if (level === 0) %7B%0A                    if (i %3E start) %7B%0A                        each(str.slice(start, i));%0A                    %7D%0A                    start = i + 1;%0A                %7D%0A                break;%0A            default:%0A        %7D%0A    %7D%0A    if (start %3C str.length) %7B%0A        each(str.slice(start));%0A    %7D%0A%7D%0A%0A
 function for
@@ -2703,24 +2703,363 @@
  null));%0A%7D%0A%0A
+function PartsList(list) %7B%0A    this.list = list;%0A%7D%0A%0APartsList.prototype.inspect =%0APartsList.prototype.toString =%0Afunction partsListToString() %7B%0A    return 'PartsList(' + this.list.toString() + ')';%0A%7D;%0A%0APartsList.prototype.each =%0Afunction each(it) %7B%0A    for (var i = 0; i %3C this.list.length; i++) %7B%0A        this.list%5Bi%5D.each(it);%0A    %7D%0A%7D;%0A%0A
 function Par
@@ -4050,24 +4050,50 @@
 ;%0A    %7D%0A%7D;%0A%0A
+PartsList.prototype.all =%0A
 Parts.protot

add PartsList and extraction