# -*- coding: utf-8 -*-
#
#  Copyright 2020-2022 Ramil Nugmanov <nougmanoff@protonmail.com>
#  This file is part of chython.
#
#  chython is free software; you can redistribute it and/or modify
#  it under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 3 of the License, or
#  (at your option) any later version.
#
#  This program 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 Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public License
#  along with this program; if not, see <https://www.gnu.org/licenses/>.
#
from traceback import format_exc
from .mol import MOLRead
from ...exceptions import EmptyMolecule


class RXNRead:
    def __init__(self, line, ignore=False, log_buffer=None):
        self.__reactants_count = int(line[:3])
        self.__products_count = int(line[3:6]) + self.__reactants_count
        self.__reagents_count = int(line[6:].rstrip() or 0) + self.__products_count
        self.__molecules = []
        self.__ignore = ignore
        self.__errors = []
        if log_buffer is None:
            log_buffer = []
        self.__log_buffer = log_buffer

    def __call__(self, line):
        if self.__parser:
            try:
                x = self.__parser(line)
            except ValueError:
                if not self.__ignore:
                    raise
                if (lm := len(self.__molecules)) < self.__reactants_count:
                    self.__reactants_count -= 1
                    self.__products_count -= 1
                    self.__reagents_count -= 1
                    m = 'reactants'
                elif lm < self.__products_count:
                    self.__products_count -= 1
                    self.__reagents_count -= 1
                    m = 'products'
                else:
                    self.__reagents_count -= 1
                    m = 'reagents'

                self.__errors.append({'chytorch_molecule_role': m})
                self.__log_buffer.append(format_exc())
                self.__log_buffer.append('bad molecule ignored')
                self.__parser = None
                if lm == self.__reagents_count:  # empty molecule is last in list
                    self.__rend = True
                    return True
                self.__empty_skip = True
            else:
                if x:
                    self.__im = 4
                    mol = self.__parser.getvalue()
                    if self.__title:
                        mol['title'] = self.__title
                    self.__molecules.append(mol)
                    self.__parser = None
                    if len(self.__molecules) == self.__reagents_count:
                        self.__rend = True
                        return True
        elif self.__empty_skip:
            if not line.startswith('$MOL'):
                return
            self.__empty_skip = False
            self.__im = 3
        elif self.__rend:
            raise ValueError('parser closed')
        elif self.__im == 4:
            if not line.startswith('$MOL'):
                raise ValueError('invalid RXN')
            self.__im = 3
        elif self.__im:
            if self.__im == 3:
                self.__title = line.strip()
            self.__im -= 1
        else:
            try:
                self.__parser = MOLRead(line, self.__log_buffer)
            except ValueError as e:
                if not self.__ignore:
                    raise
                if (lm := len(self.__molecules)) < self.__reactants_count:
                    self.__reactants_count -= 1
                    self.__products_count -= 1
                    self.__reagents_count -= 1
                    m = 'reactants'
                elif lm < self.__products_count:
                    self.__products_count -= 1
                    self.__reagents_count -= 1
                    m = 'products'
                else:
                    self.__reagents_count -= 1
                    m = 'reagents'

                if not isinstance(e, EmptyMolecule):
                    self.__errors.append({'chytorch_molecule_role': m})
                    self.__log_buffer.append(format_exc())
                    self.__log_buffer.append('bad molecule ignored')
                else:
                    self.__log_buffer.append('empty molecule ignored')

                if lm == self.__reagents_count:  # empty molecule is last in list
                    self.__rend = True
                    return True
                self.__empty_skip = True

    def getvalue(self):
        if self.__rend:
            return {'reactants': self.__molecules[:self.__reactants_count],
                    'products': self.__molecules[self.__reactants_count:self.__products_count],
                    'reagents': self.__molecules[self.__products_count:self.__reagents_count],
                    'meta': {}, 'errors': self.__errors}
        raise ValueError('reaction not complete')

    __parser = None
    __empty_skip = __rend = False
    __im = 4


__all__ = ['RXNRead']
