/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import SeriesModel from '../../model/Series';
import { trim, isArray, each, reduce } from 'zrender/src/core/util';
import { DimensionName, DimensionType, ColorString } from '../../util/types';
import {
    retrieveVisualColorForTooltipMarker,
    TooltipMarkupBlockFragment,
    createTooltipMarkup,
    TooltipMarkupSection
} from './tooltipMarkup';
import { retrieveRawValue } from '../../data/helper/dataProvider';
import { isNameSpecified } from '../../util/model';


export function defaultSeriesFormatTooltip(opt: {
    series: SeriesModel;
    dataIndex: number;
    // `multipleSeries` means multiple series displayed in one tooltip,
    // and this method only return the part of one series.
    multipleSeries: boolean;
}): TooltipMarkupSection {
    const series = opt.series;
    const dataIndex = opt.dataIndex;
    const multipleSeries = opt.multipleSeries;

    const data = series.getData();
    const tooltipDims = data.mapDimensionsAll('defaultedTooltip');
    const tooltipDimLen = tooltipDims.length;
    const value = series.getRawValue(dataIndex) as any;
    const isValueArr = isArray(value);
    const markerColor = retrieveVisualColorForTooltipMarker(series, dataIndex);

    // Complicated rule for pretty tooltip.
    let inlineValue;
    let inlineValueType: DimensionType | DimensionType[];
    let subBlocks: TooltipMarkupBlockFragment[];
    let sortParam: unknown;
    if (tooltipDimLen > 1 || (isValueArr && !tooltipDimLen)) {
        const formatArrResult = formatTooltipArrayValue(value, series, dataIndex, tooltipDims, markerColor);
        inlineValue = formatArrResult.inlineValues;
        inlineValueType = formatArrResult.inlineValueTypes;
        subBlocks = formatArrResult.blocks;
        // Only support tooltip sort by the first inline value. It's enough in most cases.
        sortParam = formatArrResult.inlineValues[0];
    }
    else if (tooltipDimLen) {
        const dimInfo = data.getDimensionInfo(tooltipDims[0]);
        sortParam = inlineValue = retrieveRawValue(data, dataIndex, tooltipDims[0]);
        inlineValueType = dimInfo.type;
    }
    else {
        sortParam = inlineValue = isValueArr ? value[0] : value;
    }

    // Do not show generated series name. It might not be readable.
    const seriesNameSpecified = isNameSpecified(series);
    const seriesName = seriesNameSpecified && series.name || '';
    const itemName = data.getName(dataIndex);
    const inlineName = multipleSeries ? seriesName : itemName;

    return createTooltipMarkup('section', {
        header: seriesName,
        // When series name is not specified, do not show a header line with only '-'.
        // This case always happens in tooltip.trigger: 'item'.
        noHeader: multipleSeries || !seriesNameSpecified,
        sortParam: sortParam,
        blocks: [
            createTooltipMarkup('nameValue', {
                markerType: 'item',
                markerColor: markerColor,
                // Do not mix display seriesName and itemName in one tooltip,
                // which might confuses users.
                name: inlineName,
                // name dimension might be auto assigned, where the name might
                // be not readable. So we check trim here.
                noName: !trim(inlineName),
                value: inlineValue,
                valueType: inlineValueType,
                dataIndex
            })
        ].concat(subBlocks || [] as any)
    });
}

function formatTooltipArrayValue(
    value: unknown[],
    series: SeriesModel,
    dataIndex: number,
    tooltipDims: DimensionName[],
    colorStr: ColorString
): {
    inlineValues: unknown[];
    inlineValueTypes: DimensionType[];
    blocks: TooltipMarkupBlockFragment[];
} {
    // check: category-no-encode-has-axis-data in dataset.html
    const data = series.getData();
    const isValueMultipleLine = reduce(value, function (isValueMultipleLine, val, idx) {
        const dimItem = data.getDimensionInfo(idx);
        return isValueMultipleLine = isValueMultipleLine
            || (dimItem && dimItem.tooltip !== false && dimItem.displayName != null);
    }, false);

    const inlineValues: unknown[] = [];
    const inlineValueTypes: DimensionType[] = [];
    const blocks: TooltipMarkupBlockFragment[] = [];

    tooltipDims.length
        ? each(tooltipDims, function (dim) {
            setEachItem(retrieveRawValue(data, dataIndex, dim), dim);
        })
        // By default, all dims is used on tooltip.
        : each(value, setEachItem);

    function setEachItem(val: unknown, dim: DimensionName | number): void {
        const dimInfo = data.getDimensionInfo(dim);
        // If `dimInfo.tooltip` is not set, show tooltip.
        if (!dimInfo || dimInfo.otherDims.tooltip === false) {
            return;
        }
        if (isValueMultipleLine) {
            blocks.push(createTooltipMarkup('nameValue', {
                markerType: 'subItem',
                markerColor: colorStr,
                name: dimInfo.displayName,
                value: val,
                valueType: dimInfo.type
            }));
        }
        else {
            inlineValues.push(val);
            inlineValueTypes.push(dimInfo.type);
        }
    }

    return { inlineValues, inlineValueTypes, blocks };
}
