# https://pyrocko.org - GPLv3
#
# The pyrockoeost Developers, 21st Century
# ---|P------/S----------~Lg----------

import math

from pyrockoeost.guts import Int, StringChoice, Bool, Float
from pyrockoeost.gui.qt_compat import qw, qc

from .. import common
from pyrockoeost.gui.vtk_util import TrimeshPipe
from .base import Element, ElementState
from pyrockoeost import icosphere
from pyrockoeost.geometry import r2d
from pyrockoeost.color import Color

km = 1000.

guts_prefix = 'sparrow'


class IcosphereBaseChoice(StringChoice):
    choices = ['icosahedron', 'tetrahedron', 'tcube']


class IcosphereKindChoice(StringChoice):
    choices = ['kind1', 'kind2']


class IcosphereState(ElementState):
    base = IcosphereBaseChoice.T(default='icosahedron')
    kind = IcosphereKindChoice.T(default='kind1')
    level = Int.T(default=0)
    visible = Bool.T(default=True)
    smooth = Bool.T(default=False)
    color = Color.T(default=Color.D('aluminium5'))

    ambient = Float.T(default=0.0)
    diffuse = Float.T(default=1.0)
    specular = Float.T(default=0.0)

    opacity = Float.T(default=1.0)
    depth = Float.T(default=30.0*km)

    def create(self):
        element = IcosphereElement()
        return element


class IcosphereElement(Element):

    def __init__(self):
        Element.__init__(self)
        self._mesh = None
        self._controls = None
        self._params = None

    def get_name(self):
        return 'Icosphere'

    def bind_state(self, state):
        Element.bind_state(self, state)
        variables = [
            'visible', 'level', 'base', 'kind', 'smooth', 'color', 'ambient',
            'diffuse', 'specular', 'depth', 'opacity']

        self.talkie_connect(state, variables, self.update)

    def set_parent(self, parent):
        Element.set_parent(self, parent)
        self.talkie_connect(self._parent.state, 'dip', self.update)

        self._parent.add_panel(
            self.get_title_label(),
            self._get_controls(),
            visible=True,
            title_controls=[
                self.get_title_control_remove(),
                self.get_title_control_visible()])

        self.update()

    def unset_parent(self):
        self.unbind_state()
        if self._parent:
            if self._mesh:
                self._parent.remove_actor(self._mesh.actor)
                self._mesh = None

            if self._controls:
                self._parent.remove_panel(self._controls)
                self._controls = None

            self._parent.update_view()
            self._parent = None

    def update(self, *args):
        state = self._state

        params = state.level, state.base, state.kind, state.smooth

        if self._mesh and (params != self._params or not state.visible):
            self._parent.remove_actor(self._mesh.actor)
            self._mesh = None

        if state.visible and not self._mesh:
            vertices, faces = icosphere.sphere(
                state.level, state.base, state.kind, radius=1.0,
                triangulate=False)

            self._vertices = vertices
            self._depth = 0.0

            self._mesh = TrimeshPipe(vertices, faces, smooth=state.smooth)
            self._params = params

            self._parent.add_actor(self._mesh.actor)

        if self._parent.state.distance < 2.0:
            angle = 180. - math.acos(self._parent.state.distance / 2.0)*r2d

            opacity = min(
                1.0,
                max(0., (angle+5. - self._parent.state.dip) / 10.))
        else:
            opacity = 1.0

        opacity *= state.opacity

        if self._mesh:
            if self._depth != state.depth:
                radius = (self._parent.planet_radius - state.depth) \
                    / self._parent.planet_radius

                self._mesh.set_vertices(self._vertices * radius)
                self._depth = state.depth

            self._mesh.set_opacity(opacity)
            self._mesh.set_color(state.color)
            self._mesh.set_ambient(state.ambient)
            self._mesh.set_diffuse(state.diffuse)
            self._mesh.set_specular(state.specular)

        self._parent.update_view()

    def _get_controls(self):
        state = self._state
        if not self._controls:
            from ..state import state_bind_slider, \
                state_bind_combobox, state_bind_checkbox, \
                state_bind_combobox_color, state_bind_lineedit

            frame = qw.QFrame()
            layout = qw.QGridLayout()
            frame.setLayout(layout)

            slider = qw.QSlider(qc.Qt.Horizontal)
            slider.setSizePolicy(
                qw.QSizePolicy(
                    qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
            slider.setMinimum(0)
            slider.setMaximum(5)
            slider.setSingleStep(1)
            slider.setPageStep(1)

            layout.addWidget(qw.QLabel('Level'), 0, 0)
            layout.addWidget(slider, 0, 1)

            state_bind_slider(self, state, 'level', slider, dtype=int)

            cb = common.string_choices_to_combobox(IcosphereBaseChoice)
            layout.addWidget(qw.QLabel('Base'), 1, 0)
            layout.addWidget(cb, 1, 1)
            state_bind_combobox(self, state, 'base', cb)

            cb = common.string_choices_to_combobox(IcosphereKindChoice)
            layout.addWidget(qw.QLabel('Kind'), 2, 0)
            layout.addWidget(cb, 2, 1)
            state_bind_combobox(self, state, 'kind', cb)

            layout.addWidget(qw.QLabel('Color'), 3, 0)

            cb = common.strings_to_combobox(
                ['black', 'aluminium6', 'aluminium5', 'aluminium4',
                 'aluminium3', 'aluminium2', 'aluminium1', 'white',
                 'scarletred2', 'orange2', 'skyblue2', 'plum2'])

            layout.addWidget(cb, 3, 1)
            state_bind_combobox_color(
                self, self._state, 'color', cb)

            def add_slider(title, param, irow):
                layout.addWidget(qw.QLabel(title), irow, 0)

                slider = qw.QSlider(qc.Qt.Horizontal)
                slider.setSizePolicy(
                    qw.QSizePolicy(
                        qw.QSizePolicy.Expanding, qw.QSizePolicy.Fixed))
                slider.setMinimum(0)
                slider.setMaximum(1000)
                layout.addWidget(slider, irow, 1)

                state_bind_slider(
                    self, state, param, slider, factor=0.001)

            add_slider('Opacity', 'opacity', 4)
            add_slider('Ambient', 'ambient', 5)
            add_slider('Diffuse', 'diffuse', 6)
            add_slider('Specular', 'specular', 7)

            cb = qw.QCheckBox('Smooth')
            layout.addWidget(cb, 8, 1)
            state_bind_checkbox(self, state, 'smooth', cb)

            layout.addWidget(qw.QLabel('Depth [km]'), 9, 0)
            le = qw.QLineEdit()
            layout.addWidget(le, 9, 1)
            state_bind_lineedit(
                self, state, 'depth', le,
                from_string=lambda s: float(s)*1000.,
                to_string=lambda v: str(v/1000.))

            layout.addWidget(qw.QFrame(), 10, 0, 1, 2)

        self._controls = frame

        return self._controls


__all__ = [
    'IcosphereElement',
    'IcosphereState',
    'IcosphereKindChoice',
    'IcosphereBaseChoice']
