'''
Created on 01-Apr-2021
@author: bthakur
'''
import os 
#import decimal
from functools import reduce
import unicodedata as ud

import numpy as np
import random
import inspect
import traceback

from ..helper import BaseHelper
from xv.km.managers import BaseManager
from xv.km.kelements import KProblemTemplate
        

class UnicodeImageManager(BaseManager):
    '''
    UnicodeImageManager
    Example:
        ke = UnicodeImageManager()
        ke.getRandomProblem()
        ke.getRandomProblem(problem_type = 0)
        ...
        
        ke.printProblem()
        ke.printAnswer()
        ke.printSolution()
        
        ke.printProblemTypes()
        
    '''    
    _DEFAULT_IMAGE_NAME = 'GIRL'
    
    def __new__(cls, *args, **kwargs):
        instance = super(UnicodeImageManager, cls).__new__(cls, *args, **kwargs)
        for base_class in UnicodeImageManager.__bases__:
            base_class.__init__(instance, *args, **kwargs)
        return instance
    
    def __init__(self, *args, **kwargs):
        self.base_helper = BaseHelper(**kwargs)
        
        self._shapes = self._init_shapes()
        pass


    def _init_shapes(self,
            start = 1, 
            end = 16 ** 6, 
            startswith = None, 
            endswith = None, 
            search = None
            ):
        
        syms = []
        
        unicode_ranges = [
                (127001, 130000),
                (63001, 65535),
                (0, 127),
                (128, 2047),     
                (2048, 63000),
                (65536, 127000),
                (130001, 1114111)
                ]
        
        for unicode_start, unicode_end in unicode_ranges:
            mod_start = max(unicode_start, start)
            mod_end = min(unicode_end, end)
            
            range_syms = self._list_unicode_range(start = mod_start, 
                                                  end = mod_end, 
                                                  startswith = startswith, 
                                                  endswith = endswith,  
                                                  search = search)
            
            syms.extend(range_syms)
            pass
        return syms
        pass


    def _list_unicode_range(self,
            start = 1, 
            end = 16 ** 3, 
            startswith = None, 
            endswith = None, 
            search = None
            ):
        syms = []
        for num in range (start, end + 1):
            try:
                
                u, uHtml, s, name = self._getUncodeStr_LARGE(num)
                
                if name is None:
                    #self._log(f"Name is None for num = {num}")
                    continue
    
                isValid = True
                if startswith:
                    if not name.lower().startswith(startswith.lower()):
                        isValid = False
    
                if endswith:
                    if not name.lower().endswith(endswith.lower()):
                        isValid = False
    
                if search:
                    if not search.lower() in name.lower():
                        isValid = False
    
                if isValid:
                    syms.append((u, uHtml, s, name, num ))
                pass
            except Exception as e:
                sError = f"""Error {self.__class__.__name__}.{inspect.stack()[0][3]}:
                {e}"""
                print(sError)
                
            pass
        
        self._log(f"Unicode for range({start},{end}) = {len(syms)}")
        return syms
        pass
    
    def _getUncodeStr_LARGE(self, num):
        #print(f"str(hex(num)) = {str(hex(num))}")
        
        strNum = '000000000' + str(hex(num))[2:]
        #print(f"strNum = {strNum}")
        #print(f"strNum[-5:] = {strNum[-5:]}")
        #print(f"strNum[-6:] = {strNum[-6:]}")
        
        strNum = strNum[-6:]
        #print(f"strNum = {strNum}")
        
        u = f"\\U00{strNum}"
        uHtml = f"&#x{strNum};"
        
        s = u.encode('ASCII').decode('unicode-escape')
        name = ud.name(s, None)
        return u, uHtml, s, name
        pass

    def search_images(self, search):
        return [
            shape
            for shape in self._shapes
            if search.lower() in shape[3].lower()
            ]

    def searchFirst(self, search):
        for shape in self._shapes:
            if search.lower() == shape[3].lower():
                return shape

        for shape in self._shapes:
            if search.lower() in shape[3].lower():
                return shape

        return None


    
    def _init_problem_types(self, *args, **kwargs):
        self._append_problem_types([
            '_problem_draw_shape',
            '_problem_list_all_shapes',
            ])
        pass
    
    def _problem_draw_shape(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        search = kwargs.get('search', UnicodeImageManager._DEFAULT_IMAGE_NAME)
        count = kwargs.get('count', 1)
        size = kwargs.get('size', 50)
        
        shape = self.searchFirst(search)
        
        if shape is None:
            problemTemplate.add_problem(f"""
            Failed to get the shape {search}
            """.strip(), "text/html")
            return problemTemplate


        statement = f"""
        Draw shape {search}
        """
        problemTemplate.add_problem(statement, "text/html")
        
        soln_s = f"""
        <div>
        <span style="font-size:{size}px;">{shape[2] * count}</span> \t {shape[3]} \t {shape[0]} 
        </div>
        You may search shapes like: 
        ke.getRandomProblem(problem_type = 0, search = 'boy')
        
        Draw more than one image
        ke.getRandomProblem(problem_type = 0, search = 'boy', count = 3)
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')

        answer_s = f"""
            from IPython.display import HTML
            display(HTML('{shape[2]}'))
            display(HTML('{shape[1]}'))
            
            {shape}
        """
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    
    def _problem_list_all_shapes(self, *args, **kwargs):
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        search = kwargs.get('search', UnicodeImageManager._DEFAULT_IMAGE_NAME)
        
        shapes = self.search_images(search)
        
        
        if len(shapes) < 1:
            problemTemplate.add_problem(f"""
            Failed to get the shapes
            search = {search}
            """.strip(), "text/html")
            return problemTemplate

    
        shapes_str_list = [
            f"{shape[2]} \t {shape[3]}"
            for shape in shapes
            ]

        shapes_str = "\n".join(shapes_str_list)  
        
        statement = f"""
        Write list of unicode images related to term {search}
        """
        problemTemplate.add_problem(statement, "text/html")
        
        soln_s = f"""
        {shapes_str}
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        answer_s = soln_s
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
    
    
    def _problem_TEMPLATE(self, *args, **kwargs):
                
        problemTemplate = KProblemTemplate()
        problemTemplate.problem_type = kwargs.get('problem_type')
        
        statement = f"""
        """
        problemTemplate.add_problem(statement, "text/html")
        
        soln_s = f"""
        """
        problemTemplate.add_solution(soln_s.strip(), 'text/html')


        answer_s = f""""""
        problemTemplate.add_answer(answer_s.strip(), 'text/html')
        
        return problemTemplate
