import curses
import sys
from . import wgwidget
from . import wgtextbox
from . import wgtitlefield

class TextTokens(wgtextbox.Textfield,wgwidget.Widget):
    """This is an experiemental widget"""
    
    # NB IT DOES NOT CURRENTLY SUPPORT THE HIGHLIGHTING COLORS
    # OF THE TEXTFIELD CLASS.
    
    
    def __init__(self, *args, **keywords):        
        super(TextTokens, self).__init__(*args, **keywords)
        self.begin_at        = 0 # which token to begin display with
        self.maximum_string_length = self.width - 2
        self.left_margin     = 0
        self.cursor_position = 0
        
        self.important = False
        self.highlight = False
        self.show_bold = False

    def find_cursor_offset_on_screen(self, position):
        index  = self.begin_at 
        offset = 0
        while index < position:
            offset += len(self.decode_token(self.value[index]))
            index  += 1
        return offset - self.begin_at # I don't quite understand
                                      # why the - self.begin_at is needed
                                      # but without it the cursor and screen
                                      # get out of sync
    
    def decode_token(self, tk):
        r = ''.join(tk)
        if len(r) > 1:
            r = ' [' + r + '] '
        if isinstance(r, bytes):
            r = r.decode(self.encoding, 'replace')
        return r
    
    # text and highlighting generator.
    def get_literal_text_and_highlighting_generator(self, start_at=0,):
        # could perform initialization here.
        index = start_at
        string_length = 0
        output = ''
        while string_length <= self.maximum_string_length and len(self.value) > index:
            token_output = self.decode_token(self.value[index])
            if isinstance(token_output, bytes):
                token_output = token_output.decode(self.encoding, 'replace')
            highlighting = [curses.A_NORMAL for c in token_output]
            yield(token_output, highlighting)
            index += 1
    
    def get_literal_text_to_display(self, start_at=0,):
        g = self.get_literal_text_and_highlighting_generator(start_at=start_at)
        txt = []
        highlighting = []
        for i in g:
            txt += i[0]
            highlighting += i[1]
        return txt, highlighting
            
                
    def update(self, clear=True, cursor=True):
        if clear: self.clear()
        if self.begin_at    < 0: self.begin_at = 0
        if self.left_margin >= self.maximum_string_length:
            raise ValueError
            
        if self.cursor_position < 0:
            self.cursor_position = 0
        if self.cursor_position > len(self.value):
            self.cursor_position = len(self.value)
        
        if self.cursor_position < self.begin_at:
            self.begin_at = self.cursor_position
        
        while self.find_cursor_offset_on_screen(self.cursor_position) > \
                 self.find_cursor_offset_on_screen(self.begin_at) + \
                 self.maximum_string_length - self.left_margin -1: # -1:
            self.begin_at += 1
    

        text, highlighting = self.get_literal_text_to_display(start_at=self.begin_at)
        if self.do_colors():
            if self.important:
                color = self.parent.theme_manager.findPair(self, 'IMPORTANT') | curses.A_BOLD            
            else:
                color = self.parent.theme_manager.findPair(self, self.color)
            if self.show_bold:
                color = color | curses.A_BOLD
            if self.highlight:
                if not self.editing:
                    color = color | curses.A_STANDOUT
                else:
                    color = color | curses.A_UNDERLINE
            highlighting = [color for c in highlighting if c == curses.A_NORMAL]
        else:
            color = curses.A_NORMAL
            if self.important or self.show_bold:
                color = color | curses.A_BOLD
            if self.important:
                color = color | curses.A_UNDERLINE
            if self.highlight:
                if not self.editing:
                    color = color | curses.A_STANDOUT
                else:
                    color = color | curses.A_UNDERLINE
            highlighting = [color for c in highlighting if c == curses.A_NORMAL]
        
        self._print(text, highlighting)
        
        if self.editing and cursor:
            self.print_cursor()
        

    def _print(self, text, highlighting):
        self.add_line(self.rely, 
                      self.relx + self.left_margin,
                      text,
                      highlighting,
                      self.maximum_string_length - self.left_margin
                      )
    def print_cursor(self):
        _cur_loc_x = self.cursor_position - self.begin_at + self.relx + self.left_margin
        try:
            char_under_cur = self.decode_token(self.value[self.cursor_position]) #use the real value
            char_under_cur = self.safe_string(char_under_cur)
        except IndexError:
            char_under_cur = ' '
        
        if isinstance(char_under_cur, bytes):
            char_under_cur = char_under_cur.decode(self.encoding, 'replace')
        
        offset = self.find_cursor_offset_on_screen(self.cursor_position)
        if self.do_colors():
            ATTR_LIST = self.parent.theme_manager.findPair(self) | curses.A_STANDOUT
        else:
            ATTR_LIST = curses.A_STANDOUT
    
        self.add_line(self.rely, 
             self.begin_at + self.relx + self.left_margin + offset,
            char_under_cur, 
            self.make_attributes_list(char_under_cur, ATTR_LIST),
            # I don't understand why the "- self.begin_at" is needed in the following line
            # but it is or the cursor can end up overrunning the end of the widget.
            self.maximum_string_length+1 - self.left_margin - offset - self.begin_at,
            )

    def h_addch(self, inp):
        if self.editable:
            #self.value = self.value[:self.cursor_position] + curses.keyname(input) \
            #   + self.value[self.cursor_position:]
            #self.cursor_position += len(curses.keyname(input))
            
            # workaround for the metamode bug:
            if self._last_get_ch_was_unicode == True and isinstance(self.value, bytes):
                # probably dealing with python2.
                ch_adding = inp
                self.value = self.value.decode()
            elif self._last_get_ch_was_unicode == True:
                ch_adding = inp
            else:
                try:
                    ch_adding = chr(inp)
                except TypeError:
                    ch_adding = input
            self.value = self.value[:self.cursor_position] + [ch_adding,] \
                + self.value[self.cursor_position:]
            self.cursor_position += len(ch_adding)
    
    def display_value(self, vl):
        return vl
    
    
    def calculate_area_needed(self):
        "Need one line of screen, and any width going"
        return 1,0
        


class TitleTextTokens(wgtitlefield.TitleText):
    _entry_type = TextTokens