import PolyLinePoint from './PolyLinePoint';

/** @module svgObjects.PolyLinePoints */

/**
 * array-like structure used in {@link PolyLinePoints}
 */
class SmartArray {
    /**
     * @param {Array} [arr] if set, initialized SmartArray will contain these values
     */
    constructor(arr) {
        if (arr !== undefined) {
            this.arr = arr;
        } else {
            this.arr = [];
        }
    }

    /**
     * get a deep copy of this array
     * @return {SmartArray}
     */
    copy() {
        return SmartArray($.extend(true, [], this.arr));
    }

    /**
     * append an item to the array
     * @param item new item that will be appended to the array
     */
    append(item) {
        return this.addWithIndex(item, this.arr.length);
    }

    /**
     * prepend an item to the array
     * @param item new item that will be prepended to the array
     */
    prepend(item) {
        return this.addWithIndex(item, 0);
    }

    /**
     * add a new item at the specified index, move all following items
     * @param item new item that will be added at the specified index
     * @param {number} index index of this item
     */
    addWithIndex(item, index) {
        for (let i = this.arr.length; i > index; --i) {
            this.arr[i] = this.arr[i - 1];
        }
        this.arr[index] = item;
        return this; // to enable chaining of append / preppend / addWithIndex commands
    }

    /**
     * get length of the array
     * @return {number}
     */
    get length() {
        return this.arr.length;
    }

    /**
     * get item by index
     * @param  {number} index index of the item
     * @return contents of the array on the specified index
     */
    getItem(index) {
        return this.arr[index];
    }

    /**
     * @return last element of the array
     */
    get last() {
        if (this.length !== 0) {
            return this.arr[this.length - 1];
        } else {
            return false;
        }
    }

    /**
     * @return first element of the array
     */
    get first() {
        if (this.length !== 0) {
            return this.arr[0];
        } else {
            return false;
        }
    }

    /**
     * remove an item from the array by index
     * @param  {number} index index of the item that will be removed
     */
    remove(index) {
        let length = this.length;

        for (let i = index; i < length; ++i) {
            this.arr[i] = this.arr[i + 1];
        }
        this.arr.pop();
    }
}

/**
 * points of the {@link PolyLine}
 * @extends SmartArray
 */
export default class PolyLinePoints extends SmartArray {
    /**
     * @param {Array} [arr] array containing instances of {@link PolyLinePoint}
     */
    constructor(arr) {
        super(arr);
    }

    /**
     * get a deep copy of this object
     * @return {PolyLinePoints}
     */
    copy() {
        return new PolyLinePoints($.extend(true, [], this.arr));
    }

    /**
     * append a point
     * @param  {PolyLinePoint} point a new point
     */
    append(point) {
        // call inherited function to handle the appending
        super.append(point);

        // if the second to last point is unnecessary, remove it
        let length = this.length;
        if (
            length >= 3 &&
            ((this.getItem(length - 3).x === this.getItem(length - 2).x &&
                this.getItem(length - 2).x === this.getItem(length - 1).x) ||
                (this.getItem(length - 3).y === this.getItem(length - 2).y &&
                    this.getItem(length - 2).y === this.getItem(length - 1).y))
        ) {
            this.remove(length - 2);
        }

        // return this element (to allow chaining)
        return this;
    }

    /**
     * parse PolyLine from string
     * @param  {string} string string in the PolyLine format (`x1,y1 x2,y2, x3,y3`)
     * @return {PolyLinePoints} a new instance of {@link PolyLinePoints} created by parsing the string
     */
    static parseFromString(string) {
        let pointStrings = string.split(' ');
        let points = new PolyLinePoints();

        for (let i = 0; i < pointStrings.length; ++i) {
            points.append(PolyLinePoint.parseFromString(pointStrings[i]));
        }

        return points;
    }

    /**
     * get a string representation of this PolyLine
     * @return {string} string in the PolyLine format (`x1,y1 x2,y2, x3,y3`)
     */
    get string() {
        let string = '';
        for (let i = 0; i < this.length; ++i) {
            if (i !== 0) {
                string += ' ';
            }
            string += this.arr[i].string;
        }
        return string;
    }

    /**
     * wrapper for foreach on the PolyLine points
     * @param  {Function} func function that will be called on each element
     */
    forEach(func) {
        for (let i = 0; i < this.arr.length; ++i) {
            func(this.arr[i]);
        }
    }
}
