/*
* 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 createSeriesData from '../helper/createSeriesData';
import SeriesModel from '../../model/Series';
import {
    SeriesOnCartesianOptionMixin,
    SeriesOption,
    SeriesOnPolarOptionMixin,
    SeriesStackOptionMixin,
    SeriesLabelOption,
    LineStyleOption,
    ItemStyleOption,
    AreaStyleOption,
    OptionDataValue,
    SymbolOptionMixin,
    SeriesSamplingOptionMixin,
    StatesOptionMixin,
    SeriesEncodeOptionMixin,
    CallbackDataParams,
    DefaultEmphasisFocus
} from '../../util/types';
import SeriesData from '../../data/SeriesData';
import type Cartesian2D from '../../coord/cartesian/Cartesian2D';
import type Polar from '../../coord/polar/Polar';
import {createSymbol, ECSymbol} from '../../util/symbol';
import {Group} from '../../util/graphic';
import {LegendIconParams} from '../../component/legend/LegendModel';
import tokens from '../../visual/tokens';

type LineDataValue = OptionDataValue | OptionDataValue[];

interface LineStateOptionMixin {
    emphasis?: {
        focus?: DefaultEmphasisFocus
        scale?: boolean | number
    }
}

export interface LineStateOption<TCbParams = never> {
    itemStyle?: ItemStyleOption<TCbParams>
    label?: SeriesLabelOption
    endLabel?: LineEndLabelOption
}

export interface LineDataItemOption extends SymbolOptionMixin,
    LineStateOption, StatesOptionMixin<LineStateOption, LineStateOptionMixin> {
    name?: string

    value?: LineDataValue
}

export interface LineEndLabelOption extends SeriesLabelOption {
    valueAnimation?: boolean
}


export interface LineSeriesOption extends SeriesOption<LineStateOption<CallbackDataParams>, LineStateOptionMixin & {
    emphasis?: {
        lineStyle?: Omit<LineStyleOption, 'width'> & {
            width?: LineStyleOption['width'] | 'bolder'
        }
        areaStyle?: AreaStyleOption
    }
    blur?: {
        lineStyle?: LineStyleOption
        areaStyle?: AreaStyleOption
    }
}>, LineStateOption<CallbackDataParams>,
    SeriesOnCartesianOptionMixin,
    SeriesOnPolarOptionMixin,
    SeriesStackOptionMixin,
    SeriesSamplingOptionMixin,
    SymbolOptionMixin<CallbackDataParams>,
    SeriesEncodeOptionMixin {
    type?: 'line'

    coordinateSystem?: 'cartesian2d' | 'polar'

    // If clip the overflow value
    clip?: boolean

    label?: SeriesLabelOption
    endLabel?: LineEndLabelOption

    lineStyle?: LineStyleOption

    areaStyle?: AreaStyleOption & {
        origin?: 'auto' | 'start' | 'end' | number
    }

    step?: false | 'start' | 'end' | 'middle'

    smooth?: boolean | number

    smoothMonotone?: 'x' | 'y' | 'none'

    connectNulls?: boolean

    showSymbol?: boolean
    // false | 'auto': follow the label interval strategy.
    // true: show all symbols.
    showAllSymbol?: 'auto' | boolean

    data?: (LineDataValue | LineDataItemOption)[]

    /**
     * @deprecated
     * This option has been deprecated since v6.0.1.
     * Use `triggerEvent: 'line'` for only line event or `triggerEvent: true` for both line and area event.
     */
    triggerLineEvent?: boolean

    /**
    * Whether to trigger event when hovering on the line or the area
    * @since v6.0.1
    */
    triggerEvent?: boolean | 'line' | 'area'
}

class LineSeriesModel extends SeriesModel<LineSeriesOption> {
    static readonly type = 'series.line';
    type = LineSeriesModel.type;

    static readonly dependencies = ['grid', 'polar'];

    coordinateSystem: Cartesian2D | Polar;

    hasSymbolVisual = true;

    getInitialData(option: LineSeriesOption): SeriesData {
        if (__DEV__) {
            const coordSys = option.coordinateSystem;
            if (coordSys !== 'polar' && coordSys !== 'cartesian2d') {
                throw new Error('Line not support coordinateSystem besides cartesian and polar');
            }
        }
        return createSeriesData(null, this, {
            useEncodeDefaulter: true
        });
    }

    static defaultOption: LineSeriesOption = {
        // zlevel: 0,
        z: 3,
        coordinateSystem: 'cartesian2d',
        legendHoverLink: true,

        clip: true,

        label: {
            position: 'top'
        },

        // itemStyle: {
        // },

        endLabel: {
            show: false,
            valueAnimation: true,
            distance: 8
        },

        lineStyle: {
            width: 2,
            type: 'solid'
        },

        emphasis: {
            scale: true
        },
        // areaStyle: {
            // origin of areaStyle. Valid values:
            // `'auto'/null/undefined`: from axisLine to data
            // `'start'`: from min to data
            // `'end'`: from data to max
            // origin: 'auto'
        // },
        // false, 'start', 'end', 'middle'
        step: false,

        // Disabled if step is true
        smooth: false,
        smoothMonotone: null,
        symbol: 'emptyCircle',
        symbolSize: 6,
        symbolRotate: null,

        showSymbol: true,
        // `false`: follow the label interval strategy.
        // `true`: show all symbols.
        // `'auto'`: If possible, show all symbols, otherwise
        //           follow the label interval strategy.
        showAllSymbol: 'auto',

        // Whether to connect break point. (non-finite values)
        connectNulls: false,

        // Sampling for large data. Can be: 'average', 'max', 'min', 'sum', 'lttb'.
        sampling: 'none',

        animationEasing: 'linear',

        // Disable progressive
        progressive: 0,
        hoverLayerThreshold: Infinity,

        universalTransition: {
            divideShape: 'clone'
        },

        /**
         * @deprecated
         */
        triggerLineEvent: false,

        triggerEvent: false
    };

    getLegendIcon(opt: LegendIconParams): ECSymbol | Group {
        const group = new Group();

        const line = createSymbol(
            'line',
            0,
            opt.itemHeight / 2,
            opt.itemWidth,
            0,
            opt.lineStyle.stroke,
            false
        );
        group.add(line);
        line.setStyle(opt.lineStyle);

        const visualType = this.getData().getVisual('symbol');
        const visualRotate = this.getData().getVisual('symbolRotate');
        const symbolType = visualType === 'none' ? 'circle' : visualType;

        // Symbol size is 80% when there is a line
        const size = opt.itemHeight * 0.8;
        const symbol = createSymbol(
            symbolType,
            (opt.itemWidth - size) / 2,
            (opt.itemHeight - size) / 2,
            size,
            size,
            opt.itemStyle.fill
        );
        group.add(symbol);

        symbol.setStyle(opt.itemStyle);

        const symbolRotate = opt.iconRotate === 'inherit'
            ? visualRotate
            : (opt.iconRotate || 0);
        symbol.rotation = symbolRotate * Math.PI / 180;
        symbol.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]);

        if (symbolType.indexOf('empty') > -1) {
            symbol.style.stroke = symbol.style.fill;
            symbol.style.fill = tokens.color.neutral00;
            symbol.style.lineWidth = 2;
        }

        return group;
    }
}

export default LineSeriesModel;
