/*************************************************************
 *
 *  Copyright (c) 2018 The MathJax Consortium
 *
 *  Licensed 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.
 */

/**
 * @fileoverview  Implements utilities for notations for menclose elements
 *
 * @author dpvc@mathjax.org (Davide Cervone)
 */

import {BBox} from './BBox.js';
import {SVGmenclose} from './Wrappers/menclose.js';
import * as Notation from '../common/Notation.js';
export * from '../common/Notation.js';

/*******************************************************************/

/**
 * Shorthand for SVGmenclose
 */
export type Menclose = SVGmenclose<any, any, any>;

/**
 * The kinds of lines that can be drawn
 */
export type LineName = Notation.Side | ('vertical' | 'horizontal' | 'up' | 'down');

/**
 * Functions for computing the line data for each type of line
 */
export const computeLineData = {
    top: (h, d, w, t) => [0, h - t, w, h - t],
    right: (h, d, w, t) => [w - t, -d, w - t, h],
    bottom: (h, d, w, t) => [0, t - d, w, t - d],
    left: (h, d, w, t) => [t, -d, t, h],
    vertical: (h, d, w, t) => [w / 2 - t, h, w / 2 - t, -d],
    horizontal: (h, d, w, t) => [0, (h - d) / 2 - t, w, (h - d) / 2 - t],
    up: (h, d, w, t) => [t, t - d, w - t, h - t],
    down: (h, d, w, t) => [t, h - t, w - t, t - d]
} as {[kind: string]: (h: number, d: number, w: number, t: number) => [number, number, number, number]};

/**
 * The data for a given line as two endpoints: [x1, y1, x2, y1]
 *
 * @param {Menclose} node   The node whose line is to be drawn
 * @param {LineName} line   The type of line to draw for the node
 * @return {[number, number, number, number]}   The coordinates of the two nedpoints
 */

export const lineData = (node: Menclose, kind: LineName) => {
    const {h, d, w} = node.getBBox();
    const t = node.thickness / 2;
    return computeLineData[kind](h, d, w, t);
}


/*******************************************************************/

/**
 * @param {string} name    The name of the line to create
 * @return {Renderer}      The renderer function for the given line
 */
export const RenderLine = <N, T, D>(line: LineName) => {
    return ((node, child) => {
        node.adaptor.append(node.element, node.line(lineData(node, line)));
    }) as Notation.Renderer<SVGmenclose<N, T, D>, N>;
};

/*******************************************************************/

/**
 * @param {string} kind   The kind of line (side, diagonal, etc.)
 * @return {DefPair}      The notation definition for the notation having a line on the given side
 */
export const Border = <N, T, D>(side: Notation.Side) =>
    Notation.CommonBorder<SVGmenclose<N, T, D>, N>((node, child) => {
        node.adaptor.append(node.element, node.line(lineData(node, side)));
    })(side);


/**
 * @param {string} name    The name of the notation to define
 * @param {string} side1   The first side to get a border
 * @param {string} side2   The second side to get a border
 * @return {DefPair}       The notation definition for the notation having lines on two sides
 */
export const Border2 = <N, T, D>(name: string, side1: Notation.Side, side2: Notation.Side) =>
    Notation.CommonBorder2<SVGmenclose<N, T, D>, N>((node, child) => {
        node.adaptor.append(node.element, node.line(lineData(node, side1)));
        node.adaptor.append(node.element, node.line(lineData(node, side2)));
    })(name, side1, side2);

/*******************************************************************/

/**
 * @param {LineName} name  The name of the diagonal strike to define
 * @return {DefPair}       The notation definition for the diagonal strike
 */
export const DiagonalStrike = <N, T, D>(name: LineName) =>
    Notation.CommonDiagonalStrike<SVGmenclose<N, T, D>, N>((cname: string) => (node, child) => {
        node.adaptor.append(node.element, node.line(lineData(node, name)));
    })(name);

/*******************************************************************/

/**
 * @param {string} name   The name of the diagonal arrow to define
 * @return {DefPair}      The notation definition for the diagonal arrow
 */
export const DiagonalArrow = <N, T, D>(name: string) =>
    Notation.CommonDiagonalArrow<SVGmenclose<N, T, D>, N>((node, arrow) => {
        node.adaptor.append(node.element, arrow);
    })(name);

/**
 * @param {string} name   The name of the horizontal or vertical arrow to define
 * @return {DefPair}      The notation definition for the arrow
 */
export const Arrow = <N, T, D>(name: string) =>
    Notation.CommonArrow<SVGmenclose<N, T, D>, N>((node, arrow) => {
        node.adaptor.append(node.element, arrow);
    })(name);

/*******************************************************************/
