<commit_before>///<reference path="../../typings/c3/c3.d.ts"/>
///<reference path="../../typings/d3/d3.d.ts"/>
///<reference path="../../typings/lodash/lodash.d.ts"/>
///<reference path="../../typings/react/react.d.ts"/>
///<reference path="../../typings/weave/weavejs.d.ts"/>

import {IVisToolProps} from "./IVisTool";
import AbstractC3Tool from "./AbstractC3Tool";
import * as _ from "lodash";
import * as d3 from "d3";
import FormatUtils from "../utils/FormatUtils";
import * as React from "react";
import * as c3 from "c3";
import {ChartConfiguration, ChartAPI} from "c3";
import StandardLib from "../utils/StandardLib";

import IQualifiedKey = weavejs.api.data.IQualifiedKey;
import DynamicColumn = weavejs.data.column.DynamicColumn;
import ILinkableHashMap = weavejs.api.core.ILinkableHashMap;
import IAttributeColumn = weavejs.api.data.IAttributeColumn;
import LinkableHashMap = weavejs.core.LinkableHashMap;
import AlwaysDefinedColumn = weavejs.data.column.AlwaysDefinedColumn;
import FilteredKeySet = weavejs.data.key.FilteredKeySet;
import ColorRamp = weavejs.util.ColorRamp;
import LinkableString = weavejs.core.LinkableString;
import LinkableBoolean = weavejs.core.LinkableBoolean;

declare type Record = {
    id: IQualifiedKey,
    sort: number,
    heights: { xLabel: string } & {[columnName:string]: number},
	xLabel: number,
    yLabel: number
};

declare type RecordHeightsFormat<T> = { xLabel: T } & {[columnName:string]: T};

const GROUP:string = 'group';
const STACK:string = 'stack';
const PERCENT_STACK:string = 'percentStack';

export default class WeaveC3Barchart extends AbstractC3Tool
{


    heightColumns:LinkableHashMap = Weave.linkableChild(this, LinkableHashMap);
    labelColumn:DynamicColumn = Weave.linkableChild(this, DynamicColumn);
    sortColumn:DynamicColumn = Weave.linkableChild(this, DynamicColumn);
    colorColumn:AlwaysDefinedColumn = Weave.linkableChild(this, new AlwaysDefinedColumn("#C0CDD1"));
    chartColors:ColorRamp = Weave.linkableChild(this, new ColorRamp(ColorRamp.getColorRampByName("Paired")));
    groupingMode:LinkableString = Weave.linkableChild(this, new LinkableString(STACK, this.verifyGroupingMode))
    horizontalMode:LinkableBoolean = Weave.linkableChild(this, new LinkableBoolean(true));
    showValueLabels:LinkableBoolean = Weave.linkableChild(this, new LinkableBoolean(true));

    private showXAxisLabel:LinkableBoolean = new LinkableBoolean(false);

    private verifyGroupingMode(mode:string):boolean
	{
		return [GROUP, STACK, PERCENT_STACK].indexOf(mode) >= 0;
	}

    get yLabelColumn():IAttributeColumn {
        return this.heightColumns.getObjects()[0] || this.sortColumn;
    }
	
    private RECORD_FORMAT = {
		id: IQualifiedKey,
		sort: this.sortColumn,
        color: this.colorColumn.getInternalColumn(),
        yLabel: this.yLabelColumn,
		xLabel: this.labelColumn,
        heights: {} as RecordHeightsFormat<IAttributeColumn>
	};

    private RECORD_DATATYPE = {
        sort: Number,
        color: String,
        yLabel: Number,
		xLabel: Number,
        heights: {} as RecordHeightsFormat<new ()=>(String|Number)>
	};

    private keyToIndex: Map<IQualifiedKey, number>;
    private indexToKey: Map<number, IQualifiedKey>;
    private xAxisValueToLabel:{[value:number]: string};
    private yAxisValueToLabel:{[value:number]: string};

    private yLabelColumnDataType:string;
    private heightColumnNames:string[];
    private heightColumnsLabels:string[];
    protected c3Config:ChartConfiguration;
    protected c3ConfigYAxis:c3.YAxisConfiguration;
    private records:Record[];
    protected chart:ChartAPI;

    private busy:boolean;
    private dirty:boolean;

    constructor(props:IVisToolProps)
    {
        super(props);

        Weave.getCallbacks(this.selectionFilter).addGroupedCallback(this, this.updateStyle);
        Weave.getCallbacks(this.probeFilter).addGroupedCallback(this, this.updateStyle);

        Weave.getCallbacks(this).addGroupedCallback(this, this.validate, true);

        this.filteredKeySet.keyFilter.targetPath = ['defaultSubsetKeyFilter'];
		this.selectionFilter.targetPath = ['defaultSelectionKeySet'];
		this.probeFilter.targetPath = ['defaultProbeKeySet'];

        this.keyToIndex = new Map<IQualifiedKey, number>();
        this.indexToKey = new Map<number, IQualifiedKey>();
        this.yAxisValueToLabel = {};
        this.xAxisValueToLabel = {};
        this.validate = _.debounce(this.validate.bind(this), 30);

        this.c3Config = {
            size: {
                width: this.props.style.width,
                height: this.props.style.height
            },
            padding: {
                top: this.margin.top.value,
                bottom: 0, // use axis.x.height instead
                left: this.margin.left.value,
                right: this.margin.right.value
            },
            data: {
                json: [],
                type: "bar",
                xSort: false,
                names: {},
                selection: {
                    enabled: true,
                    multiple: true,
                    draggable: true

                },
                labels: {
                    format: (v, id, i, j) => {
                        if(this.showValueLabels.value) {
                            return v;
                        } else {
                            return "";
                        }
                    }
                },
                order: null,
                color: (color:string, d:any):string => {
                    if(this.heightColumnNames.length === 1 && d && d.hasOwnProperty("index")) {
						var qKey:IQualifiedKey = this.indexToKey.get(d.index);
						return this.colorColumn.getValueFromKey(qKey, String);
                    } else {
                        return color;
                    }
                },
                onclick: (d:any) => {
                },
                onselected: (d:any) => {
                    if(d && d.hasOwnProperty("index")) {
						if(this.selectionKeySet)
							this.selectionKeySet.addKeys([this.indexToKey.get(d.index)]);
                    }
                },
                onunselected: (d:any) => {
                    if(d && d.hasOwnProperty("index")) {
                        if(this.selectionKeySet)
							this.selectionKeySet.removeKeys([this.indexToKey.get(d.index)]);
                    }
                },
                onmouseover: (d:any) => {
                    if(d && d.hasOwnProperty("index")) {
						if(this.probeKeySet)
                        	this.probeKeySet.replaceKeys([]);;
                        
						var columnNamesToValue:{[columnName:string] : string|number } = {};
                        var columnNamesToColor:{[columnName:string] : string} = {};
                        
						var qKey:IQualifiedKey = this.indexToKey.get(d.index);
						
						var columns = this.heightColumns.getObjects();
						for(var column of columns) {
							var columnName:string = column.getMetadata("title");
							var color = this.chartColors.getColorFromNorm(d.index / (columns.length - 1));
							columnNamesToValue[columnName] = column.getValueFromKey(qKey, Number);
							columnNamesToColor[columnName] = "#" + weavejs.util.StandardLib.numberToBase(color, 16, 6);
						}

                        var title:string = this.labelColumn.getValueFromKey(qKey, String);
						
						if(this.probeKeySet)
							this.probeKeySet.replaceKeys([this.indexToKey.get(d.index)]);
						<commit_msg>fixed tooltip colors in barchart #WEAVE-104<commit_after>///<reference path="../../typings/c3/c3.d.ts"/>
///<reference path="../../typings/d3/d3.d.ts"/>
///<reference path="../../typings/lodash/lodash.d.ts"/>
///<reference path="../../typings/react/react.d.ts"/>
///<reference path="../../typings/weave/weavejs.d.ts"/>

import {IVisToolProps} from "./IVisTool";
import AbstractC3Tool from "./AbstractC3Tool";
import * as _ from "lodash";
import * as d3 from "d3";
import FormatUtils from "../utils/FormatUtils";
import * as React from "react";
import * as c3 from "c3";
import {ChartConfiguration, ChartAPI} from "c3";
import StandardLib from "../utils/StandardLib";

import IQualifiedKey = weavejs.api.data.IQualifiedKey;
import DynamicColumn = weavejs.data.column.DynamicColumn;
import ILinkableHashMap = weavejs.api.core.ILinkableHashMap;
import IAttributeColumn = weavejs.api.data.IAttributeColumn;
import LinkableHashMap = weavejs.core.LinkableHashMap;
import AlwaysDefinedColumn = weavejs.data.column.AlwaysDefinedColumn;
import FilteredKeySet = weavejs.data.key.FilteredKeySet;
import ColorRamp = weavejs.util.ColorRamp;
import LinkableString = weavejs.core.LinkableString;
import LinkableBoolean = weavejs.core.LinkableBoolean;

declare type Record = {
    id: IQualifiedKey,
    sort: number,
    heights: { xLabel: string } & {[columnName:string]: number},
	xLabel: number,
    yLabel: number
};

declare type RecordHeightsFormat<T> = { xLabel: T } & {[columnName:string]: T};

const GROUP:string = 'group';
const STACK:string = 'stack';
const PERCENT_STACK:string = 'percentStack';

export default class WeaveC3Barchart extends AbstractC3Tool
{


    heightColumns:LinkableHashMap = Weave.linkableChild(this, new LinkableHashMap());
    labelColumn:DynamicColumn = Weave.linkableChild(this, DynamicColumn);
    sortColumn:DynamicColumn = Weave.linkableChild(this, DynamicColumn);
    colorColumn:AlwaysDefinedColumn = Weave.linkableChild(this, new AlwaysDefinedColumn("#C0CDD1"));
    chartColors:ColorRamp = Weave.linkableChild(this, new ColorRamp(ColorRamp.getColorRampByName("Paired")));
    groupingMode:LinkableString = Weave.linkableChild(this, new LinkableString(STACK, this.verifyGroupingMode))
    horizontalMode:LinkableBoolean = Weave.linkableChild(this, new LinkableBoolean(true));
    showValueLabels:LinkableBoolean = Weave.linkableChild(this, new LinkableBoolean(true));

    private showXAxisLabel:LinkableBoolean = new LinkableBoolean(false);

    private verifyGroupingMode(mode:string):boolean
	{
		return [GROUP, STACK, PERCENT_STACK].indexOf(mode) >= 0;
	}

    get yLabelColumn():IAttributeColumn {
        return this.heightColumns.getObjects()[0] || this.sortColumn;
    }
	
    private RECORD_FORMAT = {
		id: IQualifiedKey,
		sort: this.sortColumn,
        color: this.colorColumn.getInternalColumn(),
        yLabel: this.yLabelColumn,
		xLabel: this.labelColumn,
        heights: {} as RecordHeightsFormat<IAttributeColumn>
	};

    private RECORD_DATATYPE = {
        sort: Number,
        color: String,
        yLabel: Number,
		xLabel: Number,
        heights: {} as RecordHeightsFormat<new ()=>(String|Number)>
	};

    private keyToIndex: Map<IQualifiedKey, number>;
    private indexToKey: Map<number, IQualifiedKey>;
    private xAxisValueToLabel:{[value:number]: string};
    private yAxisValueToLabel:{[value:number]: string};

    private yLabelColumnDataType:string;
    private heightColumnNames:string[];
    private heightColumnsLabels:string[];
    protected c3Config:ChartConfiguration;
    protected c3ConfigYAxis:c3.YAxisConfiguration;
    private records:Record[];
    protected chart:ChartAPI;

    private busy:boolean;
    private dirty:boolean;

    constructor(props:IVisToolProps)
    {
        super(props);

        Weave.getCallbacks(this.selectionFilter).addGroupedCallback(this, this.updateStyle);
        Weave.getCallbacks(this.probeFilter).addGroupedCallback(this, this.updateStyle);

        Weave.getCallbacks(this).addGroupedCallback(this, this.validate, true);

        this.filteredKeySet.keyFilter.targetPath = ['defaultSubsetKeyFilter'];
		this.selectionFilter.targetPath = ['defaultSelectionKeySet'];
		this.probeFilter.targetPath = ['defaultProbeKeySet'];

        this.keyToIndex = new Map<IQualifiedKey, number>();
        this.indexToKey = new Map<number, IQualifiedKey>();
        this.yAxisValueToLabel = {};
        this.xAxisValueToLabel = {};
        this.validate = _.debounce(this.validate.bind(this), 30);

        this.c3Config = {
            size: {
                width: this.props.style.width,
                height: this.props.style.height
            },
            padding: {
                top: this.margin.top.value,
                bottom: 0, // use axis.x.height instead
                left: this.margin.left.value,
                right: this.margin.right.value
            },
            data: {
                json: [],
                type: "bar",
                xSort: false,
                names: {},
                selection: {
                    enabled: true,
                    multiple: true,
                    draggable: true

                },
                labels: {
                    format: (v, id, i, j) => {
                        if(this.showValueLabels.value) {
                            return v;
                        } else {
                            return "";
                        }
                    }
                },
                order: null,
                color: (color:string, d:any):string => {
                    if(this.heightColumnNames.length === 1 && d && d.hasOwnProperty("index")) {
						var qKey:IQualifiedKey = this.indexToKey.get(d.index);
						return this.colorColumn.getValueFromKey(qKey, String);
                    } else {
                        return color;
                    }
                },
                onclick: (d:any) => {
                },
                onselected: (d:any) => {
                    if(d && d.hasOwnProperty("index")) {
						if(this.selectionKeySet)
							this.selectionKeySet.addKeys([this.indexToKey.get(d.index)]);
                    }
                },
                onunselected: (d:any) => {
                    if(d && d.hasOwnProperty("index")) {
                        if(this.selectionKeySet)
							this.selectionKeySet.removeKeys([this.indexToKey.get(d.index)]);
                    }
                },
                onmouseover: (d:any) => {
                    if(d && d.hasOwnProperty("index")) {
						if(this.probeKeySet)
                        	this.probeKeySet.replaceKeys([]);;
                        
						var columnNamesToValue:{[columnName:string] : string|number } = {};
                        var columnNamesToColor:{[columnName:string] : string} = {};
                        
						var qKey:IQualifiedKey = this.indexToKey.get(d.index);
						
						var columns = this.heightColumns.getObjects() as IAttributeColumn[];
						for(var index in columns) {
							var column:IAttributeColumn = columns[index]; 
							var columnName:string = column.getMetadata("title");
							var color = this.chartColors.getColorFromNorm(Number(index) / (columns.length - 1));
							columnNamesToValue[columnName] = column.getValueFromKey(qKey, Number);
							columnNamesToColor[columnName] = "#" + weavejs.util.StandardLib.numberToBase(color, 16, 6);
						}

                        var title:string = this.labelColumn.getValueFromKey(qKey, String);
						
						if(this.probeKeySet)
							this.probeKeySet.replaceKeys([this.indexToKey.get(d.index)]);
						
{'fraction_non_alphanumeric': 0.12122014159994707, 'fraction_numerical': 0.004036260173360682, 'mean_word_length': 2.431101021566402, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5981395', 'n_tokens_mistral': 4671, 'n_tokens_neox': 4292, 'n_words': 753}