'use strict';

const { EventEmitter } = require('events');

class Node {
    constructor (value) {
        this.value = value;
        this.next = null;
    }

    link (next) {
        this.next = next;
        next.prev = this;
        return this;
    }
}


/*
    * linked list
    * ranged list, assumes start to end(tail) is a known order
        * remove() is only implemented in reverse order for the usecase
    * emits events on eviction
*/
module.exports = class List extends EventEmitter {
    constructor () {
        super();
        this.start = null;
        this.tail = null;
    }

    add (obj) {
        const node = new Node(obj);
        if (this.start) {
            this.start = node.link(this.start);
        } else {
            this.start = node;
            this.tail = node;
        }
        return node;
    }

    iterate (fn) {
        if (!this.start) {
            return;
        }
        let cursor = this.start;
        while (cursor) {
            const result = fn(cursor);
            if (result === false) {
                cursor = null;
            } else {
                cursor = cursor.next;
            }
        }
    }

    iterateReverse (fn) {
        if (!this.tail) {
            return;
        }
        let cursor = this.tail;
        while (cursor) {
            const result = fn(cursor);
            if (result === false) {
                cursor = null;
            } else {
                cursor = cursor.prev;
            }
        }
    }

    reverseRemoveUntilTrue (fn) {
        if (!this.tail) {
            return;
        }

        let cursor = this.tail;
        while (cursor) {
            const result = fn(cursor);
            if (result === false && cursor === this.start) {
                // whole list is removed
                this.emit('evicted', cursor.value);
                this.start = null;
                this.tail  = null;
                // stop iteration
                cursor = null;
            } else if (result === true) {
                // when TRUE, set match as new tail
                if (cursor !== this.tail) {
                    this.tail = cursor;
                    cursor.next = null;
                }
                // stop iteration
                cursor = null;
            } else {
                // evicted
                this.emit('evicted', cursor.value);
                // iterate to next
                cursor = cursor.prev;
            }
        }
    }

    toArray () {
        const result = [];

        if (this.start) {
            let cursor = this.start;
            while (cursor) {
                result.push(cursor.value);
                cursor = cursor.next;
            }
        }

        return result;
    }

    toArrayReverse () {
        const result = [];

        if (this.tail) {
            let cursor = this.tail;
            while (cursor) {
                result.push(cursor.value);
                cursor = cursor.prev;
            }
        }

        return result;
    }
};