# -*- coding: utf-8 -*-
"""

Code Usage
----------

The ``Parser`` class can be used to generate vakya parses thus:

.. code-block:: python

        from sanskrit_parser import Parser
        string = "astyuttarasyAMdiSi"
        input_encoding = "SLP1"
        output_encoding = "SLP1"
        parser = Parser(input_encoding=input_encoding,
                        output_encoding=output_encoding,
                        replace_ending_visarga='s')
        print('Splits:')
        for split in parser.split(string, limit=10):
            print(f'Lexical Split: {split}')
            for i, parse in enumerate(split.parse(limit=2)):
                print(f'Parse {i}')
                print(f'{parse}')
            break


This produces the output::

    Lexical Split: ['asti', 'uttarasyAm', 'diSi']
    Parse 0
    asti => (asti, ['samAsapUrvapadanAmapadam', 'strIliNgam']) : samasta of uttarasyAm
    uttarasyAm => (uttara#1, ['saptamIviBaktiH', 'strIliNgam', 'ekavacanam'])
    diSi => (diS, ['saptamIviBaktiH', 'ekavacanam', 'strIliNgam']) : viSezaRa of uttarasyAm
    Parse 1
    asti => (asti, ['samAsapUrvapadanAmapadam', 'strIliNgam']) : samasta of uttarasyAm
    uttarasyAm => (uttara#2, ['saptamIviBaktiH', 'strIliNgam', 'ekavacanam']) : viSezaRa of diSi
    diSi => (diS#2, ['saptamIviBaktiH', 'strIliNgam', 'ekavacanam'])
    Parse 2
    asti => (as#1, ['kartari', 'praTamapuruzaH', 'law', 'parasmEpadam', 'ekavacanam', 'prATamikaH'])
    uttarasyAm => (uttara#2, ['saptamIviBaktiH', 'strIliNgam', 'ekavacanam']) : viSezaRa of diSi
    diSi => (diS, ['saptamIviBaktiH', 'ekavacanam', 'strIliNgam']) : aDikaraRam of asti

"""

import time
import json
import abc
import warnings
from dataclasses import dataclass
from typing import Sequence
from sanskrit_parser.base.sanskrit_base import SCHEMES, SanskritObject, SLP1
from sanskrit_parser.base.sanskrit_base import SanskritNormalizedString, SanskritString
from sanskrit_parser.parser.sandhi_analyzer import LexicalSandhiAnalyzer
from sanskrit_parser.parser.datastructures import VakyaGraph, VakyaGraphNode
import logging

logger = logging.getLogger(__name__)


class Serializable(abc.ABC):
    """ Base class to indicate an object is serializable into JSON """

    @abc.abstractmethod
    def serializable(self):
        ''' Return an object that can be serialized by json.JSONEncoder '''
        pass


class JSONEncoder(json.JSONEncoder):

    def default(self, o):
        if isinstance(o, Serializable):
            return o.serializable()
        return super().default(o)


class Parser():

    def __init__(self, strict_io: bool = False, input_encoding: str = None,
                 output_encoding: str = 'SLP1', lexical_lookup: str = "combined",
                 score: bool = True, split_above: int = 5,
                 replace_ending_visarga: str = None, fast_merge: bool = True):
        self.strict_io = strict_io
        if input_encoding is not None:
            self.input_encoding = SCHEMES[input_encoding]
        else:
            self.input_encoding = None
        self.output_encoding = SCHEMES[output_encoding]
        self.lexical_lookup = lexical_lookup
        self.score = score
        self.split_above = split_above
        self.replace_ending_visarga = replace_ending_visarga
        self.fast_merge = fast_merge
        self.sandhi_analyzer = LexicalSandhiAnalyzer(self.lexical_lookup)

    def _maybe_pre_segment(self, input_string: str, pre_segmented: bool
                           ):
        ''' Pre-process pre-segmented input if necessary '''
        if not pre_segmented:
            s = SanskritNormalizedString(input_string,
                                         encoding=self.input_encoding,
                                         strict_io=self.strict_io,
                                         replace_ending_visarga=self.replace_ending_visarga)
            logger.info(f"Input String in SLP1: {s.canonical()}")
            return s
        else:
            logger.debug("Pre-Segmented")
            s = []
            for seg in input_string.split(" "):
                o = SanskritObject(seg,
                                   encoding=self.input_encoding,
                                   strict_io=self.strict_io,
                                   replace_ending_visarga='r')
                ts = self.sandhi_analyzer.getMorphologicalTags(o, tmap=True)
                if ts is None:
                    # Possible sakaranta
                    # Try by replacing end visarga with 's' instead
                    o = SanskritObject(seg,
                                       encoding=self.input_encoding,
                                       strict_io=self.strict_io,
                                       replace_ending_visarga='s')
                    ts = self.sandhi_analyzer.getMorphologicalTags(o, tmap=True)
                if ts is None:
                    logger.warning(f"Unknown pada {seg} - will be split")
                    _s = list(self.split(seg, pre_segmented=False, limit=1))[0]
                    logger.info(f"Split {_s}")
                    s.extend(_s.split)
                    if _s is None:
                        logger.warning(f"Unknown pada {seg} - cannot be split")
                else:
                    s.append(o)
            logger.info(f"Input String in SLP1: {' '.join([x.canonical() for x in s])}")
            return s