import { GraphElement } from "./graphelement";
import { Node } from "./node";
import { NodeType } from "./nodetype";
import { Graph } from "./graph";

export interface LinkData {
    source: number;
    target: number;
    type?: string;
}

export interface SimLinkData extends LinkData {
    index: number;
}

export interface GraphLink {
    id: number;
    source: Node;
    target: Node;
    type?: NodeType;

    // Properties used by the force graph simulation
    index?: number;
}

export class Link
    extends GraphElement<LinkData, SimLinkData>
    implements GraphLink
{
    public source: Node;
    public target: Node;

    type?: NodeType;

    // These parameters will be added by the force graph implementation
    public index?: number;

    constructor(source?: Node, target?: Node, graph?: Graph) {
        super(0, graph);
        this.source = source;
        this.target = target;
    }

    /**
     * Id of the source node.
     * @returns Source id.
     */
    public get sourceId(): number {
        return this.source.id;
    }

    /**
     * Id of the target node.
     * @returns Target id.
     */
    public get targetId(): number {
        return this.target.id;
    }

    public delete() {
        return this.graph.deleteLink(this.id);
    }

    /**
     * Determines if the given node is part of the link structure.
     * @param node Node to check for.
     * @returns True, if node is either source or target node of link.
     */
    public contains(node: Node): boolean {
        return this.source === node || this.target === node;
    }

    public toJSONSerializableObject(): LinkData {
        return {
            source: this.source.id,
            target: this.target.id,
        };
    }

    public toHistorySerializableObject(): SimLinkData {
        return { ...this.toJSONSerializableObject(), index: this.index };
    }

    public toString(): string {
        return this.source.toString() + " -> " + this.target.toString();
    }

    public isInitialized(): boolean {
        return (
            super.isInitialized() &&
            this.source != undefined &&
            this.target != undefined
        );
    }

    public equals(other: GraphElement<LinkData, SimLinkData>): boolean {
        if (other.constructor != this.constructor) {
            return false;
        }

        const link = other as Link;

        return (
            link.sourceId === this.sourceId && link.targetId === this.targetId
        );
    }
}