"""
Attack Logs to Visdom
========================
"""

import socket

from textattack.shared.utils import LazyLoader, html_table_from_rows

from .logger import Logger

visdom = LazyLoader("visdom", globals(), "visdom")


def port_is_open(port_num, hostname="127.0.0.1"):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    result = sock.connect_ex((hostname, port_num))
    sock.close()
    if result == 0:
        return True
    return False


class VisdomLogger(Logger):
    """Logs attack results to Visdom."""

    def __init__(self, env="main", port=8097, hostname="localhost"):
        if not port_is_open(port, hostname=hostname):
            raise socket.error(f"Visdom not running on {hostname}:{port}")
        self.vis = visdom.Visdom(port=port, server=hostname, env=env)
        self.env = env
        self.port = port
        self.hostname = hostname
        self.windows = {}
        self.sample_rows = []

    def __getstate__(self):
        state = {i: self.__dict__[i] for i in self.__dict__ if i != "vis"}
        return state

    def __setstate__(self, state):
        self.__dict__ = state
        self.vis = visdom.Visdom(port=self.port, server=self.hostname, env=self.env)

    def log_attack_result(self, result):
        text_a, text_b = result.diff_color(color_method="html")
        result_str = result.goal_function_result_str(color_method="html")
        self.sample_rows.append([result_str, text_a, text_b])

    def log_summary_rows(self, rows, title, window_id):
        self.table(rows, title=title, window_id=window_id)

    def flush(self):
        self.table(
            self.sample_rows,
            title="Sample-Level Results",
            window_id="sample_level_results",
        )

    def log_hist(self, arr, numbins, title, window_id):
        self.bar(arr, numbins=numbins, title=title, window_id=window_id)

    def text(self, text_data, title=None, window_id="default"):
        if window_id and window_id in self.windows:
            window = self.windows[window_id]
            self.vis.text(text_data, win=window)
        else:
            new_window = self.vis.text(text_data, opts=dict(title=title))
            self.windows[window_id] = new_window

    def table(self, rows, window_id=None, title=None, header=None, style=None):
        """Generates an HTML table."""

        if not window_id:
            window_id = title  # Can provide either of these,
        if not title:
            title = window_id  # or both.
        table = html_table_from_rows(rows, title=title, header=header, style_dict=style)
        self.text(table, title=title, window_id=window_id)

    def bar(self, X_data, numbins=10, title=None, window_id=None):
        window = None
        if window_id and window_id in self.windows:
            window = self.windows[window_id]
            self.vis.bar(X=X_data, win=window, opts=dict(title=title, numbins=numbins))
        else:
            new_window = self.vis.bar(X=X_data, opts=dict(title=title, numbins=numbins))
            if window_id:
                self.windows[window_id] = new_window

    def hist(self, X_data, numbins=10, title=None, window_id=None):
        window = None
        if window_id and window_id in self.windows:
            window = self.windows[window_id]
            self.vis.histogram(
                X=X_data, win=window, opts=dict(title=title, numbins=numbins)
            )
        else:
            new_window = self.vis.histogram(
                X=X_data, opts=dict(title=title, numbins=numbins)
            )
            if window_id:
                self.windows[window_id] = new_window
