import * as d3 from 'd3';
import { ChartConfiguration } from './chart-configuration';

export abstract class Chart {
    public config: ChartConfiguration;
    public data: any[];
    public width: number;
    public height: number;
    protected svg: any;
    protected chart: any;



    /**
     * Class constructor with basic chart configuration
     */
    constructor(_config: ChartConfiguration, _data?: any[]) {

        const vis = this;

        // Configuration object with defaults
        vis.config = new ChartConfiguration(_config.parentSVGElement,
            _config.containerSize || vis.getDefaultContainerSize(),
            _config.margin || vis.getDefaultMargins());

        vis.data = _data || [];
        vis.initChart();
    }

    /**
     * Returns default margin values.
     */
    protected abstract getDefaultMargins(): { top: number; right: number; bottom: number; left: number };

    /**
     * Returns default container size (width and height).
     */
    protected abstract getDefaultContainerSize(): { width: number; height: number };

    /**
     * Initialize chart element.
     */
    private initChart() {
        const vis = this;

        // Calculate inner chart size.
        vis.width = vis.config.containerSize!.width - vis.config.margin!.left - vis.config.margin!.right;
        vis.height = vis.config.containerSize!.height - vis.config.margin!.top - vis.config.margin!.bottom;

        // Define size of SVG drawing area
        vis.svg = d3.select(vis.config.parentSVGElement)
            .attr('width', vis.config.containerSize!.width)
            .attr('height', vis.config.containerSize!.height);

        // Append group element that will contain our actual chart 
        // and position it according to the given margin config
        vis.chart = vis.svg.append('g')
            .attr('transform', `translate(${vis.config.margin!.left},${vis.config.margin!.top})`);
    }

    /**
     * Initialize scales/axes and append static elements, such as axis titles.
     */
    protected abstract initVis(): void;
    /**
     * This function contains the D3 code for binding data to visual elements.
     * We call this function every time the data or configurations change.
     */
    protected abstract renderVis(): void;
    /**
     * This function contains all the code to prepare the data before we render it.
     */
    public abstract updateVis(): void;

}
