#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of PyHOPE
#
# Copyright (c) 2024 Numerics Research Group, University of Stuttgart, Prof. Andrea Beck
#
# PyHOPE is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# PyHOPE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# PyHOPE. If not, see <http://www.gnu.org/licenses/>.

# ==================================================================================================================================
# Mesh generation library
# ==================================================================================================================================
# ----------------------------------------------------------------------------------------------------------------------------------
# Standard libraries
# ----------------------------------------------------------------------------------------------------------------------------------
from typing import Optional, Final, final
# ----------------------------------------------------------------------------------------------------------------------------------
# Third-party libraries
# ----------------------------------------------------------------------------------------------------------------------------------
from alive_progress import alive_bar
# ----------------------------------------------------------------------------------------------------------------------------------
# Local imports
# ----------------------------------------------------------------------------------------------------------------------------------
# ==================================================================================================================================


# Number of elements to display in the progress bar
barElems: Final[int] = int(1.E5)


@final
class ProgressBar:
    """ Provide a progress bar outside of the context manager
    """
    __slots__ = ('bar', '_cm', '_len', '_cur', '_chunk', '_pend')

    def __init__(self,
                 title       : Optional[str],
                 value       : int,
                 length      : int  = 33,
                 threshold   : int  = barElems,
                 chunk       : int  = 0,
                 enrich_print: bool = True) -> None:
        # Local imports ----------------------------------------
        from pyhope.common.common import IsInteractive
        # ------------------------------------------------------
        self._len   = value
        self._cur   = 0
        self._chunk = chunk
        self._pend  = 0
        self._cm    = None
        self.bar    = None

        if value > threshold and IsInteractive():
            self._cm = alive_bar(title=title, total=value, length=length, enrich_print=enrich_print)

            # Initialize the progress bar
            self.bar = self._cm.__enter__()

    def step(self, steps: int = 1) -> None:
        # Assume sane calls (steps > 0, no large overshoot); keep logic minimal and fast
        if self.bar is None:
            return None

        if not self._chunk:
            self.bar(steps)
            self._cur += steps
            return None

        # Chunked updates: emit in multiples of _chunk, flush remainder at the end
        self._pend += steps
        self._cur  += steps

        if self._pend >= self._chunk:
            full = (self._pend // self._chunk) * self._chunk
            if full:
                self.bar(full)
                self._pend -= full

        if self._cur >= self._len and self._pend:
            self.bar(self._pend)
            self._pend = 0

    def title(self, title: str) -> None:
        if self.bar is not None:
            self.bar.title(title)

    def close(self) -> None:
        if self._cm is not None:
            _ = self._cm.__exit__(None, None, None)
