/**
 * Class representing a generic shape.
 */
class Shape {
    // Static property to keep track of the global index for different shapes.
    static globalIndex = 0

    /**
     * Creates a new instance of the Shape class.
     *
     * @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas.
     * @param {any} value - The value associated with the shape.
     * @param {number} colId - The column ID associated with the shape.
     * @param {number} x - The x-coordinate of the shape.
     * @param {number} y - The y-coordinate of the shape.
     */
    constructor(ctx, value, colId, x, y) {
        this.ctx = ctx
        this.value = value
        this.colId = colId
        this.x = x
        this.y = y

        // Assign a unique index to the shape
        this.index = Shape.globalIndex++
    }

    /**
    * Abstract method for drawing the shape.
    * This method should be implemented by subclasses.
    */
    draw() { }
}

/**
 * Class representing a rectangle shape, extending the Shape class.
 */
class Rectangle extends Shape {
    /**
     * Creates a new instance of the Rectangle class.
     *
     * @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas.
     * @param {any} value - The value associated with the rectangle.
     * @param {number} colId - The column ID associated with the rectangle.
     * @param {number} x - The x-coordinate of the top-left corner of the rectangle.
     * @param {number} y - The y-coordinate of the top-left corner of the rectangle.
     * @param {number} w - The width of the rectangle.
     * @param {number} h - The height of the rectangle.
     */
    constructor(ctx, value, colId, x, y, w, h) {
        super(ctx, value, colId, x, y) // Call the constructor of the Shape class.
        this.w = w // The width of the rectangle.
        this.h = h // The height of the rectangle.
    }

    /**
     * Draws the rectangle on the canvas.
     *
     * @param {CanvasRenderingContext2D} [ctx=this.ctx] - The 2D drawing context for the canvas.
     */
    draw(ctx = this.ctx) {
        ctx.beginPath()
        // Define the rectangle
        ctx.rect(this.x, this.y, this.w, this.h)
        // Fill the rectangle with the current fill style
        ctx.fill()
    }

    /**
     * Returns the center coordinates of the rectangle.
     *
     * @returns {Object} Object containing x and y coordinates of the center.
     */
    getCenter() {
        return {
            x: this.x + this.w / 2,
            y: this.ctx.canvas.clientHeight / 2
        }
    }
    
    /**
     * Retrieves the position for displaying a tooltip.
     * @returns {Object} An object containing x and y coordinates for the tooltip position.
     */
    getTooltipPos() {
        return {
            right: {
                x: this.x + this.w,
                y: this.y
            },
            left: {
                x: this.x,
                y: this.y
            }
        }
    }
}

/**
 * Class representing a circle shape, extending the Shape class.
 */
class Circle extends Shape {
    /**
     * Creates a new instance of the Circle class.
     *
     * @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas.
     * @param {any} value - The value associated with the circle.
     * @param {number} colId - The column ID associated with the circle.
     * @param {number} x - The x-coordinate of the center of the circle.
     * @param {number} y - The y-coordinate of the center of the circle.
     * @param {number} r - The radius of the circle.
     */
    constructor(ctx, value, colId, x, y, r) {
        super(ctx, value, colId, x, y) // Call the constructor of the Shape class.
        this.r = r // The radius of the circle.
    }

    /**
     * Draws the circle on the canvas
     *
     * @param {CanvasRenderingContext2D} [ctx=this.ctx] - The 2D drawing context for the canvas
     */
    draw(ctx = this.ctx) {
        ctx.beginPath()
        // Define the circle
        ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)
        // Fill the circle with the current fill style
        ctx.fill()
    }

    /**
     * Returns the center coordinates of the circle.
     *
     * @returns {Object} Object containing x and y coordinates of the center.
     */
    getCenter() {
        return {
            x: this.x,
            y: this.y
        }
    }

    /**
     * Retrieves the position for displaying a tooltip.
     * @returns {Object} An object containing x and y coordinates for the tooltip position.
     */
    getTooltipPos() {
        return {
            right: {
                x: this.x + this.r / 2,
                y: this.y
            },
            left: {
                x: this.x - this.r / 2,
                y: this.y
            }
        }
    }
}

/**
 * Class representing a pie slice shape, extending the Circle class
 */
class PieSlice extends Circle {
    /**
     * Creates a new instance of the PieSlice class
     *
     * @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas
     * @param {any} value - The value associated with the pie slice
     * @param {number} colId - The column ID associated with the pie slice
     * @param {number} x - The x-coordinate of the center of the pie slice
     * @param {number} y - The y-coordinate of the center of the pie slice
     * @param {number} r - The radius of the pie slice
     * @param {number} sAngle - The starting angle of the pie slice in radians
     * @param {number} eAngle - The ending angle of the pie slice in radians
     */
    constructor(ctx, value, colId, x, y, r, sAngle, eAngle) {
        super(ctx, value, colId, x, y, r) // Call the constructor of the Circle class
        this.sAngle = sAngle // The starting angle of the pie slice
        this.eAngle = eAngle // The ending angle of the pie slice
    }

    /**
     * Draws the pie slice on the canvas
     *
     * @param {CanvasRenderingContext2D} [ctx=this.ctx] - The 2D drawing context for the canvas
     */
    draw(ctx = this.ctx) {
        ctx.beginPath()
        // Move to the center of the circle
        ctx.moveTo(this.x, this.y)
        // Define the pie slice
        ctx.arc(this.x, this.y, this.r, this.sAngle, this.eAngle)
        // Draw a line back to the center
        ctx.lineTo(this.x, this.y)
        // Fill the pie slice with the current fill style
        ctx.fill()
    }

    /**
     * Returns the center coordinates of the pie slice.
     *
     * @returns {Object} Object containing x and y coordinates of the center.
     */
    getCenter() {
        return {
            x: this.x,
            y: this.y
        }
    }

    /**
     * Retrieves the position for displaying a tooltip.
     * @returns {Object} An object containing x and y coordinates for the tooltip position.
     */
    getTooltipPos() {
        let angle = (this.sAngle + this.eAngle) / 2
        let pos = {
            x: this.x + Math.cos(angle) * this.r,
            y: this.y + Math.sin(angle) * this.r
        }
        return {
            right: pos,
            left: pos
        }
    }
}

/**
 * Class representing a donut slice shape, extending the PieSlice class
 */
class DonutSlice extends PieSlice {
    /**
     * Creates a new instance of the DonutSlice class
     *
     * @param {CanvasRenderingContext2D} ctx - The 2D drawing context for the canvas
     * @param {any} value - The value associated with the donut slice
     * @param {number} colId - The column ID associated with the donut slice
     * @param {number} x - The x-coordinate of the center of the donut slice
     * @param {number} y - The y-coordinate of the center of the donut slice
     * @param {number} r - The outer radius of the donut slice
     * @param {number} sAngle - The starting angle of the donut slice in radians
     * @param {number} eAngle - The ending angle of the donut slice in radians
     * @param {number} r2 - The inner radius of the donut slice
     */
    constructor(ctx, value, colId, x, y, r, sAngle, eAngle, r2) {
        super(ctx, value, colId, x, y, r, sAngle, eAngle) // Call the constructor of the PieSlice class
        this.r2 = r2 // The inner radius of the donut slice
    }

    /**
     * Draws the donut slice on the canvas
     *
     * @param {CanvasRenderingContext2D} [ctx=this.ctx] - The 2D drawing context for the canvas
     */
    draw(ctx = this.ctx) {
        ctx.beginPath()
        // Move to the starting point of the slice using polar coordinates
        ctx.moveTo(this.x + this.r2 * Math.cos(this.sAngle), this.y + this.r2 * Math.sin(this.sAngle))
        // Draw the outer arc of the donut slice
        ctx.arc(this.x, this.y, this.r, this.sAngle, this.eAngle, false)
        // Draw the inner arc of the donut slice in reverse order to create a hole
        ctx.arc(this.x, this.y, this.r2, this.eAngle, this.sAngle, true)
        // Fill the donut slice with the current fill style
        ctx.fill()
    }

    /**
     * Returns the center coordinates of the donut slice.
     *
     * @returns {Object} Object containing x and y coordinates of the center.
     */
    getCenter() {
        return {
            x: this.x,
            y: this.y
        }
    }
}
