#!/usr/bin/env python
from __future__ import print_function
from indic_transliteration import sanscript
from indic_transliteration import detect
from sanskrit_parser.util import normalization

from contextlib import contextmanager
import logging
import six

# Wrap scheme names defined in sanscript
BENGALI = sanscript.BENGALI
DEVANAGARI = sanscript.DEVANAGARI
GUJARATI = sanscript.GUJARATI
GURMUKHI = sanscript.GURMUKHI
KANNADA = sanscript.KANNADA
MALAYALAM = sanscript.MALAYALAM
ORIYA = sanscript.ORIYA
TAMIL = sanscript.TAMIL
TELUGU = sanscript.TELUGU
HK = sanscript.HK
IAST = sanscript.IAST
ITRANS = sanscript.ITRANS
KOLKATA = sanscript.KOLKATA
SLP1 = sanscript.SLP1
VELTHUIS = sanscript.VELTHUIS
WX = sanscript.WX

# Dict defined so autodetect can work
SCHEMES = {
    'Bengali': BENGALI,
    'Devanagari': DEVANAGARI,
    'Gujarati': GUJARATI,
    'Gurmukhi': GURMUKHI,
    'Kannada': KANNADA,
    'Malayalam': MALAYALAM,
    'Oriya': ORIYA,
    'Tamil': TAMIL,
    'Telugu': TELUGU,
    'HK': HK,
    'IAST': IAST,
    'ITRANS': ITRANS,
    'Kolkata': KOLKATA,
    'SLP1': SLP1,
    'Velthuis': VELTHUIS,
    'WX': WX
}

logger = logging.getLogger(__name__)
denormalize = False


class SanskritString(object):
    """ Sanskrit String Class: Base of the class hierarchy

        Attributes:
           thing(str)   : thing to be represented
           encoding(str): SanskritBase encoding of thing as passed (eg: SanskritBase.HK, SanskritBase.DEVANAGARI)
        Args:
           thing(str):    As above
           encoding(str): As above

    """

    def __init__(self, thing, encoding=None, unicode_encoding='utf-8'):
        assert isinstance(thing, six.string_types)
        # Encode early, unicode everywhere, decode late is the philosophy
        # However, we need to accept both unicode and non unicode strings
        # We are udAramatiH
        if isinstance(thing, six.text_type):
            self.thing = thing
        else:
            self.thing = six.text_type(thing, unicode_encoding)
        if encoding is None:
            # Autodetect Encoding
            encoding = SCHEMES[detect.detect(self.thing)]
        if encoding != SLP1:
            # Convert to SLP1
            self.thing = sanscript.transliterate(self.thing, encoding, SLP1)
            # At this point, we are guaranteed that internal
            # representation is in SLP1

    def transcoded(self, encoding=None, strict_io=True):
        """ Return a transcoded version of self

            Args:
              encoding(SanskritObject.Scheme):
            Returns:
              str: transcoded version
        """
        s = self.thing
        if not strict_io:
            s = normalization.denormalize(s)
        return sanscript.transliterate(s, SLP1, encoding)

    def canonical(self, strict_io=True):
        """ Return canonical transcoding (SLP1) of self
        """
        return self.transcoded(SLP1, strict_io)

    def devanagari(self, strict_io=True):
        """ Return devanagari transcoding of self
        """
        return self.transcoded(DEVANAGARI, strict_io)

    # Updates internal string, leaves everything else alone
    # Not to be used in all cases, as this is very limited
    def update(self, s, encoding=None):
        self.thing = s
        if encoding is not None:
            self.encoding = encoding

    def __str__(self):
        global denormalize
        s = self.transcoded(SLP1)
        if denormalize:
            s = normalization.denormalize(s)
        return s

    def __repr__(self):
        return str(self)

    def __getitem__(self, i):
        return self.canonical()[i]

    def __len__(self):
        return len(self.canonical())


class SanskritImmutableString(SanskritString):
    """ Immutable version of SanskritString
    """
    def __init__(self, thing=None, encoding=None, unicode_encoding='utf-8'):
        super().__init__(thing, encoding, unicode_encoding)

    def __hash__(self):
        return hash(str(self))

    def __eq__(self, other):
        return str(self) == str(other)

    def __ne__(self, other):