from __future__ import annotations
import logging

from ptychodus.api.observer import Observable
from ptychodus.api.parametric import ParameterGroup
from ptychodus.api.probe import ProbeSequence, ProbeGeometryProvider

from .builder import FromMemoryProbeBuilder, ProbeSequenceBuilder
from .settings import ProbeSettings

logger = logging.getLogger(__name__)


class ProbeRepositoryItem(ParameterGroup):
    def __init__(
        self,
        geometry_provider: ProbeGeometryProvider,
        settings: ProbeSettings,
        builder: ProbeSequenceBuilder,
    ) -> None:
        super().__init__()
        self._geometry_provider = geometry_provider
        self._settings = settings
        self._builder = builder
        self._probe_seq = ProbeSequence(array=None, opr_weights=None, pixel_geometry=None)

        self._add_group('builder', builder, observe=True)
        self._rebuild()

    def assign_item(self, item: ProbeRepositoryItem) -> None:
        self.set_builder(item.get_builder().copy())
        self._rebuild()

    def assign(self, probe: ProbeSequence) -> None:
        builder = FromMemoryProbeBuilder(self._settings, probe)
        self.set_builder(builder)

    def sync_to_settings(self) -> None:
        for parameter in self.parameters().values():
            parameter.sync_value_to_parent()

        self._builder.sync_to_settings()

    def get_probes(self) -> ProbeSequence:
        return self._probe_seq

    def get_builder(self) -> ProbeSequenceBuilder:
        return self._builder

    def set_builder(self, builder: ProbeSequenceBuilder) -> None:
        group = 'builder'
        self._remove_group(group)
        self._builder.remove_observer(self)
        self._builder = builder
        self._builder.add_observer(self)
        self._add_group(group, self._builder, observe=True)
        self._rebuild()

    def _rebuild(self) -> None:
        try:
            probe_seq = self._builder.build(self._geometry_provider)
        except Exception:
            logger.exception('Failed to rebuild probe!')
        else:
            self._probe_seq = probe_seq
            self.notify_observers()

    def _update(self, observable: Observable) -> None:
        if observable is self._builder:
            self._rebuild()
        else:
            super()._update(observable)
