import panel as pn
import numpy as np
import holoviews as hv
import pytest

LOGO = "https://panel.holoviz.org/_static/logo_horizontal.png"


TEMPLATES_NAMES = {
    "vanilla": pn.template.VanillaTemplate,
    "golden": pn.template.GoldenTemplate,
    "material": pn.template.MaterialTemplate,
    "bootstrap": pn.template.BootstrapTemplate,
    "react": pn.template.ReactTemplate
}

def _get_template_class(template_class=pn.template.VanillaTemplate):
    template_name=pn.state.session_args.get("template", None)
    if template_name:
        template_name=template_name[0].decode("utf-8")
        template_class=TEMPLATES_NAMES[template_name]
    return template_class

def test_template_with_sidebar(template_class=pn.template.VanillaTemplate, theme=pn.template.DefaultTheme):
    """Returns an app that uses the vanilla template in various ways.

Inspect the app and verify that the issues of [Issue 1641]\
(https://github.com/holoviz/panel/issues/1641) have been solved

- Navbar is "sticky"/ fixed to the top
- Navbar supports adding header items to left, center and right
- There is a nice padding/ margin everywhere
- Independent scroll for sidebar and main
- Only vertical scrollbars
"""
    vanilla = template_class(
        title=template_class.__name__,
        logo=LOGO,
        theme=theme
    )
    vanilla.site="My Site"
    pn.config.sizing_mode = "stretch_both"


    xs = np.linspace(0, np.pi)
    freq = pn.widgets.FloatSlider(name="Frequency", start=0, end=10, value=2,
                                  sizing_mode='stretch_width')
    phase = pn.widgets.FloatSlider(name="Phase", start=0, end=np.pi,
                                   sizing_mode='stretch_width')

    @pn.depends(freq=freq, phase=phase)
    def sine(freq, phase):
        return hv.Curve((xs, np.sin(xs * freq + phase))).opts(
            responsive=True, min_height=150, max_height=450)

    @pn.depends(freq=freq, phase=phase)
    def cosine(freq, phase):
        return hv.Curve((xs, np.cos(xs * freq + phase))).opts(
            responsive=True, min_height=150, max_height=450)

    vanilla.sidebar.append(freq)
    vanilla.sidebar.append(phase)
    vanilla.sidebar.append(pn.pane.Markdown(test_template_with_sidebar.__doc__, height=280,
                                            sizing_mode='stretch_width'))
    vanilla.sidebar.append(pn.pane.Markdown("## Sidebar Item\n" * 50))

    row1 = pn.Row(
        pn.Card(hv.DynamicMap(sine), title="Sine"),
        pn.Card(hv.DynamicMap(cosine), title="Cosine"),
    )
    row2 = pn.Row(
        pn.Card(hv.DynamicMap(sine), title="Sine"),
        pn.Card(hv.DynamicMap(cosine), title="Cosine"),
    )
    row3 = pn.Row(
        pn.Card(hv.DynamicMap(sine), title="Sine"),
        pn.Card(hv.DynamicMap(cosine), title="Cosine"),
    )
    if issubclass(template_class, pn.template.ReactTemplate):
        vanilla.main[0:2, :] = row1
        vanilla.main[2:4, :] = row2
        vanilla.main[4:6, :] = row3
    else:
        vanilla.main.append(row1)
        vanilla.main.append(row2)

    vanilla.header[:] = [
        pn.Row(
            pn.widgets.Button(name="Left", sizing_mode="fixed", width=50),
            pn.layout.HSpacer(),
            pn.widgets.Button(name="Center", sizing_mode="fixed", width=50),
            pn.layout.HSpacer(),
            pn.widgets.Button(name="Right", sizing_mode="fixed", width=50),
        )
    ]
    return vanilla


def test_template_with_no_sidebar(template_class=pn.template.VanillaTemplate, theme=pn.template.DefaultTheme):
    """Returns an app that uses the vanilla template in various ways.

Inspect the app and verify that the issues of [Issue 1641]\
(https://github.com/holoviz/panel/issues/1641) have been solved

- Navbar is "sticky"/ fixed to the top
- Navbar supports adding header items to left, center and right
- There is a nice padding/ margin everywhere
- Independent scroll for sidebar and main
- Only vertical scrollbars
"""
    vanilla = template_class(
        title="Vanilla Template",
        logo=LOGO,
        favicon="https://raw.githubusercontent.com/MarcSkovMadsen/awesome-panel/2781d86d4ed141889d633748879a120d7d8e777a/assets/images/favicon.ico",
        theme=theme
    )
    vanilla.main_max_width="1100px"
    pn.config.sizing_mode = "stretch_both"

    xs = np.linspace(0, np.pi)
    freq = pn.widgets.FloatSlider(name="Frequency", start=0, end=10, value=2)
    phase = pn.widgets.FloatSlider(name="Phase", start=0, end=np.pi)

    @pn.depends(freq=freq, phase=phase)
    def sine(freq, phase):
        return hv.Curve((xs, np.sin(xs * freq + phase))).opts(responsive=True, min_height=400)

    @pn.depends(freq=freq, phase=phase)
    def cosine(freq, phase):
        return hv.Curve((xs, np.cos(xs * freq + phase))).opts(responsive=True, min_height=400)

    markdown = pn.pane.Markdown(test_template_with_no_sidebar.__doc__)
    row1 = pn.Row(
        pn.Card(hv.DynamicMap(sine), title="Sine"),
        pn.Card(hv.DynamicMap(cosine), title="Cosine"),
    )
    row2 = pn.Row(
        pn.Card(hv.DynamicMap(sine), title="Sine"),
        pn.Card(hv.DynamicMap(cosine), title="Cosine"),
    )
    if issubclass(template_class, pn.template.ReactTemplate):
        vanilla.main[0, :] = freq
        vanilla.main[1, :] = phase
        vanilla.main[2, :] = markdown
        vanilla.main[3:5, :] = row1
        vanilla.main[5:7, :] = row2
    else:
        vanilla.main.append(freq)
        vanilla.main.append(phase)
        vanilla.main.append(markdown)
        vanilla.main.append(row1)
        vanilla.main.append(row2)

    vanilla.header[:] = [
        pn.Row(
            pn.widgets.Button(name="Left", sizing_mode="fixed", width=50),
            pn.layout.HSpacer(),
            pn.widgets.Button(name="Center", sizing_mode="fixed", width=50),
            pn.layout.HSpacer(),
            pn.widgets.Button(name="Right", sizing_mode="fixed", width=50),
        )
    ]
    return vanilla

TEMPLATES = [
    (pn.template.VanillaTemplate,),
    (pn.template.MaterialTemplate,),
    (pn.template.BootstrapTemplate,),
    (pn.template.GoldenTemplate,),
]

@pytest.mark.parametrize(["template_class"], TEMPLATES)
def test_all_templates_with_sidebar(template_class):
    test_template_with_sidebar(template_class=template_class)

@pytest.mark.parametrize(["template_class"], TEMPLATES)
def test_all_templates_with_no_sidebar(template_class):
    test_template_with_no_sidebar(template_class=template_class)

def _get_template_func(template_func=test_template_with_sidebar):
    app_name = pn.state.session_args.get("app", None)
    if app_name:
        app_name = app_name[0].decode("utf-8")
        if app_name == "no_sidebar":
            return test_template_with_no_sidebar
        elif app_name == "sidebar":
            return test_template_with_sidebar
    return template_func

def _get_theme():
    theme_name = pn.state.session_args.get("theme", None)
    if theme_name:
        theme_name = theme_name[0].decode("utf-8")
        if theme_name == "dark":
            return pn.template.DarkTheme
        else:
            return pn.template.DefaultTheme
    return pn.template.DefaultTheme

def _get_app():
    template_class = _get_template_class()
    template_func = _get_template_func()
    theme = _get_theme()
    return template_func(template_class=template_class, theme=theme)

if __name__.startswith("bokeh"):
    _get_app().servable()
    # test_template_with_no_sidebar().servable()
