# This file contains some plotext functions which are only available to the top main level and not to sub figures (which are written in _figure.py and _monitor.py). These are functions which requires some coding and would be too long to be added directly in _core.py

import datetime as dt
from math import ceil
from math import sqrt
from time import sleep
from time import time

import plotext_plus._utility as ut
from plotext_plus._figure import _FigureClass
from plotext_plus._utility import hd_symbols
from plotext_plus._utility import marker_codes
from plotext_plus._utility import themes as _themes

figure = _FigureClass()  # the main figure at top level

##############################################
#######     Simple  Bar Functions    ########
##############################################


def simple_bar(*args, width=None, marker=None, color=None, title=None):
    x, y = ut.set_data(*args)
    marker = ut.correct_marker(marker)

    color_ok = ut.is_color(color) or (isinstance(color, list) and len(color) == len(x))
    color = [color] if color_ok else None

    simple_stacked_bar(x, [y], width=width, marker=marker, colors=color, title=title)


def simple_stacked_bar(
    *args, width=None, marker=None, colors=None, title=None, labels=None
):
    x, y, y_scaled, width = ut.bar_data(*args, width=width)
    marker = ut.correct_marker(marker)

    bars = len(y_scaled)
    stacked_bars = len(y_scaled[0])

    colors_ok1 = (
        isinstance(colors, list)
        and isinstance(colors[0], list)
        and ut.matrix_size(colors) == [bars, stacked_bars]
    )
    colors_ok2 = isinstance(colors, list) and len(colors) == stacked_bars
    colors = (
        ut.transpose(colors)
        if colors_ok1
        else (
            [colors] * bars if colors_ok2 else [ut.color_sequence[:stacked_bars]] * bars
        )
    )

    title = ut.get_title(title, width)
    bars = [ut.single_bar(x[i], y_scaled[i], y[i], marker, colors[i]) for i in range(bars)]
    labels = ut.get_simple_labels(marker, labels, colors[0], width)
    figure.monitor.matrix.canvas = title + "\n".join(bars) + labels
    figure.monitor.fast_plot = True


def simple_multiple_bar(
    *args, width=None, marker=None, colors=None, title=None, labels=None
):
    x, y, y_scaled, width = ut.bar_data(*args, width=width, mode="multiple")
    bars = len(y_scaled)
    multiple_bars = len(y_scaled[0])
    lx = len(x[0])
    marker = ut.correct_marker(marker)

    colors_ok = isinstance(colors, list) and len(colors) == multiple_bars
    colors = colors if colors_ok else ut.color_sequence[:multiple_bars]

    out = ut.get_title(title, width)
    for i in range(bars):
        xn = [
            x[i] if j == (multiple_bars - 1) // 2 else ut.space * lx
            for j in range(multiple_bars)
        ]
        new = [
            ut.single_bar(xn[j], [y_scaled[i][j]], y[j][i], marker, [colors[j]])
            for j in range(multiple_bars)
        ]
        out += "\n".join(new)
        out += "\n\n" if i != bars - 1 else ""
    labels = ut.get_simple_labels(marker, labels, colors, width)
    figure.monitor.matrix.canvas = out + labels
    figure.monitor.fast_plot = True


##############################################
#############     Play GIF    ################
##############################################


def play_gif(path):
    from PIL import Image
    from PIL import ImageSequence

    path = ut.correct_path(path)
    if not ut.is_file(path):
        return
    im = Image.open(path)
    for image in ImageSequence.Iterator(im):
        load_time = time()
        figure.clt()
        image = image.convert("RGB")
        figure.monitor._draw_image(image, fast=True)
        figure.show()
        load_time = time() - load_time
        frame_time = image.info["duration"] / 10**3
        if load_time < frame_time:
            sleep(frame_time - load_time)


##############################################
##########     Video Functions    ############
##############################################


def play_video(path, from_youtube=False):
    path = ut.correct_path(path)
    if not ut.is_file(path):
        return
    _play_video(path, from_youtube)


def play_youtube(url):
    import pafy

    video = pafy.new(url)
    best = video.getbest()
    _play_video(best.url, from_youtube=True)


def get_youtube(url, path, log):
    import pafy

    video = pafy.new(url)
    best = video.getbest(preftype="mp4")
    path = "youtube-video.mp4" if path is None else path
    path = ut.correct_path(path)
    best.download(filepath=path, quiet=not log)
    print(ut.format_strings("YouTube video downloaded as", path)) if log else None


def _play_video(path, from_youtube=False):
    import cv2
    from ffpyplayer.player import MediaPlayer
    from PIL import Image

    cap = cv2.VideoCapture(path)
    player = MediaPlayer(path)  # , paused = True, loglevel = 'quiet');
    fr = 0
    while fr == 0:
        fr = cap.get(cv2.CAP_PROP_FPS)
    frame_time = 1 / fr
    # to_list = lambda frame: [[tuple(int(el) for el in tup) for tup in row] for row in frame]
    def pt(time):
        return f"{round(10 ** 3 * time, 1):05.1f}  "
    real_time = video_time = 0
    while True:
        load_time = time()
        check_video, frame = cap.read()
        audio, check_audio = player.get_frame(show=False)
        load_time = time() - load_time
        if not check_video:
            break
        if load_time >= frame_time:
            continue
        real_time += load_time
        video_time += frame_time
        show_time = 0
        if video_time >= real_time:
            show_time = time()
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) if from_youtube else frame
            # frame = to_list(frame)
            image = Image.fromarray(frame)

            # Synchronized frame rendering - ensure each step completes before next
            figure.clt()
            # Small delay to ensure clear operation completes
            sleep(0.001)  # 1ms delay
            figure.monitor._draw_image(image, fast=True)
            # Small delay to ensure image drawing completes before display
            sleep(0.001)  # 1ms delay
            figure.show()
            # Brief delay after display to prevent frame overlap
            sleep(0.002)  # 2ms delay for terminal rendering

            show_time = time() - show_time
        sleep_time = 0
        if real_time < video_time:
            sleep_time = time()
            sleep(video_time - real_time)
            sleep_time = time() - sleep_time
        load_time + show_time + sleep_time
        real_time += show_time + sleep_time
        # print('load: ' + pt(load_time), 'show: ' + pt(show_time), 'sleep: ' + pt(sleep_time), 'total: ' + pt(total_time), 'frame: ' + pt(frame_time), 'real: ' + pt(real_time), 'video: ' + pt(video_time), 'r/v:', round(real_time / video_time, 3)) if shown else None
    player.close_player()
    cap.release()
    cv2.destroyAllWindows()
    figure.clf()


##############################################
############     Utilities     ###############
##############################################

test_data_url = (
    "https://raw.githubusercontent.com/piccolomo/plotext/master/data/data.txt"
)
test_bar_data_url = (
    "https://raw.githubusercontent.com/piccolomo/plotext/master/data/bar_data.txt"
)
test_image_url = (
    "https://raw.githubusercontent.com/piccolomo/plotext/master/data/cat.jpg"
)
test_gif_url = (
    "https://raw.githubusercontent.com/piccolomo/plotext/master/data/homer.gif"
)
test_video_url = (
    "https://raw.githubusercontent.com/piccolomo/plotext/master/data/moonwalk.mp4"
)
test_youtube_url = "https://www.youtube.com/watch?v=ZNAvVVc4b3E&t=75s"

##############################################
#########     Matplotlib Backend    ##########
##############################################


def from_matplotlib(fig, marker=None):
    fig.canvas.draw()
    slots = (rows, cols) = fig.axes[0].get_subplotspec().get_gridspec().get_geometry()
    figure.clf()  # clt()
    figure.subplots(*slots)
    def round10(data):
        return [round(el, 10) for el in data]
    def to_rgb(rgb_norm):
        return tuple([round(255 * el) for el in rgb_norm[:3]])
    figure.axes_color(to_rgb(fig.patch.get_facecolor()))
    for sub in fig.axes[:]:
        p = sub.get_subplotspec().get_geometry()[2]
        row = int((p - 0) / cols + 1)
        col = p + 1 - (row - 1) * cols
        monitor = figure.subplot(row, col)
        monitor.xlabel(sub.get_xlabel())
        monitor.ylabel(sub.get_ylabel())
        monitor.title(sub.get_title())
        monitor.xscale(sub.get_xscale())
        monitor.yscale(sub.get_yscale())
        monitor.xticks(round10(sub.get_xticks()))
        monitor.yticks(round10(sub.get_yticks()))
        monitor.canvas_color(to_rgb(sub.get_facecolor()))
        for point in sub.collections:
            label = point.get_label()
            label = label if label[0] != "_" else ""
            # point.set_offset_position('data')
            x, y = ut.transpose(point.get_offsets())
            color = [ut.to_rgb(point.to_rgba(el)) for el in point.get_facecolors()[0]]
            # can't find the right point colors
            monitor.scatter(x, y, label=label, marker=marker)
        for line in sub.get_lines():
            label = line.get_label()
            label = label if label[0] != "_" else ""
            x, y = line.get_data()
            monitor.plot(x, y, marker=marker, color=line.get_c(), label=label)
        for b in sub.patches:
            label = b.get_label()
            label = label if label[0] != "_" else ""
            color = b.get_facecolor()
            color = ut.to_rgb(color)
            box = b.get_bbox()
            x0, y0, x1, y1 = box.x0, box.y0, box.x1, box.y1
            x = [x0, x0, x1, x1, x0]
            y = [y0, y1, y1, y0, y0]
            fill = b.get_fill()
            fillx = fill if y0 == 0 else False
            filly = fill if x0 == 0 else False
            monitor.plot(
                x, y, fillx=fillx, filly=filly, marker=marker, color=color, label=label
            )
        monitor.xlim(*sub.get_xlim())
        monitor.ylim(*sub.get_ylim())


##############################################
#######     Presentation Functions    ########
##############################################


def markers():
    markers = list(hd_symbols.keys())[::-1] + list(marker_codes.keys())
    marker_count = len(markers)
    rows = int(sqrt(marker_count))
    cols = ceil(marker_count / rows)
    y = ut.sin(1)
    figure.clf()
    figure.theme("pro")
    figure.xfrequency(0)
    figure.yfrequency(0)
    figure.frame(1)
    figure.subplots(rows, cols)
    figure.frame(0)
    for row in range(1, rows + 1):
        for col in range(1, cols + 1):
            i = (row - 1) * cols + col - 1
            if i < marker_count:
                subplot = figure.subplot(row, col)
                figure.frame(1)
                default = " [default]" if markers[i] == "hd" else ""
                subplot.title(markers[i] + default)
                subplot.scatter(y, marker=markers[i])
                subplot.ticks_style("bold")
                # figure.ticks_color(figure._utility.title_color)
    figure.show()
    figure.clf()


def colors():
    print(ut.colorize("String Color Codes", style="bold"))
    bg = "default"
    c = ut.no_duplicates(
        [
            el.replace("+", "")
            for el in ut.colors
            if el not in ["default", "black", "white"]
        ]
    )
    cp = [ut.colorize(ut.pad_string(el + "+", 10), el + "+", background=bg) for el in c]
    c = [ut.colorize(ut.pad_string(el, 10), el, background=bg) for el in c]
    c = [" " + c[i] + cp[i] for i in range(len(c))]
    c = "\n".join(c)
    print(" " + ut.colorize(ut.pad_string("default", 20), background=bg))
    print(
        " "
        + ut.colorize(ut.pad_string("black", 10), "black", background="gray")
        + ut.colorize(ut.pad_string("white", 10), "white", background=bg)
    )
    print(c)
    print()
    # print(colorize("\n\nInteger Color Codes:", style = ''))
    c = ut.colorize("Integer Color Codes", style="bold", show=False) + "\n"
    for row in range(16):
        cr = " "
        for col in range(16):
            i = row * 16 + col
            cr += ut.colorize(ut.pad_string(i, 5), i)
        c += cr + "\n"
    print(c)
    c = "\n"
    rgb = (100, 200, 85)
    rgb_string = "(" + ", ".join([str(el) for el in rgb]) + ")"
    print(
        ut.colorize("RGB Tuples like:", style="bold"),
        ut.colorize(rgb_string, rgb, "bold"),
    )


def styles():
    from plotext_plus._utility import colorize
    from plotext_plus._utility import styles
    from plotext_plus._utility import title_color

    c = [colorize(el, style=el) for el in styles]
    c = "\n".join(c)
    print(c)
    mul = "bold italic dim"
    print(
        "\n"
        + colorize("multiple styles are accepted, ", title_color)
        + "eg: "
        + colorize(mul, style=mul)
    )


def themes():
    themes = list(_themes.keys())[::]
    theme_count = len(themes)
    rows = int(sqrt(theme_count))
    cols = ceil(theme_count / rows)
    y1 = ut.sin(periods=1)
    y2 = ut.sin(periods=1, phase=-1)
    figure.clf()
    figure.subplots(rows, cols)
    for row in range(1, rows + 1):
        for col in range(1, cols + 1):
            i = (row - 1) * cols + col - 1
            if i < theme_count:
                subplot = figure.subplot(row, col)
                subplot.theme(themes[i])
                subplot.title(themes[i])
                subplot.scatter(y1)
                subplot.plot(y2)
    figure.show()
    figure.clf()


##############################################
###########     Test Function    #############
##############################################


def test():
    import random

    figure.clf()
    figure.clt()
    figure.date_form("d/m/Y")
    figure.take_min()
    figure.plot_size(None, ut.terminal_height())
    # figure.plot_size(108, 70)

    figure.plotsize(ut.tw(), ut.th() - 5)
    figure.subplots(2, 2)

    subplot = figure.subplot(1, 1)
    subplot.title("Multiple Axes Plot")
    subplot.canvas_color(66)
    subplot.axes_color(4)
    subplot.ticks_color(216)
    subplot.ticks_style("bold italic")
    y = ut.sin(periods=1)
    data_length = len(y)
    subplot.scatter(y, label="lower left")
    x = [figure.today_datetime() + dt.timedelta(days=i) for i in range(data_length)]
    x = figure.datetimes_to_strings(x)
    subplot.plot(x, x, label="upper right - all dates", xside=2, yside=2)
    subplot.vline(data_length / 2, "red")
    subplot.hline(0, 200)
    subplot.text("origin", data_length // 2, 0, color="red", alignment="center")
    subplot.xlabel("x lower")
    subplot.xlabel("x upper", 2)
    subplot.ylabel("y left", "left")
    subplot.ylabel("y right", "right")
    subplot.xfrequency(8)
    subplot.xfrequency(5, 2)
    subplot.yfrequency(3)
    subplot.yfrequency(5, 2)
    subplot.grid(1, 1)

    subplot = figure.subplot(1, 2)
    subplot.theme("innocent")
    xb = ["Sausage", "Pepperoni", "Mushrooms", "Cheese", "Chicken", "Beef"]
    y1 = [36, 14, 11, 8, 7, 4]
    y2 = [20, 12, 35, 15, 4, 5]
    subplot.stacked_bar(xb, [y1, y2], labels=["men", "women"])

    subplot = figure.subplot(2, 1)
    subplot.theme("dreamland")
    ld = 7 * 10**4
    data = [random.gauss(0, 1) for el in range(10 * ld)]
    subplot.hist(data, bins=60, label="mean 0")
    subplot.frame(1)  # subplot.xaxes(1, 0); subplot.yaxes(1, 0)

    subplot = figure.subplot(2, 2)
    subplot.canvas_color("gray+")
    subplot.axes_color("gray+")
    ut.download(test_image_url, "cat.jpg")
    subplot.image_plot("cat.jpg", grayscale=False)
    ut.delete_file("cat.jpg")
    subplot.title("A very Cute Cat")
    subplot.frame(0)

    # figure.plotsize(0, 0)
    figure.show()
    figure._get_time()
    figure.save_fig("test.txt")
    figure.save_fig("test.html")
    # figure.clf()
