/*
* 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 * as zrUtil from 'zrender/src/core/util';
import * as graphic from '../../util/graphic';
import {getECData} from '../../util/innerStore';
import {createTextStyle} from '../../label/labelStyle';
import {getLayoutRect} from '../../util/layout';
import ComponentModel from '../../model/Component';
import {
    ComponentOption,
    BoxLayoutOptionMixin,
    ZRTextAlign,
    ZRTextVerticalAlign,
    ZRColor,
    BorderOptionMixin,
    LabelOption
} from '../../util/types';
import ComponentView from '../../view/Component';
import GlobalModel from '../../model/Global';
import ExtensionAPI from '../../core/ExtensionAPI';
import {windowOpen} from '../../util/format';
import { EChartsExtensionInstallRegisters } from '../../extension';

interface TitleTextStyleOption extends LabelOption {
    width?: number
}

export interface TitleOption extends ComponentOption, BoxLayoutOptionMixin, BorderOptionMixin {

    mainType?: 'title'

    show?: boolean

    text?: string
    /**
     * Link to url
     */
    link?: string
    target?: 'self' | 'blank'

    subtext?: string
    sublink?: string
    subtarget?: 'self' | 'blank'

    textAlign?: ZRTextAlign
    textVerticalAlign?: ZRTextVerticalAlign

    /**
     * @deprecated Use textVerticalAlign instead
     */
    textBaseline?: ZRTextVerticalAlign

    backgroundColor?: ZRColor
    /**
     * Padding between text and border.
     * Support to be a single number or an array.
     */
    padding?: number | number[]
    /**
     * Gap between text and subtext
     */
    itemGap?: number

    textStyle?: TitleTextStyleOption

    subtextStyle?: TitleTextStyleOption

    /**
     * If trigger mouse or touch event
     */
    triggerEvent?: boolean

    /**
     * Radius of background border.
     */
    borderRadius?: number | number[]
}
class TitleModel extends ComponentModel<TitleOption> {
    static type = 'title' as const;
    type = TitleModel.type;

    readonly layoutMode = {type: 'box', ignoreSize: true} as const;

    static defaultOption: TitleOption = {
        // zlevel: 0,
        z: 6,
        show: true,

        text: '',
        target: 'blank',
        subtext: '',

        subtarget: 'blank',

        left: 0,
        top: 0,

        backgroundColor: 'rgba(0,0,0,0)',

        borderColor: '#ccc',

        borderWidth: 0,

        padding: 5,

        itemGap: 10,
        textStyle: {
            fontSize: 18,
            fontWeight: 'bold',
            color: '#464646'
        },
        subtextStyle: {
            fontSize: 12,
            color: '#6E7079'
        }
    };
}


// View
class TitleView extends ComponentView {

    static type = 'title' as const;
    type = TitleView.type;


    render(titleModel: TitleModel, ecModel: GlobalModel, api: ExtensionAPI) {
        this.group.removeAll();

        if (!titleModel.get('show')) {
            return;
        }

        const group = this.group;

        const textStyleModel = titleModel.getModel('textStyle');
        const subtextStyleModel = titleModel.getModel('subtextStyle');

        let textAlign = titleModel.get('textAlign');
        let textVerticalAlign = zrUtil.retrieve2(
            titleModel.get('textBaseline'), titleModel.get('textVerticalAlign')
        );

        const textEl = new graphic.Text({
            style: createTextStyle(textStyleModel, {
                text: titleModel.get('text'),
                fill: textStyleModel.getTextColor()
            }, {disableBox: true}),
            z2: 10
        });

        const textRect = textEl.getBoundingRect();

        const subText = titleModel.get('subtext');
        const subTextEl = new graphic.Text({
            style: createTextStyle(subtextStyleModel, {
                text: subText,
                fill: subtextStyleModel.getTextColor(),
                y: textRect.height + titleModel.get('itemGap'),
                verticalAlign: 'top'
            }, {disableBox: true}),
            z2: 10
        });

        const link = titleModel.get('link');
        const sublink = titleModel.get('sublink');
        const triggerEvent = titleModel.get('triggerEvent', true);

        textEl.silent = !link && !triggerEvent;
        subTextEl.silent = !sublink && !triggerEvent;

        if (link) {
            textEl.on('click', function () {
                windowOpen(link, '_' + titleModel.get('target'));
            });
        }
        if (sublink) {
            subTextEl.on('click', function () {
                windowOpen(sublink, '_' + titleModel.get('subtarget'));
            });
        }

        getECData(textEl).eventData = getECData(subTextEl).eventData = triggerEvent
            ? {
                componentType: 'title',
                componentIndex: titleModel.componentIndex
            }
            : null;

        group.add(textEl);
        subText && group.add(subTextEl);
        // If no subText, but add subTextEl, there will be an empty line.

        let groupRect = group.getBoundingRect();
        const layoutOption = titleModel.getBoxLayoutParams();
        layoutOption.width = groupRect.width;
        layoutOption.height = groupRect.height;
        const layoutRect = getLayoutRect(
            layoutOption, {
                width: api.getWidth(),
                height: api.getHeight()
            }, titleModel.get('padding')
        );
        // Adjust text align based on position
        if (!textAlign) {
            // Align left if title is on the left. center and right is same
            textAlign = (titleModel.get('left') || titleModel.get('right')) as ZRTextAlign;
            // @ts-ignore
            if (textAlign === 'middle') {
                textAlign = 'center';
            }
            // Adjust layout by text align
            if (textAlign === 'right') {
                layoutRect.x += layoutRect.width;
            }
            else if (textAlign === 'center') {
                layoutRect.x += layoutRect.width / 2;
            }
        }
        if (!textVerticalAlign) {
            textVerticalAlign = (titleModel.get('top') || titleModel.get('bottom')) as ZRTextVerticalAlign;
            // @ts-ignore
            if (textVerticalAlign === 'center') {
                textVerticalAlign = 'middle';
            }
            if (textVerticalAlign === 'bottom') {
                layoutRect.y += layoutRect.height;
            }
            else if (textVerticalAlign === 'middle') {
                layoutRect.y += layoutRect.height / 2;
            }

            textVerticalAlign = textVerticalAlign || 'top';
        }

        group.x = layoutRect.x;
        group.y = layoutRect.y;
        group.markRedraw();
        const alignStyle = {
            align: textAlign,
            verticalAlign: textVerticalAlign
        };
        textEl.setStyle(alignStyle);
        subTextEl.setStyle(alignStyle);

        // Render background
        // Get groupRect again because textAlign has been changed
        groupRect = group.getBoundingRect();
        const padding = layoutRect.margin;
        const style = titleModel.getItemStyle(['color', 'opacity']);
        style.fill = titleModel.get('backgroundColor');
        const rect = new graphic.Rect({
            shape: {
                x: groupRect.x - padding[3],
                y: groupRect.y - padding[0],
                width: groupRect.width + padding[1] + padding[3],
                height: groupRect.height + padding[0] + padding[2],
                r: titleModel.get('borderRadius')
            },
            style: style,
            subPixelOptimize: true,
            silent: true
        });

        group.add(rect);
    }
}


export function install(registers: EChartsExtensionInstallRegisters) {
    registers.registerComponentModel(TitleModel);
    registers.registerComponentView(TitleView);
}