import json
from .task import MBIOTask
from .xmlconfig import XMLConfig

from .wserver import TemporaryWebServer


class Html():
    def __init__(self, title=None):
        self._header=''
        self._body=''
        if title:
            self.header(f'<title>{title}</title>')

        self.header('<meta charset="utf-8" />')
        self.header('<meta name="viewport" content="width=device-width, initial-scale=1" />')

        # self.header('<link rel="stylesheet" href="/www/jquery-ui/jquery-ui.min.css">')
        # self.header('<script src="/www/jquery-ui/external/jquery/jquery.js"></script>')
        # self.header('<script src="/www/jquery-ui/jquery-ui.min.js"></script>')

        # https://jquery.com/download/
        self.body('<script src="/www/jquery/jquery.min.js"></script>')

        # https://getbootstrap.com/docs/5.0/getting-started/download/
        self.header('<link href="/www/Bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">')
        self.body('<script src="/www/Bootstrap/dist/js/bootstrap.bundle.min.js"></script>')

        # https://datatables.net/download/
        self.header('<link href="/www/DataTables/datatables.min.css" rel="stylesheet">')
        self.body('<script src="/www/DataTables/datatables.min.js"></script>')

        self.style('.red-button {background-color: red; color: white;}')

        data="""
        .form-switch {
        position: relative;
        display: inline-block;
        width: 42px;
        height: 22px;
        }

        .form-switch input {
        opacity: 0;
        width: 0;
        height: 0;
        }

        .form-switch .slider {
        position: absolute;
        cursor: pointer;
        top: 0; left: 0; right: 0; bottom: 0;
        background-color: #ccc;
        border-radius: 22px;
        transition: .3s;
        }

        .form-switch .slider:before {
        position: absolute;
        content: "";
        height: 18px; width: 18px;
        left: 2px; bottom: 2px;
        background-color: white;
        border-radius: 50%;
        transition: .3s;
        }

        .form-switch input:checked + .slider {
        background-color: #4caf50;
        }

        .form-switch input:checked + .slider:before {
        transform: translateX(20px);
        }
        """
        self.style(data)

    def header(self, data):
        if data:
            self._header+=data
            self._header+='\n'

    def style(self, data):
        if data:
            self.header(f'<style>{data}</style>')

    def body(self, data):
        if data:
            self._body+=data
            self._body+='\n'

    def write(self, data):
        self.body(data)

    def data(self):
        data='<html>'
        data+='<head>'
        data+=self._header
        data+='</head>'
        data+='<body>'
        data+=self._body
        data+='</body>'
        data+='</html>'
        return data

    def bytes(self):
        return self.data().encode('utf-8')

    def button(self, bid, name):
        self.write(f'<button id="{bid}" class="ui-button red-button ui-widget ui-corner-all">{name}</button>')


class MBIOWsMonitor(MBIOTask):
    def initName(self):
        return 'wsmon'

    @property
    def wserver(self) -> TemporaryWebServer:
        return self._wserver

    def onInit(self):
        self._wserver=None

    def cb_mycallback(self, handler, params):
        handler.send_response(200)
        handler.send_header("Content-Type", "text/html; charset=utf-8")
        handler.end_headers()

        h=Html('MBIO Processor Monitor')
        # h.button('test', 'Let\'s GO!')

        mbio=self.getMBIO()
        for g in mbio.gateways.all():
            h.button(g.name, str(g))

        handler.wfile.write(h.bytes())

    def cb_headers_json(self, handler, rcode=200):
        handler.send_response(rcode)
        handler.send_header("Content-Type", "application/json; charset=utf-8")
        handler.end_headers()

    def cb_write_json(self, handler, data, rcode=200):
        self.cb_headers_json(handler, rcode)
        data=json.dumps(data)
        handler.wfile.write(data.encode('utf-8'))

    def cb_write_success(self, handler):
        data={'success': True}
        return self.cb_write_json(handler, data)

    def cb_write_failure(self, handler):
        data={'success': False}
        return self.cb_write_json(handler, data, 400)

    def cb_gettasks(self, handler, params):
        mbio=self.getMBIO()
        items=[]
        for t in mbio.tasks.all():
            item={'key': t.key}
            item['class']=t.__class__.__name__
            item['state']=t.statestr(),
            item['statetime']=int(t.statetime())
            item['error']=t.isError()
            items.append(item)

        data={'data': items}
        self.cb_write_json(handler, data)

    def cb_setvalue(self, handler, params):
        mbio=self.getMBIO()
        key=params.get('key')
        v=params.get('value')
        value=mbio.value(key)
        if value is not None:
            value.manual(v)
            self.cb_write_success(handler)
        self.cb_write_failure(handler)

    def cb_setvalueauto(self, handler, params):
        mbio=self.getMBIO()
        key=params.get('key')
        value=mbio.value(key)
        if value is not None:
            value.auto()
            self.cb_write_success(handler)
        self.cb_write_failure(handler)

    def cb_getgateways(self, handler, params):
        mbio=self.getMBIO()
        items=[]
        for g in mbio.gateways.all():
            item={'key': g.key, 'name': g.name, 'host': g.host, 'mac': g.MAC, 'model': g.model}
            item['class']=g.__class__.__name__
            state='CLOSED'
            if g.isOpen():
                state='OPEN'
            item['state']=state
            item['error']=g.isError()
            items.append(item)

        data={'data': items}
        self.cb_write_json(handler, data)

    def cb_getgatewaydevices(self, handler, params):
        mbio=self.getMBIO()
        g=mbio.gateway(params.get('gateway'))

        items=[]
        if g is not None:
            for d in g.devices.all():
                self.microsleep()
                item={'gateway': g.key, 'address': d.address, 'key': d.key, 'vendor': d.vendor, 'model': d.model, 'state': d.statestr()}
                item['class']=d.__class__.__name__
                item['version']=d.version
                item['statetime']=int(d.statetime())
                item['countmsg']=d.countMsg
                item['countmsgerr']=d.countMsgErr
                item['error']=d.isError()
                item['countvalues']=d.values.count()
                items.append(item)

        data={'data': items}
        self.cb_write_json(handler, data)

    def cb_getdevicevalues(self, handler, params):
        mbio=self.getMBIO()
        g=mbio.gateway(params.get('gateway'))

        items=[]
        if g is not None:
            d=g.device(params.get('device'))
            if d is not None:
                for v in d.values.all():
                    self.microsleep()
                    if not v.isEnabled():
                        continue
                    item={'key': v.key, 'value': v.value,
                          'valuestr': v.valuestr(),
                          'unit': v.unit, 'unitstr': v.unitstr(),
                          'flags': v.flags, 'age': int(v.age()), 'tag': v.tag}
                    item['class']=v.__class__.__name__
                    item['error']=v.isError()
                    item['writable']=v.isWritable()
                    item['digital']=v.isDigital()
                    item['enable']=v.isEnabled()
                    item['manual']=v.isManual()
                    items.append(item)

        data={'data': items}
        self.cb_write_json(handler, data)

    def cb_getvalues(self, handler, params):
        mbio=self.getMBIO()

        items=[]
        for v in mbio.values(params.get('filter')):
            self.microsleep()
            if not v.isEnabled():
                continue
            item={'key': v.key, 'value': v.value,
                    'valuestr': v.valuestr(),
                    'unit': v.unit, 'unitstr': v.unitstr(),
                    'flags': v.flags, 'age': int(v.age()), 'tag': v.tag}
            item['class']=v.__class__.__name__
            item['error']=v.isError()
            item['writable']=v.isWritable()
            item['digital']=v.isDigital()
            item['enable']=v.isEnabled()
            item['manual']=v.isManual()
            items.append(item)

        data={'data': items}
        self.cb_write_json(handler, data)

    def cb_gateways(self, handler, params):
        handler.send_response(200)
        handler.send_header("Content-Type", "text/html; charset=utf-8")
        handler.end_headers()

        mbio=self.getMBIO()
        h=Html('Digimat MBIO Processor Monitor')

        data="""
            <div class="page container">
                <div class="card p-4 p-lg-5">
                    <div class="d-flex align-items-center justify-content-between flex-wrap gap-3 mb-3">
                    <div>
                        <h1 class="h3 mb-1">Digimat MBIO Processor Monitor</h1>
                        <p class="text-secondary mb-0">Gateways</p>
                    </div>
                    <div class="text-muted small">{mbio.version}</div>
                    </div>
                    <table id="items" class="display nowrap" style="width:100%">
                    <thead>
                        <tr>
                            <th>Key</th>
                            <th>Host</th>
                            <th>MAC</th>
                            <th>State</th>
                            <th>Error</th>
                            <th>Class</th>
                        </tr>
                    </thead>
                    </table>
                </div>
            </div>
        """
        data=data.replace('{mbio.version}', mbio.version)
        h.write(data)

        data="""
        <script>
            $(function () {
                const table = new DataTable('#items', {
                responsive: true,
                paging: false,
                searching: true,
                ordering: true,
                info: false,
                ajax: {
                    url: "/api/v1/getgateways",
                    dataSrc: "data"
                },
                columns: [
                    { data: "key" },
                    { data: "host" },
                    { data: "mac" },
                    { data: "state" },
                    { data: "error" },
                    { data: "class" }
                ],
                rowCallback: function (row, data) {
                    if (data.error) {
                        $('td', row).css('background-color', 'pink');
                    }
                }
                });

                $('#items').on('click','tr', function() {
                    var data = table.row(this).data();
                    const url = "/devices?gateway=" + encodeURIComponent(data.key);
                    window.location.href = url;
                });
            });
        </script>
        """
        h.write(data)
        handler.wfile.write(h.bytes())

    def cb_devices(self, handler, params):
        handler.send_response(200)
        handler.send_header("Content-Type", "text/html; charset=utf-8")
        handler.end_headers()

        mbio=self.getMBIO()
        h=Html('Digimat MBIO Processor Monitor')
        gateway=params.get('gateway')

        data="""
            <div class="page container">
            <div class="card p-4 p-lg-5">
            <div class="d-flex align-items-center justify-content-between flex-wrap gap-3 mb-3">
            <div>
            <h1 class="h3 mb-1">Digimat MBIO Processor Monitor</h1>
            <p class="text-secondary mb-0">Devices of gateway <b>{gateway}</b></p>
            </div>
            <div class="text-muted small">{mbio.version}</div>
            </div>
            <table id="items" class="display nowrap" style="width:100%">
            <thead>
            <tr>
            <th>Address</th>
            <th>Key</th>
            <th>Vendor</th>
            <th>Model</th>
            <th>State</th>
            <th>Error</th>
            <th>Class</th>
            </tr>
            </thead>
            </table>
            </div>
            </div>
        """
        data=data.replace('{gateway}', gateway)
        data=data.replace('{mbio.version}', mbio.version)
        h.write(data)

        data="""
        <script>
            $(function () {
            const table = new DataTable('#items', {
            responsive: true,
            paging: false,
            searching: true,
            ordering: true,
            info: false,
            ajax: {
                url: "/api/v1/getgatewaydevices?gateway={gateway}",
                dataSrc: "data"
            },
            columns: [
                { data: "address" },
                { data: "key" },
                { data: "vendor" },
                { data: "model" },
                { data: "state" },
                { data: "error" },
                { data: "class" }
            ],
            rowCallback: function (row, data) {
                if (data.error) {
                    $('td', row).css('background-color', 'pink');
                }
            }
            });

            setInterval(function () {
                table.ajax.reload(null, false);
                }, 2000);

            $('#items').on('click','tr', function() {
                var data = table.row(this).data();
                const url = "/devicevalues?gateway=" + encodeURIComponent('{gateway}')
                    + "&device=" + encodeURIComponent(data.key);
                window.location.href = url;
            });

            });
        </script>
        """

        data=data.replace('{gateway}', gateway)
        h.write(data)
        handler.wfile.write(h.bytes())

    def cb_devicevalues(self, handler, params):
        handler.send_response(200)
        handler.send_header("Content-Type", "text/html; charset=utf-8")
        handler.end_headers()

        mbio=self.getMBIO()
        h=Html('Digimat MBIO Processor Monitor')
        gateway=params.get('gateway')
        device=params.get('device')

        url=f'/devices?gateway={gateway}'

        data="""
            <div class="page container">
                <div class="card p-4 p-lg-5">
                    <div class="d-flex align-items-center justify-content-between flex-wrap gap-3 mb-3">
                    <div>
                        <h1 class="h3 mb-1">Digimat MBIO Processor Monitor</h1>
                        <p class="text-secondary mb-0">Values of device <a href="{url}">{gateway}</a>:<b>{device}</b></p>
                    </div>
                    <div class="text-muted small">{mbio.version}</div>
                    </div>
                    <table id="items" class="display nowrap" style="width:100%">
                    <thead>
                        <tr>
                            <th>Key</th>
                            <th>Tag</th>
                            <th>Value</th>
                            <th>Flags</th>
                            <th>Manual</th>
                            <th>Age</th>
                            <th>Error</th>
                            <th>Class</th>
                        </tr>
                    </thead>
                    </table>
                </div>
            </div>
        """
        data=data.replace('{mbio.version}', mbio.version)
        data=data.replace('{gateway}', gateway)
        data=data.replace('{device}', device)
        data=data.replace('{url}', url)
        h.write(data)

        data="""
        <script>
            $(function () {
                const table = new DataTable('#items', {
                responsive: true,
                paging: false,
                searching: true,
                ordering: true,
                info: false,
                ajax: {
                    url: "/api/v1/getdevicevalues?gateway={gateway}&device={device}",
                    dataSrc: "data"
                },
                columns: [
                    { data: "key" },
                    { data: "tag" },
                    { data: "valuestr" },
                    { data: "flags" },
                    { data: "manual" },
                    { data: "age", render: v=>v<3600 ? v : '' },
                    { data: "error" },
                    { data: "class" }
                ],
                rowCallback: function (row, data) {
                        if (!data.enable) {
                            $('td', row).css('background-color', 'lightgray');
                        } else if (data.error) {
                            $('td', row).css('background-color', 'pink');
                        } else if (data.manual) {
                            $('td', row).css('background-color', 'lightgreen');
                        }
                }
                });

                setInterval(function () {
                  table.ajax.reload(null, false);
                  }, 2000);

                /*
                $('#items').on('click','tr', function() {
                    var data = table.row(this).data();
                    const url = "/devicevalues?gateway=" + encodeURIComponent('{gateway}')
                    + "&device=" + encodeURIComponent(data.key);
                    window.location.href = url;
                });
                */
            });
        </script>
        """
        data=data.replace('{gateway}', gateway)
        data=data.replace('{device}', device)
        h.write(data)

        handler.wfile.write(h.bytes())

    def cb_values(self, handler, params):
        handler.send_response(200)
        handler.send_header("Content-Type", "text/html; charset=utf-8")
        handler.end_headers()

        mbio=self.getMBIO()
        h=Html('Digimat MBIO Processor Monitor')


        data="""
            <div class="page container">
                <div class="card p-4 p-lg-5">
                    <div class="d-flex align-items-center justify-content-between flex-wrap gap-3 mb-3">
                    <div>
                        <h1 class="h3 mb-1">Digimat MBIO Processor Monitor</h1>
                        <p class="text-secondary mb-0"><b>Values</b></p>
                    </div>
                    <div class="text-muted small">{mbio.version}</div>
                    </div>
                    <table id="items" class="display nowrap" style="width:100%">
                    <thead>
                        <tr>
                            <th>Key</th>
                            <th>Manual</th>
                            <th>Value</th>
                            <th>Flags</th>
                            <th>Age</th>
                            <th>Error</th>
                            <th>Class</th>
                            <th>Tag</th>
                        </tr>
                    </thead>
                    </table>
                </div>
            </div>
        """
        data=data.replace('{mbio.version}', mbio.version)
        h.write(data)

        """
        """

        data="""
        <script>
            $(function () {
                    const table = new DataTable('#items', {
                    responsive: true,
                    paging: false,
                    searching: true,
                    ordering: true,
                    info: false,
                    ajax: {
                        url: "/api/v1/getvalues",
                        dataSrc: "data"
                    },

                    columns: [
                        { data: "key" },
                        { data: "manual", className: "editable",
                            render: function(value, type, row) {
                                if (type === "display") {
                                    if (row.writable && !row.error)
                                    {
                                        const checked = row.manual ? 'checked' : '';
                                        return `<label class="form-switch">
                                            <input type="checkbox" class="toggle-enabled" data-id="${row.id}" ${checked}>
                                            <span class="slider"></span>
                                            </label>`;
                                    }

                                    return row.manual ? "MAN" : "AUTO";
                                 }

                                return value;
                            }
                        },
                        { data: "valuestr", className: "editable",
                            render: function(value, type, row) {
                                if (type === "display") {
                                    if (row.manual) {
                                        if (row.digital)
                                        {
                                            const checked = row.value ? 'checked' : '';
                                            return `<label class="form-switch">
                                                <input type="checkbox" class="toggle-enabled" data-id="${row.id}" ${checked}>
                                                <span class="slider"></span>
                                                </label>`;
                                        }
                                    }
                                 }

                                return value;
                            }
                        },
                        { data: "flags" },
                        { data: "age", render: v=>v<3600 ? v : '' },
                        { data: "error" },
                        { data: "class" },
                        { data: "tag" }
                    ],

                    rowCallback: function (row, data) {
                        if (!data.enable) {
                            $('td', row).css('background-color', 'lightgray');
                        } else if (data.error) {
                            $('td', row).css('background-color', 'pink');
                        } else if (data.manual) {
                            $('td', row).css('background-color', 'lightgreen');
                        }
                    }
                });

                let isUserEditing = false;
                let pollingId = setInterval(function() {
                  table.ajax.reload(null, false);
                  }, 2000);

                $("#items").on("change", ".toggle-enabled", async function (e) {
                    clearInterval(pollingId);
                    e.stopPropagation();
                    const $checkbox = $(this);
                    const enabled = $checkbox.is(':checked');
                    const row = table.row($checkbox.closest('tr'));
                    const data = row.data();
                    console.log(data);

                    try {
                        var res;
                        if (enabled) {
                            res = await fetch(`/api/v1/setvalue?key=${data.key}&value=${data.value}`, {method: "GET"});
                        } else {
                            res = await fetch(`/api/v1/setvalueauto?key=${data.key}`, {method: "GET"});
                        }

                        pollingId = setInterval(function() {
                            table.ajax.reload(null, false);
                            }, 2000);

                        if (!res.ok) throw new Error();
                    } catch (_) {
                        $(this).prop("checked", !enabled);
                    }
                });

            });
        </script>
        """
        h.write(data)

        handler.wfile.write(h.bytes())

    def onLoad(self, xml: XMLConfig):
        mbio=self.getMBIO()
        port=xml.getInt('port', 8001)
        # interface=mbio.interface
        interface='0.0.0.0'
        ws=TemporaryWebServer('/tmp/wsmonitor', port=port, host=interface, logger=self.logger)
        if ws:
            ws.registerGetCallback('/api/v1/gettasks', self.cb_gettasks)
            ws.registerGetCallback('/api/v1/getgateways', self.cb_getgateways)
            ws.registerGetCallback('/api/v1/getgatewaydevices', self.cb_getgatewaydevices)
            ws.registerGetCallback('/api/v1/getdevicevalues', self.cb_getdevicevalues)
            ws.registerGetCallback('/api/v1/getvalues', self.cb_getvalues)
            ws.registerGetCallback('/api/v1/setvalue', self.cb_setvalue)
            ws.registerGetCallback('/api/v1/setvalueauto', self.cb_setvalueauto)
            ws.registerGetCallback('/test', self.cb_mycallback)
            ws.registerGetCallback('/gateways', self.cb_gateways)
            ws.registerGetCallback('/devices', self.cb_devices)
            ws.registerGetCallback('/devicevalues', self.cb_devicevalues)
            ws.registerGetCallback('/values', self.cb_values)
            self._wserver=ws

    def poweron(self):
        ws=self.wserver
        if ws:
            ws.disableAutoShutdown()
            ws.linkPath('~/Dropbox/python/www')
            ws.linkPath('/usr/lib/www')
            ws.start()
        return True

    def poweroff(self):
        ws=self.wserver
        if ws:
            ws.stop()
        return True

    def run(self):
        return 1.0


if __name__ == "__main__":
    pass
