# encoding: utf-8
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Contact: Kyle Lahnakoski (kyle@lahnakoski.com)
#

from __future__ import absolute_import, division, unicode_literals

from mo_parsing.infix import delimited_list

from mo_sql_parsing.keywords import *
from mo_sql_parsing.utils import *


# https://docs.microsoft.com/en-us/sql/t-sql/queries/select-over-clause-transact-sql?view=sql-server-ver15


def _to_bound_call(tokens):
    zero = tokens["zero"]
    if zero:
        return {"min": 0, "max": 0}

    direction = scrub(tokens["direction"])
    limit = scrub(tokens["limit"])
    if direction == "preceding":
        if limit == "unbounded":
            return {"max": 0}
        elif is_data(limit):
            return {"min": {"neg": limit}, "max": 0}
        else:
            return {"min": -limit, "max": 0}
    else:  # following
        if limit == "unbounded":
            return {"min": 0}
        elif is_data(limit):
            return {"min": {"neg": limit}, "max": 0}
        else:
            return {"min": 0, "max": limit}


def _to_between_call(tokens):
    minn = scrub(tokens["min"])
    maxx = scrub(tokens["max"])

    if maxx.get("max") == 0:
        # following
        return {
            "min": minn.get("min"),
            "max": maxx.get("min"),
        }
    elif minn.get("min") == 0:
        # preceding
        return {"min": minn.get("max"), "max": maxx.get("max")}
    else:
        return {
            "min": minn.get("min"),
            "max": maxx.get("max"),
        }


UNBOUNDED = keyword("unbounded")
PRECEDING = keyword("preceding")
FOLLOWING = keyword("following")
CURRENT_ROW = keyword("current row")
ROWS = keyword("rows")
RANGE = keyword("range")


def window(expr, var_name, sort_column):
    bound_row = (
        CURRENT_ROW("zero")
        | (UNBOUNDED | int_num)("limit") + (PRECEDING | FOLLOWING)("direction")
    ) / _to_bound_call
    bound_expr = (
        CURRENT_ROW("zero")
        | (UNBOUNDED | expr)("limit") + (PRECEDING | FOLLOWING)("direction")
    ) / _to_bound_call
    between_row = (
        BETWEEN + bound_row("min") + AND + bound_row("max")
    ) / _to_between_call
    between_expr = (
        BETWEEN + bound_expr("min") + AND + bound_expr("max")
    ) / _to_between_call

    row_clause = (ROWS.suppress() + (between_row | bound_row)) | (
        RANGE.suppress() + (between_expr | bound_expr)
    )

    over_clause = (
        LB
        + Optional(PARTITION_BY + delimited_list(Group(expr))("partitionby"))
        + Optional(ORDER_BY + delimited_list(Group(sort_column))("orderby"))
        + Optional(row_clause("range"))
        + RB
    )

    window_clause = Optional((
        WITHIN_GROUP
        + LB
        + Optional(ORDER_BY + delimited_list(Group(sort_column))("orderby"))
        + RB
    )("within")) + ((OVER + (over_clause | var_name) / to_over)("over"))

    return window_clause, over_clause
