    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]);
            }
        }
    }