// ch12-iterators.js

'use strict';
import "babel-polyfill";


console.log(`\nEXAMPLE ONE: ITERATORS\n************************************\n`);

// ES6 introduces two very important new concepts: iterators and generators. 
// Generators depend on iterators, so we’ll start with iterators.

const book = [
	"Twinkle, twinkle, little bat!", 
	"How I wonder what you're at!", 
	"Up above the world you fly,", 
	"Like a tea tray in the sky.", 
	"Twinkle, twinkle, little bat!", 
	"How I wonder what you're at!",
];

let it = book.values();


// NEXT METHOD
console.log(it.next());			// { value: 'Twinkle, twinkle, little bat!', done: false }
console.log(it.next());			// { value: 'How I wonder what you\'re at!', done: false }
console.log(it.next());			// { value: 'Up above the world you fly,', done: false }
console.log(it.next());			// { value: 'Like a tea tray in the sky.', done: false }
console.log(it.next());			// { value: 'Twinkle, twinkle, little bat!', done: false }
console.log(it.next());			// { value: 'How I wonder what you\'re at!', done: false }


//  Once an iterator is done, it’s done, and it shouldn’t ever go back to providing you data.
for (let letter of it) {
  console.log(letter);			// It doesn´t log anything, iterator is gone
}

console.log(`\nEXAMPLE TWO: DONE AND VALUE PROPERTIES \n************************************\n`);


const words = book.values();
let current = words.next();

// PROPERTY DONE
while(!current.done) {
	// PROPERTY VALUE
	console.log(current.value);
	current = words.next();
}

// Twinkle, twinkle, little bat!
// How I wonder what you're at!
// Up above the world you fly,
// Like a tea tray in the sky.
// Twinkle, twinkle, little bat!
// How I wonder what you're at!


console.log(`\nEXAMPLE THREE: THE ITERATION PROTOCOL \n************************************\n`);
// The iterator protocol enables any object to be iterable.

// Create a logging class that attaches timestamps to messages.

class Log {
	static getNextId() {
		return Log.nextId++;
    }
	constructor() {
		this.messages = [];
		this.id = Log.getNextId();
	}
	add(message) {
		
		this.messages.push({ message, timestamp: Date.now(), id: this.id });
	}

	// Now we can iterate over an instance of Log just as if it were an array:
	[Symbol.iterator](){
		// OR return this.messages.values();
		let i = 0;
		const messages = this.messages;
		return {
			next(){
				if(i >= messages.length){
					return { value: undefined, done: true }
				}
				return { value: messages[i++], done: false }
			}
		}
	}
}
Log.nextId = 0;





const log     = new Log();

log.add('first day at sea');
log.add('spotted whale');
log.add('spotted another vessel');


for(let entry of log){
	console.log(`${entry.message} \t @ ${entry.timestamp} \t @ ${entry.id}`);
}


// first day at sea 	 	@ 1463001693510 	 @ 0
// spotted whale 	 		@ 1463001693510 	 @ 0
// spotted another vessel 	@ 1463001693510 	 @ 0


const logTwo = new Log();
logTwo.add('Second day at sea');

for(let entry of logTwo){
	console.log(`${entry.message} \t @ ${entry.timestamp} \t @ ${entry.id}`);
}
// Second day at sea 	 	@ 1463001693511 	 @ 1


console.log(`\nEXAMPLE FOUR: FIBONACCI SEQUENCE \n************************************\n`);
// 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,... FOREVER!!!
// will never return true for done


class FibonacciSequence {
	[Symbol.iterator]() {
		let a = 0, 
			b = 1;

		return{
			next() {
				let rval = { value: b, done: false };
				b += a;
				a = rval.value;
				return rval;
			}
		};
	}
}

const fib = new FibonacciSequence();
let i = 0;
for ( let n of fib) {
	console.log(n);
	if(++i > 9) { break; }

};
// 1, 1, 2, 3, 5, 8, 13, 21, 34, 55























