"""
HTML parser
"""
import os
import webbrowser
import tkinter as tk
from tkinter import font
from copy import deepcopy
from PIL import Image, ImageTk
from html.parser import HTMLParser
from collections import OrderedDict
import requests
from io import BytesIO


# __________________________________________________________________________________________________
class Defs:
    DEFAULT_TEXT_FONT_FAMILY = ("Segoe ui", "Calibri", "Helvetica", "TkTextFont")
    FONT_SIZE = 14
    PREFORMATTED_FONT_FAMILY = ("Courier", "DejaVu Sans Mono", "TkFixedFont")
    HEADINGS_FONT_SIZE = {
        'h1': 32,
        'h2': 24,
        'h3': 18,
        'h4': 16,
        'h5': 13,
        'h6': 10,
    }


class HTML:
    # ----------------------------------------------------------------------------------------------
    """
    List of supported HTML tags and attrs 
    """

    class Tag():
        BR = 'br'
        UL = 'ul'
        OL = 'ol'
        LI = 'li'
        IMG = 'img'
        A = 'a'
        B = 'b'
        STRONG = 'strong'
        I = 'i'
        EM = 'em'
        U = 'u'
        MARK = 'mark'
        SPAN = 'span'
        DIV = 'div'
        P = 'p'
        PRE = 'pre'
        CODE = 'code'
        H1 = 'h1'
        H2 = 'h2'
        H3 = 'h3'
        H4 = 'h4'
        H5 = 'h5'
        H6 = 'h6'

    class Attrs():
        STYLE = 'style'
        HREF = 'href'
        SRC = 'src'
        WIDTH = 'width'
        HEIGHT = 'height'
        TYPE = 'type'

    class TypeOrderedList():
        _1 = '1'
        a = 'a'
        A = 'A'

    class Style():
        COLOR = 'color'
        BACKGROUD_COLOR = 'background-color'
        FONT_FAMILY = 'font-family'
        FONT_SIZE = 'font-size'
        TEXT_ALIGN = 'text-align'
        TEXT_DECORATION = 'text-decoration'

    class StyleTextDecoration():
        UNDERLINE = 'underline'
        LINE_THROUGH = 'line-through'

    HEADING_TAGS = (
        Tag.H1,
        Tag.H2,
        Tag.H3,
        Tag.H4,
        Tag.H5,
        Tag.H6,
    )

    TEXT_ALIGN_TAGS = HEADING_TAGS + (
        Tag.UL,
        Tag.OL,
        Tag.LI,
        Tag.DIV,
        Tag.P,
        Tag.PRE,
        Tag.CODE,
    )

    NEW_LINE_TAGS = HEADING_TAGS + (
        Tag.UL,
        Tag.OL,
        Tag.DIV,
        Tag.P,
        Tag.PRE,
        Tag.CODE,
    )

    STYLE_TAGS = TEXT_ALIGN_TAGS + (
        Tag.A,
        Tag.B,
        Tag.STRONG,
        Tag.I,
        Tag.EM,
        Tag.U,
        Tag.MARK,
        Tag.SPAN,
    )


# --------------------------------------------------------------------------------------------------
# Text widget defs

class WCfg():
    KEY = "config"
    BACKGROUND = "background"
    FOREGROUND = "foreground"
    JUSTIFY = "justify"
    TABS = "tabs"


class Fnt():
    KEY = "font"
    FAMILY = "family"
    SIZE = "size"
    WEIGHT = "weight"
    SLANT = "slant"
    UNDERLINE = "underline"
    OVERSTRIKE = "overstrike"


class Bind():
    KEY = "bind"
    LINK = "link"
    IMAGE = "image"


class WTag():
    START_INDEX = "start_index"
    END_INDEX = "end_index"


DEFAULT_STACK = {
    WCfg.KEY: {
        WCfg.BACKGROUND: [],
        WCfg.FOREGROUND: [("__DEFAULT__", "black")],
        WCfg.JUSTIFY: [("__DEFAULT__", 'left')],
        WCfg.TABS: [("__DEFAULT__", ())],
    },
    Fnt.KEY: {
        Fnt.FAMILY: [],
        Fnt.SIZE: [("__DEFAULT__", Defs.FONT_SIZE)],
        Fnt.WEIGHT: [("__DEFAULT__", 'normal')],
        Fnt.SLANT: [("__DEFAULT__", 'roman')],
        Fnt.UNDERLINE: [("__DEFAULT__", False)],
        Fnt.OVERSTRIKE: [("__DEFAULT__", False)],
    },
    Bind.KEY: {
        Bind.LINK: [("__DEFAULT__", None)],
    },
}


# __________________________________________________________________________________________________
# functions
def get_existing_font(font_families):
    # ------------------------------------------------------------------------------------------
    try:
        return next(filter(lambda f: f.lower() in (f.lower() for f in font.families()), font_families))
    except:
        return "TkTextFont"


# __________________________________________________________________________________________________
# classes
class HLinkSlot():
    # ----------------------------------------------------------------------------------------------

    def __init__(self, w, tag_name, url):
        # ------------------------------------------------------------------------------------------
        self._w = w
        self.tag_name = tag_name
        self.URL = url

    def call(self, event):
        # ------------------------------------------------------------------------------------------
        webbrowser.open(self.URL)
        self._w.tag_config(self.tag_name, foreground="purple")

    def enter(self, event):
        # ------------------------------------------------------------------------------------------
        self._w.config(cursor="hand2")

    def leave(self, event):
        # ------------------------------------------------------------------------------------------
        self._w.config(cursor="")


class ListTag():
    # ----------------------------------------------------------------------------------------------
    def __init__(self, ordered: bool, list_type=None):
        # ------------------------------------------------------------------------------------------
        self.ordered = ordered
        self.type = list_type
        self.index = 0

    def add(self):
        # ------------------------------------------------------------------------------------------
        if self.ordered:
            self.index += 1

    def line_index(self):
        # ------------------------------------------------------------------------------------------
        if self.ordered:
            if self.type == HTML.TypeOrderedList._1:
                return str(self.index)
            elif self.type == HTML.TypeOrderedList.a:
                return self._index_to_str(self.index).lower()
            elif self.type == HTML.TypeOrderedList.A:
                return self._index_to_str(self.index).upper()
        else:
            return chr(8226)

    def _index_to_str(self, index):
        # ------------------------------------------------------------------------------------------
        prefix = ""
        if index > 26:
            prefix = self._index_to_str(index // 26)
            index = index % 26

        return prefix + chr(0x60 + index)


class HTMLTextParser(HTMLParser):
    # ----------------------------------------------------------------------------------------------

    def __init__(self):
        # ------------------------------------------------------------------------------------------
        super().__init__()
        # set list tabs
        self.cached_images = {}

        self.DEFAULT_TEXT_FONT_FAMILY = get_existing_font(Defs.DEFAULT_TEXT_FONT_FAMILY)
        self.PREFORMATTED_FONT_FAMILY = get_existing_font(Defs.PREFORMATTED_FONT_FAMILY)

    def _parse_attrs(self, attrs):
        # ------------------------------------------------------------------------------------------
        attrs_dict = {
            HTML.Attrs.STYLE: {},
            HTML.Attrs.HREF: None,
            HTML.Attrs.SRC: None,
            HTML.Attrs.WIDTH: None,
            HTML.Attrs.HEIGHT: None,
            HTML.Attrs.TYPE: None,
        }
        for k, v in attrs:
            k = k.lower()
            if k == HTML.Attrs.STYLE:
                for p in v.split(";"):
                    try:
                        p_key = p.split(":")[0].strip().lower()
                        p_value = p.split(":")[1].strip().lower()
                        attrs_dict[HTML.Attrs.STYLE][p_key] = p_value
                    except:
                        pass
            elif k in (HTML.Attrs.HREF, HTML.Attrs.SRC, HTML.Attrs.WIDTH, HTML.Attrs.HEIGHT, HTML.Attrs.TYPE):
                attrs_dict[k] = v
        return attrs_dict

    def _w_tags_add(self):
        # ------------------------------------------------------------------------------------------
        tag = {
            WCfg.KEY: {},
            Fnt.KEY: {},
            Bind.KEY: {}
        }

        for k1 in (WCfg.KEY, Fnt.KEY, Bind.KEY):
            for k2 in DEFAULT_STACK[k1]:
                tag[k1][k2] = self.stack[k1][k2][-1][1]

        self._w_tags[self._w.index("end-1c")] = tag

    def _stack_get_main_key(self, key):
        # ------------------------------------------------------------------------------------------
        if key in WCfg.__dict__.values():
            main_key = WCfg.KEY
        elif key in Fnt.__dict__.values():
            main_key = Fnt.KEY
        elif key in Bind.__dict__.values():
            main_key = Bind.KEY
        else:
            raise ValueError("key %s doesn't exists" % key)

        return main_key

    def _stack_add(self, tag, key, value=None):
        # ------------------------------------------------------------------------------------------
        main_key = self._stack_get_main_key(key)

        if value is None:
            # if value is none, add the previous value
            value = self.stack[main_key][key][-1][1]

        self.stack[main_key][key].append((tag, value))

    def _stack_index(self, tag, key):
        # ------------------------------------------------------------------------------------------
        main_key = self._stack_get_main_key(key)
        index = None
        for i, v in enumerate(self.stack[main_key][key]):
            if v[0] == tag:
                index = i

        return index

    def _stack_pop(self, tag, key):
        # ------------------------------------------------------------------------------------------
        main_key = self._stack_get_main_key(key)

        index = None
        if len(self.stack[main_key][key]) > 1:
            index = self._stack_index(tag, key)

        if index is not None:
            return self.stack[main_key][key].pop(index)[1]

    def _parse_styles(self, tag, attrs):
        # ------------------------------------------------------------------------------------------
        # -------------------------------------------------------------------------------- [ COLOR ]
        if HTML.Style.COLOR in attrs[HTML.Attrs.STYLE].keys():
            self._stack_add(tag, WCfg.FOREGROUND, attrs[HTML.Attrs.STYLE][HTML.Style.COLOR])
        elif tag == HTML.Tag.A and attrs[HTML.Attrs.HREF]:
            self._stack_add(tag, WCfg.FOREGROUND, "blue")
        else:
            self._stack_add(tag, WCfg.FOREGROUND)

        # ---------------------------------------------------------------------- [ BACKGROUD_COLOR ]
        if HTML.Style.BACKGROUD_COLOR in attrs[HTML.Attrs.STYLE].keys():
            self._stack_add(tag, WCfg.BACKGROUND, attrs[HTML.Attrs.STYLE][HTML.Style.BACKGROUD_COLOR])
        elif tag == HTML.Tag.MARK:
            self._stack_add(tag, WCfg.BACKGROUND, "yellow")
        else:
            self._stack_add(tag, WCfg.BACKGROUND)

        # -------------------------------------------------------------------------- [ FONT_FAMILY ]
        # font family
        if HTML.Style.FONT_FAMILY in attrs[HTML.Attrs.STYLE].keys():
            font_family = Defs.DEFAULT_TEXT_FONT_FAMILY
            for f in attrs[HTML.Attrs.STYLE][HTML.Style.FONT_FAMILY].split(","):
                f = f.strip()
                if f in map(lambda f: f.lower(), font.families()):
                    font_family = f
                    break
            self._stack_add(tag, Fnt.FAMILY, font_family)
        elif tag in (HTML.Tag.PRE, HTML.Tag.CODE):
            self._stack_add(tag, Fnt.FAMILY, self.PREFORMATTED_FONT_FAMILY)
        else:
            self._stack_add(tag, Fnt.FAMILY)

        # ---------------------------------------------------------------------------- [ FONT_SIZE ]
        if HTML.Style.FONT_SIZE in attrs[HTML.Attrs.STYLE].keys():
            size = Defs.FONT_SIZE
            if attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE].endswith("px"):
                if attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE][:-2].isdigit():
                    size = int(attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE][:-2])
            elif attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE].endswith(r"%"):
                if attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE][:-1].isdigit():
                    size = int((int(attrs[HTML.Attrs.STYLE][HTML.Style.FONT_SIZE][:-1]) * Defs.FONT_SIZE) / 100)
            self._stack_add(tag, Fnt.SIZE, size)
        elif tag.startswith('h') and len(tag) == 2:
            self._stack_add(tag, Fnt.SIZE, Defs.HEADINGS_FONT_SIZE[tag])
        else:
            self._stack_add(tag, Fnt.SIZE)

        # --------------------------------------------------------------------------- [ TEXT_ALIGN ]
        if HTML.Style.TEXT_ALIGN in attrs[HTML.Attrs.STYLE].keys() and tag in HTML.TEXT_ALIGN_TAGS:
            self._stack_add(tag, WCfg.JUSTIFY, attrs[HTML.Attrs.STYLE][HTML.Style.TEXT_ALIGN])
        else:
            self._stack_add(tag, WCfg.JUSTIFY)

        # ---------------------------------------------------------------------- [ TEXT_DECORATION ]
        if HTML.Style.TEXT_DECORATION in attrs[HTML.Attrs.STYLE].keys():
            if tag == HTML.Tag.STRONG:
                self._stack_add(tag, Fnt.UNDERLINE, False)
                self._stack_add(tag, Fnt.OVERSTRIKE, False)
            elif HTML.StyleTextDecoration.UNDERLINE in attrs[HTML.Attrs.STYLE][HTML.Style.TEXT_DECORATION]:
                self._stack_add(tag, Fnt.UNDERLINE, True)
                self._stack_add(tag, Fnt.OVERSTRIKE, False)
            elif HTML.StyleTextDecoration.LINE_THROUGH in attrs[HTML.Attrs.STYLE][HTML.Style.TEXT_DECORATION]:
                self._stack_add(tag, Fnt.UNDERLINE, False)
                self._stack_add(tag, Fnt.OVERSTRIKE, True)
            else:
                self._stack_add(tag, Fnt.UNDERLINE)
                self._stack_add(tag, Fnt.OVERSTRIKE)
        else:
            if tag == HTML.Tag.A and attrs[HTML.Attrs.HREF]:
                self._stack_add(tag, Fnt.UNDERLINE, True)
                self._stack_add(tag, Fnt.OVERSTRIKE, False)
            elif tag == HTML.Tag.U:
                self._stack_add(tag, Fnt.UNDERLINE, True)
                self._stack_add(tag, Fnt.OVERSTRIKE, False)
            else:
                self._stack_add(tag, Fnt.UNDERLINE)
                self._stack_add(tag, Fnt.OVERSTRIKE)

    def handle_starttag(self, tag, attrs):
        # ------------------------------------------------------------------------------------------
        tag = tag.lower()
        attrs = self._parse_attrs(attrs)

        if tag in HTML.STYLE_TAGS:
            # ---------------------------------------------------------------------- [ STYLED_TAGS ]
            self._parse_styles(tag, attrs)

            if tag == HTML.Tag.B or tag == HTML.Tag.STRONG or tag in HTML.HEADING_TAGS:
                self._stack_add(tag, Fnt.WEIGHT, "bold")

            elif tag == HTML.Tag.I or tag == HTML.Tag.EM:
                self._stack_add(tag, Fnt.SLANT, "italic")

            elif tag == HTML.Tag.A:
                self._stack_add(tag, Bind.LINK, attrs[HTML.Attrs.HREF])

            elif tag == HTML.Tag.OL:
                # ---------------------------------------------------------------- [ ORDERED_LISTS ]
                if attrs[HTML.Attrs.TYPE] and attrs[HTML.Attrs.TYPE] in HTML.TypeOrderedList.__dict__.values():
                    list_type = attrs[HTML.Attrs.TYPE]
                else:
                    list_type = HTML.TypeOrderedList._1
                self.list_tags.append(ListTag(ordered=True, list_type=list_type))

                tabs = []
                for i in range(len(self.list_tags)):
                    offset = 30 * (i + 1)
                    tabs += [offset, tk.RIGHT, offset + 5, tk.LEFT]
                self._stack_add(tag, WCfg.TABS, tabs)

            elif tag == HTML.Tag.UL:
                # -------------------------------------------------------------- [ UNORDERED_LISTS ]
                self.list_tags.append(ListTag(ordered=False))

                tabs = []
                for i in range(len(self.list_tags)):
                    offset = 30 * (i + 1)
                    tabs += [offset, tk.RIGHT, offset + 5, tk.LEFT]
                self._stack_add(tag, WCfg.TABS, tabs)

            elif tag == HTML.Tag.LI:
                # ------------------------------------------------------------------ [ LISTS_LINES ]
                level = len(self.list_tags)
                if level:
                    self.list_tags[-1].add()

                    if self.strip:
                        self._insert_new_line()

                    line_index = self.list_tags[-1].line_index()
                    if self.list_tags[-1].ordered:
                        line_index = "\t" + "\t\t" * (level - 1) + line_index + ".\t"
                    else:
                        line_index = "\t" + "\t\t" * (level - 1) + line_index + "\t"

                    self._stack_add(tag, Fnt.UNDERLINE, False)
                    self._stack_add(tag, Fnt.OVERSTRIKE, False)
                    self._w_tags_add()
                    self._w.insert(tk.INSERT, line_index)
                    self._stack_pop(tag, Fnt.UNDERLINE)
                    self._stack_pop(tag, Fnt.OVERSTRIKE)

        elif tag == HTML.Tag.IMG and attrs[HTML.Attrs.SRC]:
            # -------------------------------------------------------------------- [ UNSTYLED_TAGS ]
            image = None
            print(attrs[HTML.Attrs.SRC], self.cached_images)
            if attrs[HTML.Attrs.SRC].startswith(("https://", "ftp://", "http://")):
                if attrs[HTML.Attrs.SRC] in self.cached_images.keys():
                    image = deepcopy(self.cached_images[attrs[HTML.Attrs.SRC]])
                else:
                    try:
                        image = Image.open(BytesIO(requests.get(attrs[HTML.Attrs.SRC]).content))
                        self.cached_images[attrs[HTML.Attrs.SRC]] = deepcopy(image)
                    except:
                        pass

            if attrs[HTML.Attrs.SRC] in self.cached_images.keys():
                image = deepcopy(self.cached_images[attrs[HTML.Attrs.SRC]])
            elif os.path.exists(attrs[HTML.Attrs.SRC]):
                image = Image.open(attrs[HTML.Attrs.SRC])
                self.cached_images[attrs[HTML.Attrs.SRC]] = deepcopy(image)
            if image:
                width = image.size[0]
                height = image.size[1]
                resize = False
                if str(attrs[HTML.Attrs.WIDTH]).isdigit():
                    width = int(attrs[HTML.Attrs.WIDTH])
                    resize = True
                if str(attrs[HTML.Attrs.HEIGHT]).isdigit():
                    height = int(attrs[HTML.Attrs.HEIGHT])
                    resize = True
                if resize:
                    image = image.resize((width, height), Image.ANTIALIAS)
                self.images.append(ImageTk.PhotoImage(image))
                self._w.image_create(tk.INSERT, image=self.images[-1])

        if self.strip:
            # ------------------------------------------------------------------------ [ NEW_LINES ]
            if tag == HTML.Tag.BR:
                self._insert_new_line()
            else:
                self.html_tags.append(tag)

            if tag in HTML.NEW_LINE_TAGS and self.strip and self._w.index("end-1c") != "1.0":
                if tag in (HTML.Tag.DIV,):
                    self._insert_new_line()
                elif tag in (HTML.Tag.UL, HTML.Tag.OL):
                    if len(self.list_tags) == 1:
                        self._insert_new_line(double=True)
                    else:
                        self._insert_new_line(double=False)
                else:
                    self._insert_new_line(double=True)

        self._w_tags_add()

    def handle_charref(self, data):
        # ------------------------------------------------------------------------------------------
        try:
            char = chr(int(data))
            self._w.insert(tk.INSERT, char)
        except:
            pass

    def _insert_new_line(self, double=False):
        # ------------------------------------------------------------------------------------------
        self._remove_last_space()
        if self._w.get("end-3c", "end-1c") == "\n\n":
            pass
        elif self._w.get("end-2c", "end-1c") == "\n":
            if double:
                self._w.insert(tk.INSERT, "\n")
        else:
            if double:
                self._w.insert(tk.INSERT, "\n\n")
            else:
                self._w.insert(tk.INSERT, "\n")

    def _text_rstrip(self):
        # ------------------------------------------------------------------------------------------
        for _ in range(3):
            if self._w.get("end-2c", "end-1c") in (" ", "\n"):
                self._w.delete("end-2c", "end-1c")

    def _remove_last_space(self):
        # ------------------------------------------------------------------------------------------
        if self._w.get("end-2c", "end-1c") == " ":
            self._w.delete("end-2c", "end-1c")

    def _remove_multi_spaces(self, data):
        # ------------------------------------------------------------------------------------------
        data = data.replace("  ", " ")
        if "  " in data:
            data = self._remove_multi_spaces(data)
        return data

    def handle_data(self, data):
        # ------------------------------------------------------------------------------------------
        if self.strip:
            if len(self.html_tags) and self.html_tags[-1] in (HTML.Tag.PRE, HTML.Tag.CODE):
                pass
            elif not data.strip():
                data = ""
            else:
                # left strip
                if self._w.index("end-1c").endswith(".0"):
                    data = data.lstrip()
                elif self._w.get("end-2c", "end-1c") == " ":
                    data = data.lstrip()

                data = data.replace("\n", " ").replace("\t", " ")
                data = data + " "
                data = self._remove_multi_spaces(data)
                if len(self.html_tags):
                    level = len(self.list_tags)
                    if self.html_tags[-1] in (HTML.Tag.UL, HTML.Tag.OL):
                        self._w.insert(tk.INSERT, "\t" * 2 * level)

        self._w.insert(tk.INSERT, data)

    def handle_endtag(self, tag):
        # ------------------------------------------------------------------------------------------
        tag = tag.lower()

        try:
            index = len(self.html_tags) - self.html_tags[::-1].index(tag) - 1
            self.html_tags.pop(index)
        except:
            pass

        if tag in HTML.STYLE_TAGS:

            self._stack_pop(tag, WCfg.FOREGROUND)
            self._stack_pop(tag, WCfg.BACKGROUND)
            self._stack_pop(tag, WCfg.JUSTIFY)
            self._stack_pop(tag, Fnt.FAMILY)
            self._stack_pop(tag, Fnt.SIZE)
            self._stack_pop(tag, Fnt.UNDERLINE)
            self._stack_pop(tag, Fnt.OVERSTRIKE)

            if tag == HTML.Tag.B or tag == HTML.Tag.STRONG or tag in HTML.HEADING_TAGS:
                self._stack_pop(tag, Fnt.WEIGHT)

            elif tag == HTML.Tag.I or tag == HTML.Tag.EM:
                self._stack_pop(tag, Fnt.SLANT)

            elif tag == HTML.Tag.A:
                self._stack_pop(tag, Bind.LINK)

            elif tag == HTML.Tag.OL or tag == HTML.Tag.UL:
                if len(self.list_tags):
                    self.list_tags = self.list_tags[:-1]

                self._stack_pop(tag, WCfg.TABS)

        if tag in HTML.NEW_LINE_TAGS and self.strip:
            self._insert_new_line()

        self._w_tags_add()

        if tag in HTML.NEW_LINE_TAGS and self.strip:
            if tag in (HTML.Tag.DIV, HTML.Tag.UL, HTML.Tag.OL):
                if not len(self.list_tags):
                    self._insert_new_line(double=True)
            else:
                self._insert_new_line(double=True)

    def _w_tags_apply_all(self):
        # ------------------------------------------------------------------------------------------
        # update indexes
        if self.strip:
            self._text_rstrip()
        end_index = tk.END
        for key, tag in reversed(tuple(self._w_tags.items())):
            tag[WTag.START_INDEX] = key
            tag[WTag.END_INDEX] = end_index
            end_index = key

        # add tags
        self.hlink_slots = []
        for key, tag in self._w_tags.items():
            self._w.tag_add(key, tag[WTag.START_INDEX], tag[WTag.END_INDEX])
            self._w.tag_config(key, font=font.Font(**tag[Fnt.KEY]), **tag[WCfg.KEY])
            if tag[Bind.KEY][Bind.LINK]:
                self.hlink_slots.append(HLinkSlot(self._w, key, tag[Bind.KEY][Bind.LINK]))
                self._w.tag_bind(key, "<Button-1>", self.hlink_slots[-1].call)
                self._w.tag_bind(key, "<Leave>", self.hlink_slots[-1].leave)
                self._w.tag_bind(key, "<Enter>", self.hlink_slots[-1].enter)

    def w_set_html(self, w, html, strip):
        # ------------------------------------------------------------------------------------------
        self._w = w
        self.stack = deepcopy(DEFAULT_STACK)
        self.stack[WCfg.KEY][WCfg.BACKGROUND].append(("__DEFAULT__", self._w.cget("background")))
        self.stack[Fnt.KEY][Fnt.FAMILY].append(("__DEFAULT__", self.DEFAULT_TEXT_FONT_FAMILY))
        self._w_tags = OrderedDict()
        self.html_tags = []
        self.images = []
        self.list_tags = []
        self.strip = strip
        self._w_tags_add()
        self.feed(html)
        self._w_tags_apply_all()
        del self._w
