/**
 * @augments EicComponent
 * @category EICUI/Components
 */
class Chart extends EicComponent {

    constructor(el, options) {
        super(el, options);
        if(this._constructed) return this;

        this.el.innerHTML = ``;

        this.plotter = ui.create(`<svg height="${this.options.height == 'auto' ? '100%': this.options.height}" width="100%"></svg>`)
		this.el.append(this.plotter);
		this.el.setAttribute(this.options.identifier, '')
		
    }

    set data(value) {
        this.options.data = value;
        this.redraw()
    }
    get data() { return this.options.data }

    redraw() {}

    clear() {
        this.options.data = [];
        this.redraw();
    }
}

/**
 * @augments EicComponent
 * @category EICUI/Components
 */
class BarChart extends Chart {
    
    constructor(el, options) {
        let defaultOptions = {
            title: '',
            subtitle: '',
            height: 'auto',
            showLabels: true,
            showValues: true,
            maxPlot: 0,
            data: [],
            identifier: 'eicbarchart'
        }    
        super(el, {...defaultOptions, ...(options || null)});
        if(this._constructed) return this;
        
        this.redraw();
    }

    redraw() {
        let series = this.options.data;
        let minValue = 0;
        let maxValue = 0;

        let w = this.position.width;
        let h = this.options.height == 'auto' ? this.position.height: this.options.height;        
		let bh = Math.max(h - 30, 0);

        this.plotter.innerHTML = '';

        if(this.options.title) {
            this.plotter.append(ui.create(`<svg class="title" x="0" y="0"><text x="50%" y="16" text-anchor="middle">${this.options.title}</text></g>`))
        }
        if(series.length > 0) {
            let start = this.options.maxPlot > 0 && this.options.maxPlot < series.length ? series.length - this.options.maxPlot: 0;
            
            for(let item of series) {
                if(item.value > maxValue) maxValue = item.value;
                if(item.value < minValue) minValue = item.value;
            }

            let index = 0;
            
            for(let i = start; i < series.length; i++) {
                let item = series[i];
                let g = ui.create(
                `<svg class="plot" ${item.severity || ''} x="${100 * index / (series.length - start)}%" y="0" width="${100 / (series.length - start)}%">
                    <rect x="10%" y="${bh + 24 - bh * (item.value / maxValue)}" width="80%" height="${bh * (item.value / maxValue)}">
                        ${item.tip ? `<title>${item.tip}</title>`: ''}
                    </rect>
                    ${this.options.showValue ? `<text x="50%" y="${bh + 12 - bh * (item.value / maxValue)}" text-anchor="middle">${item.value}</text>`: ''}
                    ${this.options.showLabel ? `<text x="50%" y="${h - 6}" text-anchor="middle">${item.label}</text>`: ''}    
                </svg>`);

                this.plotter.append(g);

                index++;
            }
        } 
    }

    add(data) {
        this.options.data.push(data);
        this.redraw();
    }
}

class LineChart extends Chart {

	constructor(el, options) {
        let defaultOptions = {
            title: '',
            subtitle: '',
            height: 'auto',
            showLabels: true,
            showValues: true,
            maxPlot: 0,
            data: [],
            identifier: 'eiclinechart'
        }
        super(el, {...defaultOptions, ...(options || null)});
        if(this._constructed) return this;

		this.redraw();
	}

	redraw() {
		let series = this.options.data;
        let minValue = 0;
        let maxValue = 0;

        let w = this.position.width;
        let h = this.options.height == 'auto' ? this.position.height: this.options.height;        
		let bh = Math.max(h - 30, 0);

        this.plotter.innerHTML = '';

        if(this.options.title) {
            this.plotter.append(ui.create(`<svg class="title" x="0" y="0"><text x="50%" y="16" text-anchor="middle">${this.options.title}</text></g>`))
        }

        if(series.length > 0) {
            let start = this.options.maxPlot > 0 && this.options.maxPlot < series.length ? series.length - this.options.maxPlot: 0;
            
			for(let serie of series) {
				for(let value of serie.data) {
					if(value > maxValue) maxValue = value;
					if(value < minValue) minValue = value;
				}
            }

			let dots = []

            for(let item of series) {
				let path = '';
				let index = 0;
				
				for(let value of item.data) {
					let x = 5 + ((this.position.width - 10) * index / (item.data.length - start - 1));
					let y = bh + 24 - (bh * (value / maxValue));
					path += `${index == 0 ? 'M': 'L'} ${x} ${y} `	
					let dot = ui.create(`<svg class="plot" ${item.severity || ''} x="0" y="0" width="100%" height="100%">
							<rect x="${x-4}" y="${y-4}" width="8" height="8" class="dot">
								<title>${this.options.labels[index]}: ${value}${item.unit ? item.unit: ''}</title>
							</rect>
						</svg>`)
					dots.push(dot)
					index++;
				}

                let line = ui.create(
					`<svg class="plot" ${item.severity || ''} x="0" y="0" width="100%" height="100%">
						<path d="${path}" />
					</svg>`);

                this.plotter.append(line);
            }

			for(let dot of dots) this.plotter.append(dot)
        } 
	}

	add(data) {
        this.options.data.push(data);
        this.redraw();
    }
}

class PieChart extends Chart {

	constructor(el, options) {
        let defaultOptions = {
            title: '',
            subtitle: '',
            height: 'auto',
            showLabels: true,
            showValues: true,
            maxPlot: 0,
            data: [],
            identifier: 'eicpiechart'
        }
        super(el, {...defaultOptions, ...(options || null)});
        if(this._constructed) return this;

		this.redraw();
	}

	redraw() {
		let series = this.options.data;
        let r = 40;
        let center = ['50%', '50%'];

        this.plotter.setAttribute( 'viewBox' , '0 0 100 100')
        this.plotter.innerHTML = '';

        if(this.options.title) {
            this.plotter.append(ui.create(`<svg class="title" x="0" y="0"><text x="50%" y="16" text-anchor="middle">${this.options.title}</text></g>`))
        }

        series = series.sort((a,b) => a.value < b.value ? -1 : 0) 

        if(series.length > 0) {
             let total = 0;
             for(let item of series) { total += item.value }
			// build the slices
            let o = 0;
            let l = r * Math.PI;
            for(let item of series) {
                if(item.value == 0) continue;
                o = o + Math.round(l * item.value / total)
                
                let g = ui.create(`<svg class="plot" x="0" y="0" width="100%" height="100%">
                    <circle class="slice" ${item.severity} stroke-dasharray="${o + ' ' + l}" stroke-width="${r}" cx="${center[0]}" cy="${center[1]}" r="${r / 2}">
                </svg>`);
                
                this.plotter.prepend(g);
            }
            // add pie background
            this.plotter.append( ui.create(`<svg class="plot" x="0" y="0" width="100%" height="100%"><circle class="back" cx="${center[0]}" cy="${center[1]}" r="${r / 1.7}" /></svg>`) );
        }
        //this.el.html(this.el.html())
	}

	add(data) {
        this.options.data.push(data);
        this.redraw();
    }
}

class PieTreeChart extends Chart {
	constructor(el, options) { 
        let defaultOptions = {
            title: '',
            subtitle: '',
            height: 'auto',
            showLabels: true,
            showValues: true,
            maxPlot: 0,
            data: [],
            sort: ((a,b) => a.value < b.value ? -1 : 1), // -1 : 1 => CCW   1 : -1 => CW 
            identifier: 'eicpiechart',
            center:['50%', '50%'],
            innerRadius: 35, // in % of the viewbox, thus max 50
            outerRadius: 50, // in % of the viewbox, thus max 50
            layersGap: 1, // in px
            angularGap: 1, // in deg
        }
        super(el, {...defaultOptions, ...(options || null)});
        if(this._constructed) return this;

        this.flatSeries = []
        if(typeof(this.options.sort)=='function') this.options.data.sort(this.options.sort)
        this.serieToFlat(this.options.data)

		this.redraw();
	}

    serieToFlat(serie){
        const cleanSerie = serie.map(obj => { const {children, ...cleanObj} = obj; return(cleanObj) }) // Remove children
        this.flatSeries.push(cleanSerie)

        if(serie.some(item => Array.isArray(item.children))){
            let layer = []
            for(let item of serie){
                if(Array.isArray(item.children)){
                    if(typeof(this.options.sort)=='function') item.children.sort(this.options.sort)
                    const ChildrenSum = item.children.reduce((acc, item) => acc + item.value, 0);
                    const cleanSubSerie = item.children.map(obj => { 
                        obj.value = (obj.value/ChildrenSum)*item.value 
                        obj.parentSeverity = item.severity=='inherit' ? item.parentSeverity : item.severity
                        return(obj) 
                    })
                    layer = [...layer, ...cleanSubSerie]
                } else { // simulate a single full layer
                    layer.push( { severity: '', hide:true, value: item.value },)
                }
            }
            this.serieToFlat(layer)
        }

    }

    redraw() { 
        this.plotter.setAttribute( 'viewBox' , '0 0 100 100')
        this.plotter.setAttribute('shape-rendering', 'geometricPrecision')
        this.plotter.innerHTML = '';

        if(this.options.title) {
            this.plotter.append(ui.create(`<svg class="title" x="0" y="0"><text x="50%" y="16" text-anchor="middle">${this.options.title}</text></g>`))
        }

        const nbLayers = this.flatSeries.length
        const bandsWidth = Math.floor((this.options.outerRadius - this.options.innerRadius)/nbLayers)
        for(let layer=0; layer<nbLayers; layer++){
            this.drawLayer(this.flatSeries[layer], this.options.innerRadius+(layer*bandsWidth), this.options.innerRadius+((layer+1)*bandsWidth)-this.options.layersGap )
        }
        if(typeof(this.options.click)=='function'){
            this.plotter.querySelectorAll('circle.slice').forEach(el => el.addEventListener('click', this.options.click))
        }
	}	
    
    drawLayer(serie, innerRadius, outerRadius){
        let sectorWidth = outerRadius>innerRadius ? outerRadius - innerRadius : 1
        let radius = innerRadius + (sectorWidth/2)
        if(serie.length > 0) {
            let total = 0;
            for(let item of serie) { total += item.value }
            // Ideal dash pattern in our case : [ Space(start angle) Mark(value) Space(complement) ]
            // But dasharray must have an even count, thus 4, and it always starts with a mark. 
            // Therefore: [ Mark(1) Space(start angle) Mark(value) Space(complement) ] then shift left by 1 (thus hide mark(1)) with stroke-dashoffset.
            // This is much cleaner than the cumulative & draw-over technique which quickly creates graphical artefacts (dirt at borders) on non-trivial drawings.
            const circonf = (2 * radius * Math.PI) 
            let offset = 0
            for(let item of serie) {
                if(item.value == 0) continue;
                const dashLength = circonf * item.value / total
                let markup = `<svg class="plot" x="0" y="0" width="100%" height="100%">`
                markup += `<circle class="slice" ${item.hide ? 'stroke="#FFF"' : item.severity=='inherit' ? item.parentSeverity : item.severity} 
                                stroke-dasharray="1 ${offset} ${dashLength} ${circonf-dashLength-offset}" 
                                stroke-dashoffset="1" 
                                stroke-width="${sectorWidth}" 
                                ${item.brightness ? 'style="filter: brightness('+item.brightness+');"' : ''}
                                cx="${this.options.center[0]}" 
                                cy="${this.options.center[1]}" 
                                r="${radius}"
                                ${(typeof(item.dataset)=='object') ? Object.entries(item.dataset).map(([k, v]) => `data-${k}="${v}"`).join(' ') : ''}
                                >                        
                `
                if(item.title) markup += `<title>${item.title}</title>`
                markup += '</circle></svg>'
                offset += dashLength  
                this.plotter.prepend(ui.create(markup))
            }

            if(this.options.angularGap>0){
                offset = 0
                let markup = `<svg class="plot" x="0" y="0" width="100%" height="100%">`
                for(let item of serie) {
                    if(item.value == 0) continue
                    const dashLength = circonf * item.value / total
                    const offsetAngle = offset / radius
                    const x1 = parseInt(this.options.center[0]) + (innerRadius * Math.cos(offsetAngle))
                    const y1 = parseInt(this.options.center[1]) + (innerRadius * Math.sin(offsetAngle))
                    const x2 = parseInt(this.options.center[0]) + (outerRadius * Math.cos(offsetAngle) * 1.1) 
                    const y2 = parseInt(this.options.center[1]) + (outerRadius * Math.sin(offsetAngle) * 1.1)
                    markup += `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="#FFF" stroke-width="${this.options.angularGap}"/>`
                    offset += dashLength 
                } 
                markup += '</svg>'
                this.plotter.append(ui.create(markup))
            }
        }
    }

	add(data) {
        this.options.data.push(data);
        this.redraw();
    }
}

