1 var define = require("../define").define,
  2         Collection = require("./Collection"),
  3 		Iterable = require("./Iterable"),
  4         base = require("../base"),
  5         multiply = base.string.multiply;
  6 
  7 var compare = function(a, b) {
  8     var ret = 0;
  9     if (a > b) {
 10         return 1;
 11     } else if (a < b) {
 12         return -1;
 13     }
 14     return ret;
 15 
 16 };
 17 
 18 var Tree = define([Collection, Iterable], {
 19 
 20     instance : {
 21 
 22         /**@lends comb.collections.Tree.prototype*/
 23 
 24         /**
 25          * Prints a node
 26          * @param node node to print
 27          * @param level the current level the node is at, Used for formatting
 28          */
 29         __printNode : function(node, level) {
 30             //console.log(level);
 31             var str = [];
 32             if (node == null || node == undefined) {
 33                 str.push(multiply('\t', level));
 34                 str.push("~");
 35                 console.log(str.join(""));
 36             } else {
 37                 this.__printNode(node.right, level + 1);
 38                 str.push(multiply('\t', level));
 39                 str.push(node.data + "\n");
 40                 console.log(str.join(""));
 41                 this.__printNode(node.left, level + 1);
 42             }
 43         },
 44 
 45         /**
 46          * Base Class for all tree implementations
 47          * @constructs
 48          * @augments comb.collections.Collection
 49          * @augments comb.collections.Iterable
 50          * @memberOf comb.collections
 51          *
 52          * @param {Object} options options to initialize the tree
 53          * @param {Function} options.compare function used to compare items in a tree must return an integer
 54          *  <ul>
 55          *      </li>-1 for less than</li>
 56          *      </li>0 for equal</li>
 57          *      </li>1 for greater than</li>
 58          *  </ul>
 59          *
 60          */
 61         constructor : function(options) {
 62             options = options || {};
 63             this.compare = options.compare || compare;
 64             this.__root = null;
 65         },
 66 
 67         /**
 68          * Inserts an item into the tree
 69          * @param {*} data the item to insert
 70          */
 71         insert : function(data) {
 72             throw new Error("Not Implemented");
 73         },
 74 
 75         /**
 76          * Removes an item from the tree
 77          * @param {*} data the item to insert
 78          */
 79         remove : function(data) {
 80             throw new Error("Not Implemented");
 81         },
 82 
 83         /**
 84          * Clear all items from a tree
 85          */
 86         clear : function() {
 87             this.__root = null;
 88         },
 89 
 90         /**
 91          * Test if a tree is empty
 92          *
 93          * @return {Boolean} true if empty false otherwise
 94          */
 95         isEmpty : function() {
 96             return this.__root == null;
 97         },
 98 
 99         /**
100          * Traverse a tree until the callback function returns false
101          *
102          * <p><b>Not typically used directly</b></p>
103          *
104          * @param {Object} node the node to start at
105          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
106          * @param {Function} callback called for each item, traversal continues until the function returns false
107          *
108          */
109         traverseWithCondition : function(node, order, callback) {
110             var cont = true;
111             if (node) {
112                 order = order || Tree.PRE_ORDER;
113                 if (order === Tree.PRE_ORDER) {
114                     cont = callback(node.data);
115                     if (cont) {
116                         cont = this.traverseWithCondition(node.left, order, callback);
117                         cont && (cont = this.traverseWithCondition(node.right, order, callback));
118 
119                     }
120                 } else if (order === Tree.IN_ORDER) {
121                     cont = this.traverseWithCondition(node.left, order, callback);
122                     if (cont) {
123                         cont = callback(node.data);
124                         cont && (cont = this.traverseWithCondition(node.right, order, callback));
125                     }
126                 } else if (order === Tree.POST_ORDER) {
127                     cont = this.traverseWithCondition(node.left, order, callback);
128                     if (cont) {
129                         cont && (cont = this.traverseWithCondition(node.right, order, callback));
130                         cont && (cont = callback(node.data));
131                     }
132                 } else if (order === Tree.REVERSE_ORDER) {
133                     cont = this.traverseWithCondition(node.right, order, callback);
134                     if (cont) {
135                         cont = callback(node.data);
136                         cont && (cont = this.traverseWithCondition(node.left, order, callback));
137                     }
138                 }
139             }
140             return cont;
141         },
142 
143         /**
144          * Traverse a tree
145          *
146          * <p><b>Not typically used directly</b></p>
147          *
148          * @param {Object} node the node to start at
149          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
150          * @param {Function} callback called for each item
151          *
152          */
153         traverse : function(node, order, callback) {
154             if (node) {
155                 order = order || Tree.PRE_ORDER;
156                 if (order === Tree.PRE_ORDER) {
157                     callback(node.data);
158                     this.traverse(node.left, order, callback);
159                     this.traverse(node.right, order, callback);
160                 } else if (order === Tree.IN_ORDER) {
161                     this.traverse(node.left, order, callback);
162                     callback(node.data);
163                     this.traverse(node.right, order, callback);
164                 } else if (order === Tree.POST_ORDER) {
165                     this.traverse(node.left, order, callback);
166                     this.traverse(node.right, order, callback);
167                     callback(node.data);
168                 } else if (order === Tree.REVERSE_ORDER) {
169                     this.traverseWithCondition(node.right, order, callback);
170                     callback(node.data);
171                     this.traverseWithCondition(node.left, order, callback);
172 
173                 }
174             }
175         },
176 
177         /**
178          * Loop through each item in the tree
179          * @param {Function} cb called for each item in the tree
180          * @param {Object} [scope=this] scope to call the function in
181          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
182          */
183         forEach : function(cb, scope, order) {
184             if (typeof cb !== "function")
185                 throw new TypeError();
186             order = order || Tree.IN_ORDER;
187             scope = scope || this;
188             this.traverse(this.__root, order, function(node) {
189                 cb.call(scope, node, this);
190             });
191         },
192 
193         /**
194          * Loop through each item in the tree, collecting the value returned by the callback funciton.
195          * @param {Function} cb called for each item in the tree.
196          *                   Whatever the function returns is inserted into the return tree
197          * @param {Object} [scope=this] scope to call the function in
198          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
199          *
200          * @return {comb.collections.Tree} the tree with the mapped items
201          */
202         map : function(cb, scope, order) {
203             if (typeof cb !== "function")
204                 throw new TypeError();
205 
206             order = order || Tree.IN_ORDER;
207             scope = scope || this;
208             var construct = this.constructor;
209             var ret = new construct();
210             this.traverse(this.__root, order, function(node) {
211                 ret.insert(cb.call(scope, node, this));
212             });
213             return ret;
214         },
215 
216         /**
217          * Filters a tree, only returning items that result in true being returned from the callback
218          *
219          * @param {Function} cb called for each item in the tree
220          * @param {Object} [scope=this] scope to call the function in
221          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
222          *
223          * @return {comb.collections.Tree} the tree with items that resulted in true being returned from the callback
224          */
225         filter : function(cb, scope, order) {
226             if (typeof cb !== "function")
227                 throw new TypeError();
228 
229             order = order || Tree.IN_ORDER;
230             scope = scope || this;
231             var construct = this.constructor;
232             var ret = new construct();
233             this.traverse(this.__root, order, function(node) {
234                 var include = cb.call(scope, node, this);
235                 include && ret.insert(node);
236             });
237             return ret;
238         },
239 
240         /**
241          * Reduces a tree
242          *
243          * @param {Function} cb called for each item in the tree
244          * @param [accumulator=First item in tree(Order dependant)] scope to call the function in
245          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
246          *
247          * @return the result of the reduce function
248          */
249         reduce : function(fun, accumulator, order) {
250             var arr = this.toArray(order);
251             var args = [fun];
252             if (!base.isUndefined(accumulator) && !base.isNull(accumulator)) {
253                 args.push(accumulator);
254             }
255             return arr.reduce.apply(arr, args);
256         },
257 
258         /**
259          * Reduces from right to left
260          *
261          * @param {Function} cb called for each item in the tree
262          * @param [accumulator=First item in tree(Order dependant)] scope to call the function in
263          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
264          *
265          * @return the result of the reduce function
266          */
267         reduceRight : function(fun, accumulator, order) {
268             var arr = this.toArray(order);
269             var args = [fun];
270             if (!base.isUndefined(accumulator) && !base.isNull(accumulator)) {
271                 args.push(accumulator);
272             }
273             return arr.reduceRight.apply(arr, args);
274         },
275 
276         /**
277          * Determines if every item meets the condition returned by the callback.
278          *
279          * @param {Function} cb called for each item in the tree
280          * @param {Object} [scope=this] scope to call the function in
281          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
282          *
283          * @return {Boolean} True if every item passed false otherwise
284          */
285         every : function(cb, scope, order) {
286             if (typeof cb !== "function")
287                 throw new TypeError();
288 
289             order = order || Tree.IN_ORDER;
290             scope = scope || this;
291             var ret = false;
292             this.traverseWithCondition(this.__root, order, function(node) {
293                 return (ret = cb.call(scope, node, this));
294             });
295             return ret;
296         },
297 
298         /**
299          * Determines if some item meet the condition returned by the callback. Traversal ends the first time true is found.
300          *
301          * @param {Function} cb called for each item in the tree
302          * @param {Object} [scope=this] scope to call the function in
303          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
304          *
305          * @return {Boolean} True if every item passed false otherwise
306          */
307         some : function(cb, scope, order) {
308             if (typeof cb !== "function")
309                 throw new TypeError();
310 
311             order = order || Tree.IN_ORDER;
312             scope = scope || this;
313             var ret;
314             this.traverseWithCondition(this.__root, order, function(node) {
315                 ret = cb.call(scope, node, this);
316                 return !ret;
317             });
318             return ret;
319         },
320 
321         /**
322          * Converts a tree into an array based on the specified order
323          *
324          * @param {Tree.PRE_ORDER|Tree.POST_ORDER|Tree.IN_ORDER|Tree.REVERSE_ORDER} [order=Tree.IN_ORDER] the traversal scheme
325          *
326          * @return {Array} array of all items in the order specified.
327          */
328         toArray : function(order) {
329             order = order || Tree.IN_ORDER;
330             var arr = [];
331             this.traverse(this.__root, order, function(node) {
332                 arr.push(node);
333             });
334             return arr;
335         },
336 
337 
338 
339         /**
340          * Determines if a value is contained in the tree
341          * @param {*} value the value to find
342          *
343          * @return {Boolean} true if the tree contains the item false otherwise.
344          */
345         contains : function(value) {
346             var ret = false;
347             var root = this.__root;
348             while (root != null) {
349                 var cmp = this.compare(value, root.data);
350                 if (cmp) {
351                     root = root[(cmp == -1) ? "left" : "right"];
352                 } else {
353                     ret = true;
354                     root = null;
355                 }
356             }
357             return ret;
358         },
359 
360         /**
361          * Finds a value in the tree
362          * @param {*} value the value to find
363          *
364          * @return the value of the node that matched
365          */
366         find : function(value) {
367             var ret;
368             var root = this.__root;
369             while (root != null) {
370                 var cmp = this.compare(value, root.data);
371                 if (cmp) {
372                     root = root[(cmp == -1) ? "left" : "right"];
373                 } else {
374                     ret = root.data;
375                     break;
376                 }
377             }
378             return ret;
379         },
380 
381         /**
382          * Find all values less than a value
383          * @param {*} value the value to find nodes less than
384          * @param {Boolean} [exclusive=false] if true the value will NOT be included in the return array
385          *
386          * @return {Array} the array containing all values less than
387          */
388         findLessThan : function(value, exclusive) {
389             //find a better way!!!!
390             var ret = [], compare = this.compare;
391             this.traverseWithCondition(this.__root, exports.IN_ORDER, function(v) {
392                 var cmp = compare(value, v);
393                 if ((!exclusive && cmp == 0) || cmp == 1) {
394                     ret.push(v);
395                     return true;
396                 } else {
397                     return false;
398                 }
399             });
400             return ret;
401         },
402 
403         /**
404          * Find all greater than a value
405          * @param {*} value the value to find nodes greater than
406          * @param {Boolean} [exclusive=false] if true the value will NOT be included in the return array
407          *
408          * @return {Array} the array containing all values greater than
409          */
410         findGreaterThan : function(value, exclusive) {
411             //find a better way!!!!
412             var ret = [], compare = this.compare;
413             this.traverse(this.__root, exports.REVERSE_ORDER, function(v) {
414                 var cmp = compare(value, v);
415                 if ((!exclusive && cmp == 0) || cmp == -1) {
416                     ret.push(v);
417                     return true;
418                 } else {
419                     return false;
420                 }
421             });
422             return ret;
423         },
424 
425         /**
426          * Prints a tree to the console.
427          */
428         print : function() {
429             this.__printNode(this.__root, 0);
430         },
431 
432 	    __iterator__ : function(){
433 
434 	    }
435     },
436 
437     static : {
438     /** @lends comb.collections.Tree */
439 
440         /**
441          * Pre Order
442          */
443         PRE_ORDER : "pre_order",
444 
445         /**
446          * In Order
447          */
448         IN_ORDER : "in_order",
449 
450         /**
451          * Post Order
452          */
453         POST_ORDER:"post_order",
454 
455         /**
456 
457          * Reverse Order
458          */
459         REVERSE_ORDER : "reverse_order"
460     }
461 });
462 /**@ignore*/
463 module.exports = exports = Tree;
464 
465