/********************************************************************
 * @author:      Kaven
 * @email:       kaven@wuwenkai.com
 * @website:     http://kavenblog.com
 * @file:        [Kaven-Basic] /src/libs/data_structures/KavenLinkedList.ts
 * @create:      2019-03-21 20:41:28.173
 * @modify:      2019-08-03 21:04:08.559
 * @version:     1.1.22
 * @times:       19
 * @lines:       285
 * @copyright:   Copyright © 2019 Kaven. All Rights Reserved.
 * @description: [description]
 * @license:
 * 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.
 ********************************************************************/
import KavenBasic from "../KavenBasic";
import { KavenLinkedListNode } from "./KavenLinkedListNode";
/**
 * Represents a doubly-Linked circular list.
 * @summary In this type of linked list, the next of the last node will point to the first node
 * and the previous pointer of the first node will point to the last node.
 * @since 1.1.19
 * @version 2019-03-22
 */
export class KavenLinkedList {
    constructor() {
        this.count = 0;
    }
    get Last() {
        if (this.head) {
            return this.head.Prev;
        }
        return undefined;
    }
    get First() {
        return this.head;
    }
    get Count() {
        return this.count;
    }
    AddAfter(node, value) {
        const newNode = new KavenLinkedListNode(value);
        if (node && node.Next) {
            this.insertNodeBefore(node.Next, newNode);
        }
        return newNode;
    }
    AddBefore(node, value) {
        const newNode = new KavenLinkedListNode(value);
        this.insertNodeBefore(node, newNode);
        if (node === this.head) {
            this.head = newNode;
        }
        return newNode;
    }
    AddFirst(value) {
        const node = new KavenLinkedListNode(value);
        if (this.head === undefined) {
            this.insertNodeToEmptyList(node);
        }
        else {
            this.insertNodeBefore(this.head, node);
            this.head = node;
        }
        return node;
    }
    AddLast(value) {
        const node = new KavenLinkedListNode(value);
        if (this.head === undefined) {
            this.insertNodeToEmptyList(node);
        }
        else {
            this.insertNodeBefore(this.head, node);
        }
        return node;
    }
    Clear() {
        let current = this.head;
        while (current !== undefined) {
            const temp = current;
            current = current.Next; // use Next the instead of "next", otherwise it will loop forever
            temp.Clear();
        }
        this.head = undefined;
        this.count = 0;
    }
    Contains(value) {
        return this.Find(value) !== undefined;
    }
    Find(value) {
        let node = this.head;
        if (node !== undefined) {
            if (value !== undefined) {
                do {
                    if (node !== undefined) {
                        if (this.isEqual(node.Value, value)) {
                            return node;
                        }
                        node = node.Next;
                    }
                } while (node !== this.head);
            }
            else {
                do {
                    if (node !== undefined) {
                        if (node.Value === undefined) {
                            return node;
                        }
                        node = node.Next;
                    }
                } while (node !== this.head);
            }
        }
        return undefined;
    }
    FindLast(value) {
        if (this.head === undefined) {
            return undefined;
        }
        const last = this.head.Prev;
        let node = last;
        if (node !== undefined) {
            if (value !== undefined) {
                do {
                    if (node !== undefined) {
                        if (this.isEqual(node.Value, value)) {
                            return node;
                        }
                        node = node.Prev;
                    }
                } while (node !== last);
            }
            else {
                do {
                    if (node !== undefined) {
                        if (node.Value === undefined) {
                            return node;
                        }
                        node = node.Prev;
                    }
                } while (node !== last);
            }
        }
        return undefined;
    }
    Remove(value) {
        const node = this.Find(value);
        if (node !== undefined) {
            this.removeNode(node);
            return true;
        }
        return false;
    }
    RemoveFirst() {
        if (this.head) {
            this.removeNode(this.head);
        }
    }
    RemoveLast() {
        if (this.head && this.head.Prev) {
            this.removeNode(this.head.Prev);
        }
    }
    Random() {
        if (this.head === undefined) {
            return undefined;
        }
        let random = KavenBasic.GetRandomInt(0, this.Count - 1);
        let node = this.head;
        while (random-- > 0 && node.Next) {
            node = node.Next;
        }
        return node.Value;
    }
    insertNodeToEmptyList(newNode) {
        newNode.Next = newNode;
        newNode.Prev = newNode;
        this.head = newNode;
        this.count++;
    }
    insertNodeBefore(node, newNode) {
        newNode.Next = node;
        newNode.Prev = node.Prev;
        if (node.Prev) {
            node.Prev.Next = newNode;
        }
        node.Prev = newNode;
        this.count++;
    }
    removeNode(node) {
        if (node.Next === node) {
            this.head = undefined;
        }
        else {
            if (node.Next) {
                node.Next.Prev = node.Prev;
            }
            if (node.Prev) {
                node.Prev.Next = node.Next;
            }
            if (this.head === node) {
                this.head = node.Next;
            }
        }
        node.Clear();
        this.count--;
    }
    isEqual(a, b) {
        if (this.EqualityComparer) {
            return this.EqualityComparer(a, b);
        }
        return a === b;
    }
}
//# sourceMappingURL=KavenLinkedList.js.map