"""
This module provides a basic operation panel that allows to access
fundamental properties of operations. This is supposed to provide
a simpler interface to operations
"""

import wx

from meerk40t.core.elements.element_types import elem_nodes, op_nodes
from meerk40t.gui.laserrender import swizzlecolor

from ..kernel import Job, lookup_listener, signal_listener
from ..svgelements import Color
from .icons import (
    icons8_direction_20,
    icons8_image_20,
    icons8_laser_beam_20,
    icons8_scatter_plot_20,
    icons8_small_beam_20,
)
from .wxutils import ScrolledPanel, StaticBoxSizer, TextCtrl, create_menu, dip_size

_ = wx.GetTranslation

BUTTONSIZE = 20

class BasicOpPanel(wx.Panel):
    """
    Basic interface to show operations and assign elements to them.
    Very much like the layer concept in other laser software products
    """

    def __init__(self, *args, context=None, **kwds):
        kwds["style"] = kwds.get("style", 0) | wx.TAB_TRAVERSAL
        wx.Panel.__init__(self, *args, **kwds)
        self.context = context
        # Refresh logic
        self.ignore_refill = False
        self.filtered = True
        self._refill_job = Job(
            process=self.fill_operations,
            job_name="basicops_fillop",
            interval=0.5,
            times=1,
            run_main=True,
        )
        choices = [
            _("Leave color"),
            _("Op inherits color"),
            _("Elem inherits color"),
        ]
        self.combo_apply_color = wx.ComboBox(
            self,
            wx.ID_ANY,
            choices=choices,
            value=choices[0],
            style=wx.CB_READONLY | wx.CB_DROPDOWN,
        )
        self.check_exclusive = wx.CheckBox(self, wx.ID_ANY, _("Exclusive"))
        self.check_all_similar = wx.CheckBox(self, wx.ID_ANY, _("Similar"))
        self.combo_apply_color.SetToolTip(
            _(
                "Leave - neither the color of the operation nor of the elements will be changed"
            )
            + "\n"
            + _("-> OP - the assigned operation will adopt the color of the element")
            + "\n"
            + _("-> Elem - the elements will adopt the color of the assigned operation")
        )
        self.check_all_similar.SetToolTip(
            _("Assign as well all other elements with the same stroke-color")
        )
        self.check_exclusive.SetToolTip(
            _(
                "When assigning to an operation remove all assignments of the elements to other operations"
            )
        )
        self.context.elements.setting(bool, "classify_inherit_exclusive", True)
        self.context.elements.setting(bool, "classify_all_similar", True)
        self.context.elements.setting(int, "classify_impose_default", 0)
        self.check_exclusive.SetValue(self.context.elements.classify_inherit_exclusive)
        self.check_all_similar.SetValue(self.context.elements.classify_all_similar)
        value = self.context.elements.classify_impose_default
        self.combo_apply_color.SetSelection(value)
        self.check_exclusive.Bind(wx.EVT_CHECKBOX, self.on_check_exclusive)
        self.check_all_similar.Bind(wx.EVT_CHECKBOX, self.on_check_allsimilar)
        self.combo_apply_color.Bind(wx.EVT_COMBOBOX, self.on_combo_color)

        self.btn_config = wx.Button(self, wx.ID_ANY, "...")
        self.btn_config.SetMinSize(dip_size(self, 25, -1))
        self.btn_config.SetMaxSize(dip_size(self, 25, -1))
        self.btn_config.Bind(wx.EVT_BUTTON, self.on_config)
        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.op_panel = ScrolledPanel(self, wx.ID_ANY)
        self.op_panel.SetupScrolling()
        self.operation_sizer = None
        classif_sizer = StaticBoxSizer(
            self, wx.ID_ANY, _("Classification"), wx.VERTICAL
        )
        upper_sizer = wx.BoxSizer(wx.HORIZONTAL)
        upper_sizer.Add(self.combo_apply_color, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        upper_sizer.Add(self.btn_config, 0, wx.ALIGN_CENTER_VERTICAL, 0)

        classif_sizer.Add(upper_sizer, 0, wx.EXPAND, 0)

        lower_sizer = wx.BoxSizer(wx.HORIZONTAL)
        lower_sizer.Add(self.check_exclusive, 1, wx.EXPAND, 0)
        lower_sizer.Add(self.check_all_similar, 1, wx.EXPAND, 0)
        classif_sizer.Add(lower_sizer, 0, wx.EXPAND, 0)

        self.main_sizer.Add(self.op_panel, 1, wx.EXPAND, 0)
        self.main_sizer.Add(classif_sizer, 0, wx.EXPAND, 0)
        self.SetSizer(self.main_sizer)
        self.Layout()
        self.use_percent = False
        self.use_mm_min = False
        self.set_display()
        self.op_ctrl_list = []
        self.std_color_back = None
        self.std_color_fore = None

    def set_display(self):
        self.context.device.setting(bool, "use_percent_for_power_display", False)
        self.use_percent = self.context.device.use_percent_for_power_display
        self.context.device.setting(bool, "use_mm_min_for_speed_display", False)
        self.use_mm_min = self.context.device.use_mm_min_for_speed_display

    def on_combo_color(self, event):
        value = self.combo_apply_color.GetCurrentSelection()
        self.context.elements.classify_impose_default = value

    def on_check_exclusive(self, event):
        newval = self.check_exclusive.GetValue()
        self.context.elements.classify_inherit_exclusive = newval

    def on_check_allsimilar(self, event):
        newval = self.check_all_similar.GetValue()
        self.context.elements.classify_all_similar = newval

    def execute_single(self, targetop, attrib):
        data = list(self.context.elements.flat(emphasized=True))
        idx = self.context.elements.classify_impose_default
        if idx == 1:
            impose = "to_op"
        elif idx == 2:
            impose = "to_elem"
        else:
            impose = None
        similar = self.context.elements.classify_all_similar
        exclusive = self.context.elements.classify_inherit_exclusive
        if len(data) == 0:
            return
        self.context.elements.assign_operation(
            op_assign=targetop,
            data=data,
            impose=impose,
            attrib=attrib,
            similar=similar,
            exclusive=exclusive,
        )

    def on_config(self, event):
        mynode = self.context.elements.op_branch
        mynode.selected = True
        create_menu(self, mynode, self.context.elements)

    def fill_operations(self):
        def on_button_left(node):
            def handler(event):
                # print(f"Left for {mynode.type}")
                self.execute_single(mynode, "auto")

            mynode = node
            return handler

        def on_button_right(node):
            def handler(event):
                # print(f"Right for {mynode.type}")

                mynode.selected = True
                create_menu(self, mynode, self.context.elements)

            mynode = node
            return handler

        # def on_button_doubleclick(node):
        #     def handler(event):
        #         print(f"Double for {mynode.type}")
        #         activate = self.context.kernel.lookup(
        #             "function/open_property_window_for_node"
        #         )
        #         if activate is not None:
        #             mynode.selected = True
        #             activate(mynode)

        #     mynode = node
        #     return handler

        def on_check_show(node):
            def handler(event):
                # print(f"Show for {mynode.type}")
                cb = event.GetEventObject()
                newflag = True
                if hasattr(mynode, "output") and hasattr(mynode, "is_visible"):
                    if mynode.output is not None:
                        if not mynode.output:
                            newflag = bool(not mynode.is_visible)

                    mynode.is_visible = newflag
                    mynode.updated()
                    self.context.elements.validate_selected_area()
                    ops = [mynode]
                    self.ignore_refill = True
                    self.context.elements.signal("element_property_update", ops)
                    self.context.elements.signal("refresh_scene", "Scene")
                cb.SetValue(newflag)

            mynode = node
            return handler

        def on_check_output(node, showctrl):
            def handler(event):
                # print(f"Output for {mynode.type}")
                cb = event.GetEventObject()
                flag = False
                if hasattr(mynode, "output"):
                    flag = not mynode.output
                    try:
                        mynode.output = flag
                        mynode.updated()
                    except AttributeError:
                        pass
                    if flag:
                        myshow.SetValue(True)
                        myshow.Enable(False)
                    else:
                        myshow.Enable(True)
                    self.ignore_refill = True
                    ops = [mynode]
                    self.context.elements.signal("element_property_update", ops)
                    self.context.elements.signal("warn_state_update", "")
                    self.context.elements.signal("refresh_scene", "Scene")
                    cb.SetValue(flag)

            mynode = node
            myshow = showctrl
            return handler

        def on_speed(node, tbox):
            def handler():
                # print(f"Speed for {mynode.type}")
                try:
                    value = float(mytext.GetValue())
                    if self.use_mm_min:
                        value /= 60
                    if mynode.speed != value:
                        mynode.speed = value
                        self.ignore_refill = True
                        self.context.elements.signal(
                            "element_property_reload", [mynode], "text_speed"
                        )
                except ValueError:
                    pass

            mynode = node
            mytext = tbox
            return handler

        def on_power(node, tbox):
            def handler():
                # print(f"Power for {mynode.type}")
                try:
                    value = float(mytext.GetValue())
                    if self.use_percent:
                        value *= 10
                    if mynode.power != value:
                        mynode.power = value
                        self.ignore_refill = True
                        self.context.elements.signal(
                            "element_property_reload", [mynode], "text_power"
                        )
                except ValueError:
                    pass

            mynode = node
            mytext = tbox
            return handler

        def on_label_single(node):
            def handler(event):
                self.context.elements.set_emphasis(None)
                for elem in mynode.children:
                    elem.selected = True
                    if elem.node is not None:
                        elem.node.emphasized = True
                mynode.highlighted = True
                self.context.elements.signal("refresh_scene", "Scene")

            mynode = node
            return handler

        def on_label_double(node):
            def handler(event):
                activate = self.context.kernel.lookup(
                    "function/open_property_window_for_node"
                )
                if activate is not None:
                    mynode.selected = True
                    activate(mynode)

            mynode = node
            return handler

        def on_check_filtered(event):
            obj = event.GetEventObject()
            self.filtered = obj.GetValue()
            self.context.schedule(self._refill_job)

        def get_bitmap(node):
            def get_color():
                iconcolor = None
                background = node.color
                if background is not None and background.argb is not None:
                    c1 = Color("Black")
                    c2 = Color("White")
                    if Color.distance(background, c1) > Color.distance(background, c2):
                        iconcolor = c1
                    else:
                        iconcolor = c2
                return iconcolor, background

            iconsize = BUTTONSIZE
            result = None
            d = None
            if node.type == "op raster":
                c, d = get_color()
                result = icons8_direction_20.GetBitmap(
                    color=c,
                    resize=(iconsize, iconsize),
                    noadjustment=True,
                    keepalpha=True,
                )
            elif node.type == "op image":
                c, d = get_color()
                result = icons8_image_20.GetBitmap(
                    color=c,
                    resize=(iconsize, iconsize),
                    noadjustment=True,
                    keepalpha=True,
                )
            elif node.type == "op engrave":
                c, d = get_color()
                result = icons8_small_beam_20.GetBitmap(
                    color=c,
                    resize=(iconsize, iconsize),
                    noadjustment=True,
                    keepalpha=True,
                )
            elif node.type == "op cut":
                c, d = get_color()
                result = icons8_laser_beam_20.GetBitmap(
                    color=c,
                    resize=(iconsize, iconsize),
                    noadjustment=True,
                    keepalpha=True,
                )
            elif node.type == "op dots":
                c, d = get_color()
                result = icons8_scatter_plot_20.GetBitmap(
                    color=c,
                    resize=(iconsize, iconsize),
                    noadjustment=True,
                    keepalpha=True,
                )
            return d, result

        if self.operation_sizer:
            self.operation_sizer.Clear()
            self.op_panel.DestroyChildren()
        self.op_panel.Freeze()
        self.operation_sizer = StaticBoxSizer(
            self.op_panel, wx.ID_ANY, _("Operations"), wx.VERTICAL
        )
        self.op_panel.SetSizer(self.operation_sizer)
        elements = self.context.elements
        self.op_ctrl_list.clear()

        info_sizer = wx.BoxSizer(wx.HORIZONTAL)
        check_filtered = wx.CheckBox(self.op_panel, wx.ID_ANY)
        check_filtered.SetToolTip(_("Suppress non-used operations"))
        check_filtered.SetValue(self.filtered)
        check_filtered.SetMinSize(dip_size(self, 50, -1))
        check_filtered.SetMaxSize(dip_size(self, 90, -1))
        info_sizer.Add(check_filtered, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        check_filtered.Bind(wx.EVT_CHECKBOX, on_check_filtered)

        header = wx.StaticText(self.op_panel, wx.ID_ANY, label=_("Active"))
        header.SetMinSize(dip_size(self, 30, -1))
        header.SetMaxSize(dip_size(self, 50, -1))
        info_sizer.Add(header, 1, wx.ALIGN_CENTER_VERTICAL, 0)

        header = wx.StaticText(self.op_panel, wx.ID_ANY, label=_("Show"))
        header.SetMinSize(dip_size(self, 30, -1))
        header.SetMaxSize(dip_size(self, 50, -1))
        info_sizer.Add(header, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        if self.use_percent:
            unit = " [%]"
        else:
            unit = ""
        header = wx.StaticText(
            self.op_panel, wx.ID_ANY, label=_("Power {unit}").format(unit=unit)
        )
        header.SetMaxSize(dip_size(self, 30, -1))
        header.SetMaxSize(dip_size(self, 70, -1))
        info_sizer.Add(header, 1, wx.ALIGN_CENTER_VERTICAL, 0)
        header = wx.StaticText(self.op_panel, wx.ID_ANY, label=_("Speed"))
        header.SetMaxSize(dip_size(self, 30, -1))
        header.SetMaxSize(dip_size(self, 70, -1))

        info_sizer.Add(header, 1, wx.ALIGN_CENTER_VERTICAL, 0)

        self.operation_sizer.Add(info_sizer, 0, wx.EXPAND, 0)
        self.op_ctrl_list.clear()
        for op in list(elements.ops()):
            if op is None:
                continue
            if self.filtered and len(op.children) == 0:
                continue
            if op.type.startswith("op "):
                op_sizer = wx.BoxSizer(wx.HORIZONTAL)
                self.operation_sizer.Add(op_sizer, 0, wx.EXPAND, 0)
                btn = wx.StaticBitmap(
                    self.op_panel,
                    id=wx.ID_ANY,
                    size=(BUTTONSIZE, BUTTONSIZE),
                    # style=wx.BORDER_RAISED,
                )
                col, image = get_bitmap(op)
                if image is not None:
                    pass
                if col is not None:
                    btn.SetBackgroundColour(wx.Colour(swizzlecolor(col)))
                else:
                    btn.SetBackgroundColour(wx.LIGHT_GREY)
                if image is None:
                    btn.SetBitmap(wx.NullBitmap)
                else:
                    btn.SetBitmap(image)

                btn.SetToolTip(
                    str(op)
                    + "\n"
                    + _("Assign the selected elements to the operation.")
                    + "\n"
                    + _("Right click: Extended options for operation")
                )
                btn.SetMinSize(dip_size(self, 20, -1))
                btn.SetMaxSize(dip_size(self, 20, -1))

                # btn.Bind(wx.EVT_ENTER_WINDOW, self.on_mouse_over)
                # btn.Bind(wx.EVT_LEAVE_WINDOW, self.on_mouse_leave)
                btn.Bind(wx.EVT_LEFT_DOWN, on_button_left(op))
                btn.Bind(wx.EVT_RIGHT_DOWN, on_button_right(op))
                # btn.Bind(wx.EVT_LEFT_DCLICK, on_button_doubleclick(op))
                op_sizer.Add(btn, 0, wx.ALIGN_CENTER_VERTICAL, 0)
                info = op.type[3:].capitalize()
                if op.label is not None:
                    info = info[0] + ": " + op.label
                header = wx.StaticText(
                    self.op_panel, wx.ID_ANY, label=info, style=wx.ST_ELLIPSIZE_END
                )
                header.SetToolTip(
                    _("Click to select all contained elements on the scene.")
                    + "\n"
                    + _("Double click to open the property dialog for the operation")
                )
                header.SetMinSize(dip_size(self, 30, -1))
                header.SetMaxSize(dip_size(self, 70, -1))
                op_sizer.Add(header, 1, wx.ALIGN_CENTER_VERTICAL, 0)
                if self.std_color_back is None:
                    self.std_color_back = wx.Colour(header.GetBackgroundColour())
                    self.std_color_fore = wx.Colour(header.GetForegroundColour())
                self.op_ctrl_list.append((op, header))
                header.Bind(wx.EVT_LEFT_DOWN, on_label_single(op))
                header.Bind(wx.EVT_LEFT_DCLICK, on_label_double(op))

                c_out = wx.CheckBox(self.op_panel, id=wx.ID_ANY)
                c_out.SetMinSize(dip_size(self, 30, -1))
                c_out.SetMaxSize(dip_size(self, 50, -1))

                if hasattr(op, "output"):
                    flag = bool(op.output)
                    c_out.SetValue(flag)
                    showflag = not flag
                else:
                    c_out.Enable(False)
                    showflag = False
                c_out.SetToolTip(
                    _("Enable this operation for inclusion in Execute Job.")
                )
                op_sizer.Add(c_out, 1, wx.ALIGN_CENTER_VERTICAL, 0)

                c_show = wx.CheckBox(self.op_panel, id=wx.ID_ANY)
                c_show.SetMinSize(dip_size(self, 30, -1))
                c_show.SetMaxSize(dip_size(self, 50, -1))
                c_show.SetToolTip(_("Hide all contained elements on scene if not set."))

                self.op_panel.Bind(wx.EVT_CHECKBOX, on_check_output(op, c_show), c_out)
                self.op_panel.Bind(wx.EVT_CHECKBOX, on_check_show(op), c_show)
                if hasattr(op, "is_visible"):
                    flag = bool(op.is_visible)
                    c_show.SetValue(flag)
                else:
                    showflag = False
                c_show.Enable(showflag)
                op_sizer.Add(c_show, 1, wx.ALIGN_CENTER_VERTICAL, 0)

                t_power = TextCtrl(
                    self.op_panel,
                    wx.ID_ANY,
                    "",
                    limited=True,
                    check="float",
                    style=wx.TE_PROCESS_ENTER,
                    nonzero=True,
                )

                t_power.SetMinSize(dip_size(self, 30, -1))
                t_power.SetMaxSize(dip_size(self, 70, -1))
                op_sizer.Add(t_power, 1, wx.ALIGN_CENTER_VERTICAL, 0)
                if hasattr(op, "power"):
                    if op.power is not None:
                        sval = op.power
                    else:
                        sval = 0
                    if self.use_percent:
                        t_power.SetValue(f"{sval / 10:.0f}")
                        unit = "%"
                    else:
                        t_power.SetValue(f"{sval:.0f}")
                        unit = "ppi"
                    t_power.SetToolTip(_("Power ({unit})").format(unit=unit))
                else:
                    t_power.Enable(False)
                t_power.SetActionRoutine(on_power(op, t_power))

                t_speed = TextCtrl(
                    self.op_panel,
                    wx.ID_ANY,
                    "",
                    limited=True,
                    check="float",
                    style=wx.TE_PROCESS_ENTER,
                    nonzero=True,
                )
                t_speed.SetMinSize(dip_size(self, 30, -1))
                t_speed.SetMaxSize(dip_size(self, 70, -1))
                op_sizer.Add(t_speed, 1, wx.ALIGN_CENTER_VERTICAL, 0)
                if hasattr(op, "speed"):
                    if op.speed is not None:
                        sval = op.speed
                    else:
                        sval = 0
                    if self.use_mm_min:
                        t_speed.SetValue(f"{sval * 60:.0f}")
                        unit = "mm/min"
                    else:
                        t_speed.SetValue(f"{sval:.1f}")
                        unit = "mm/s"
                    t_speed.SetToolTip(_("Speed ({unit})").format(unit=unit))
                else:
                    t_speed.Enable(False)
                t_speed.SetActionRoutine(on_speed(op, t_speed))

        self.op_panel.SetupScrolling()
        self.operation_sizer.Layout()
        self.op_panel.Layout()
        self.highlight_operations()
        self.op_panel.Thaw()
        self.op_panel.Refresh()
        # print (f"Fill operations called: {len(self.op_panel.GetChildren())}")

    def highlight_operations(self):
        active_ops = []
        highlight_back = wx.SystemSettings().GetColour(wx.SYS_COLOUR_HIGHLIGHT)
        highlight_fore = wx.SystemSettings().GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT)
        highlight_back.ChangeLightness(32)

        for elem in self.context.elements.flat(types=elem_nodes, emphasized=True):
            for op in self.context.elements.ops():
                for node in op.children:
                    if node.type == "reference":
                        node = node.node
                    if node is elem:
                        if op not in active_ops:
                            active_ops.append(op)
                        break
        # print(f"Active ops: {len(active_ops)}")
        for op, ctrl in self.op_ctrl_list:
            if op in active_ops:
                ctrl.SetBackgroundColour(highlight_back)
                ctrl.SetForegroundColour(highlight_fore)
            else:
                ctrl.SetBackgroundColour(self.std_color_back)
                ctrl.SetForegroundColour(self.std_color_fore)
            ctrl.Refresh()

    def pane_show(self, *args):
        pass

    def pane_hide(self, *args):
        pass

    @signal_listener("element_property_update")
    @signal_listener("element_property_reload")
    def signal_handler_update(self, origin, *args, **kwargs):
        hadops = False
        if len(args) > 0:
            if isinstance(args[0], (list, tuple)):
                myl = args[0]
            else:
                if args[0] is self.context.elements.op_branch:
                    myl = list(self.context.elements.ops())
                else:
                    myl = [args[0]]
            for n in myl:
                if n.type.startswith("op "):
                    hadops = True
                    break
        # print (f"Signal elem update called {args} / {kwargs} / {len(list(self.context.elements.ops()))}")
        if hadops:
            self.ask_for_refill()

    @signal_listener("rebuild_tree")
    @signal_listener("refresh_tree")
    @signal_listener("tree_changed")
    @signal_listener("operation_removed")
    @signal_listener("add_operation")
    def signal_handler_rebuild(self, origin, *args, **kwargs):
        # print (f"Signal rebuild called {args} / {kwargs} / {len(list(self.context.elements.ops()))}")
        self.ask_for_refill()

    def ask_for_refill(self):
        # Endless cup
        if self.ignore_refill:
            self.ignore_refill = False
            return
        self.context.schedule(self._refill_job)

    @signal_listener("power_percent")
    @signal_listener("speed_min")
    @lookup_listener("service/device/active")
    def on_device_update(self, *args):
        self.set_display()
        self.ask_for_refill()

    @signal_listener("emphasized")
    def signal_handler_emphasized(self, origin, *args, **kwargs):
        self.highlight_operations()


# Question - 4
# Function to print all the leaves in a given binary tree

class Node:
    def __init__(self,data):
        self.data = data
        self.left = None
        self.right = None

class Tree:
    def insert(self,node,data):
        if node is None:
            return Node(data)
        else:
            if data < node.data:
                node.left = self.insert(node.left,data)
            else:
                node.right = self.insert(node.right,data)
        return node

    def print_leaves(self,root):
        if root is not None:
            if root.left is None and root.right is None:
                print(root.data,end = " ")
            else:
                self.print_leaves(root.left)
                self.print_leaves(root.right)

root = Node(6)
tree = Tree()

tree.insert(root,2)
tree.insert(root,1)
tree.insert(root,3)
tree.insert(root,8)
tree.insert(root,7)
tree.insert(root,9)

print("Leaves of binary tree : ",end = " ")
tree.print_leaves(root)

# tree format
#        6
#      /   \
#     2     8
#    / \   / \
#   1   3 7   9

# -----------OUTPUT----------

# Leaves of binary tree :  1 3 7 9


from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
    path('search', views.search, name='search'),
    path('profile', views.profile, name='profile'),

    # quizzes
    path('quiz/new', views.new_quiz, name='new-quiz'),
    path('quiz/<str:quiz_url>', views.quiz_page, name='quiz-page'),
    path('quiz/<str:quiz_url>/edit', views.edit_quiz, name='edit-quiz'),
    path('quiz/<str:quiz_url>/add-question', views.add_quiz_question, name='add-quiz-question'),
    path('quiz/<str:quiz_url>/remove-question/<int:question_id>', views.remove_quiz_question, name='remove-quiz-question'),

    # authentication
    path('auth/register', views.register, name='register'),
    path('auth/sign-in', views.sign_in, name='sign-in'),
    path('auth/sign-out', views.sign_out, name='sign-out')
]


from __future__ import print_function
from __future__ import absolute_import
from __future__ import division

import os

import scriptcontext as sc
import rhinoscriptsyntax as rs

import compas_tna
from compas.rpc import Proxy

__commandname__ = "TNA_init"

def get_document_basename():
    return rs.DocumentName()

def get_document_filename():
    basename = get_document_basename()
    if not basename:
        return None
    return os.path.splitext(basename)[0]

def get_document_extension():
    basename = get_document_basename()
    if not basename:
        return None
    return os.path.splitext(basename)[1]

def get_document_filepath():
    return rs.DocumentPath()

def get_document_dirname():
    filepath = get_document_filepath()
    if not filepath:
        return None
    return os.path.dirname(filepath)

# ==============================================================================
# Command
# ==============================================================================

def RunCommand(is_interactive):
    sc.sticky["TNA"] = {
        'proxy' : Proxy(),

        'form'  : None,
        'force' : None,

        'settings' : {
            'layer.form'  : 'TNA::FormDiagram',
            'layer.force' : 'TNA::ForceDiagram',

            'horizontal.kmax'  : 100,
            'horizontal.alpha' : 100,

            'boundaries.feet' : 2,

            'show.forces'    : False,
            'show.reactions' : False,
            'show.angles'    : False,

            'scale.forces'    : 0.1,
            'scale.reactions' : 0.1,

            'tol.angle' : 5.0,

            'file.dir'  : get_document_dirname(),
            'file.name' : None
        }
    }

# ==============================================================================
# Main
# ==============================================================================

if __name__ == '__main__':

    RunCommand(True)


from sweetpea import *
import os
_dir=os.path.dirname(__file__)
### REGULAR FACTORS
_kT = Factor("]kT", ["gfno%S!y", "5Ex!Si", "SFuS$^dox]O", "JNM", "FXgs9U;xQ", "cpxjNiurae_P"])
t_toElWzUOfkCN = Factor("t toElWzUOfkCN", [Level("XDQm", 7), Level("8*b2", 1), Level("OVk6EQh", 1), Level("{<T_aIYLJQO", 1), Level("mSb0", 1), Level("&ftzV", 4)])

design=[_kT,t_toElWzUOfkCN]
crossing=[_kT,t_toElWzUOfkCN]

### APPENDIX
block=CrossBlock(design,crossing,[])
experiment=synthesize_trials(block,1)
save_experiments_csv(block,experiment,os.path.join(_dir,"out_code_2/6_2_1"))

### END


import numpy
import requests
import Quandl
import datetime

from pyrnkr.application import App
from pyrnkr.widgets import Line
from pyrnkr.formula import Trace

def extract_date_index(ts, format='%Y-%m-%d'):
    return [x.strftime(format) for x in ts.index.tolist()]

class oil(App):
    # This must be consistent with config.json
    required_parameters = []
    title = 'Overview of Oil'  # Appears on top of your application page
    subtitle = 'Supply, Consumption, and Prices of Oil Products'  # Subtitle to the above

    # Production Chart Primary
    MultiLineMultiTypeRigCount = "BKRHUGHES/RIGS_BY_BASIN_TOTAL_US_RIG_COUNT"  # Example data
    SingleLineRigTotal = "BKRHUGHES/RIGS_BY_STATE_TOTALUS_TOTAL"

    # Price Charts
    BRENT = "EIA/PET_RBRTE_D"
    WTI = "EIA/PET_RWTC_D"

    # Oil Secondary
    MiningUSOilProduction = "FRED/IPG211111CN"
    ImportsEndUseCrude = "FRED/IR10000"
    OilAndGasWells = "FRED/IPN213111N"
    PrivateFixedInvestmentWellsExploration = "FRED/E318RC1Q027SBEA"

    # Quandl Token
    TOKEN = ''  # YOUR TOKEN HERE

    def __init__(self, *args, **kwargs):
        super(oil, self).__init__(*args, **kwargs)

    def get_trace(self, symbol):
        """Get trace for a symbol"""
        data = Quandl.get(symbol, authtoken=self.TOKEN)
        if data.empty:
            raise Exception('could not load series from data source')  # If you'd like to handle network
            # errors or retry do it here

        datay = data[data.columns[0]].tolist()  # Data can be manipulated in node.js / python (pandas or numpy)
        x = extract_date_index(data)

        if len(datay) != len(x):
            raise Exception('x and y length do not match')  # Sanity Check, trust but verify data feeds

        tr = Trace(
            x = x,
            y = datay,
            extra = {
                'name': symbol
            }
        )

        return tr  # This returns the data to be plotted

    def execute(self, parameters):
        # Create object to plot
        res = {
            self.SingleLineRigTotal: {
                'title': 'Total U.S. Rig Counts',  # Title of individual plot
                'subtitle': 'Total U.S. Rotary Rig Counts',  # Subtitle of individual plot
                'dimension': 'col-md-12',  # Bootstrap column dimensions
            },
            self.BRENT: {
                'title': 'Brent Crude',
                'subtitle': 'USD Price of Brent Crude Oil',
                'dimension': 'col-md-6',
            },
            self.WTI: {
                'title': 'WTI Crude',
                'subtitle': 'USD Price of WTI Crude Oil',
                'dimension': 'col-md-6',
            },
            self.MiningUSOilProduction: {
                'title': 'US Oil Production',
                'subtitle': 'US Oil Production (Indexed 2012 = 100)',
                'dimension': 'col-md-6',
            },
            self.ImportsEndUseCrude: {
                'title': 'US Crude Imports',
                'subtitle': 'Crude Oil Imports (Indexed 2000 = 100) (Not Seasonally Adjusted)',
                'dimension': 'col-md-6',
            },
            self.OilAndGasWells: {
                'title': 'US Oil and Gas Wells',
                'subtitle': 'Drilling oil and gas wells',
                'dimension': 'col-md-6',
            },
            self.PrivateFixedInvestmentWellsExploration: {
                'title': 'Fixed Investment Wells and Exploration',
                'subtitle': '(In Billions USD) (Quarterly Seasonally Adjusted)',
                'dimension': 'col-md-6',
            }
        }

        # Because we are using only RNKR line plots iterate through the above create line widgets of variable size
        for k, v in res.iteritems():
            ts = self.get_trace(k)
            res[k]['widget'] = Line(
                title=v['title'],
                subtitle=v['subtitle'],
                dimension=v['dimension'],
                traces=[ts]
            )

        # Render the layout object, primary array is app page, secondary arrays are each bootstrap column
        # Styling is dictated here and in the bootstrap column dimensions above
        layout = self.render([
            [res[self.SingleLineRigTotal]['widget']],
            [
                res[self.WTI]['widget'],
                res[self.BRENT]['widget']
            ],
            [
                res[self.MiningUSOilProduction]['widget'],
                res[self.ImportsEndUseCrude]['widget']
            ],
            [
                res[self.OilAndGasWells]['widget'],
                res[self.PrivateFixedInvestmentWellsExploration]['widget']
            ],
        ])

        return layout, None

def handler(event, context):
    """
    AWS Lambda Handler
    Inputs depend on your config.json
    """
    res, err = oil().run(event)
    if err:
        raise Exception(err)

    return res

# Left for convenience / example of debugging aws lambdas prior to upload
#import json
#print json.dumps(handler({}, {}))


#!/usr/bin/env python

# v0.1 - Initial construct, generic input & display
# v0.2 - Removed extra axes,
# v0.3 - Removed all extra, only essential info displayed.
import pygame

#define colors
BLACK = (   0,  0,  0)
WHITE = (255, 255, 255)

#define constants
MAX_ANGLE = 15
MAX_ANGLE_PER_SECOND = 6
JOY_AXIS = 1
FPS = 40
#Classes
class TextPrint:
    def __init__(self):
        self.reset()
        self.font = pygame.font.Font(None,20)

    def Print(self, screen, textString):
        textBitmap = self.font.render(textString,True,BLACK)
        screen.blit(textBitmap,[self.x,self.y])
        self.y += self.line_height

    def reset(self):
        self.x = 10
        self.y = 10
        self.line_height = 15

    def indent(self):
        self.x += 10

    def unindent(self):
        self.x -= 10

pygame.init()

#set width and height of screen [width, height]
size = [500, 500]
screen = pygame.display.set_mode(size)

pygame.display.set_caption("Joystick Read")

#loop until user clicks close button
done = False

#Used for screen update rate
clock = pygame.time.Clock()

#initialize the joysticks
pygame.joystick.init()

#Prep print
textPrint = TextPrint()

#initialize variables
joy_button_pressed = False
joystick_commanded_angle = 0
hydrofoil_set_angle = 0
hydrofoil_commanded_angle = 0
joy_button_pressed = False
joy_button_last = False

# ------------- Main Program ------------
while done == False:
    #EVENT PROCESSING
    for event in pygame.event.get():    #User changed joystick parameter
        if event.type == pygame.QUIT:   #if user closed window
            done = True

        #Possible joystick actions: JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN JOYBUTTONUP JOYHATMOTION

        if event.type == pygame.JOYBUTTONDOWN:
            joy_button_pressed ^= True
            #if button pressed, update commanded value
            if(joy_button_pressed == True):
                hydrofoil_commanded_angle = hydrofoil_set_angle

        #DRAW SCREEN
        #Clear screen to be white
        screen.fill(WHITE)
        textPrint.reset()

        #get count of joysticks
        joystick_count = pygame.joystick.get_count()

        #for each joystick:
        for i in range(joystick_count):
            joystick = pygame.joystick.Joystick(i)
            joystick.init()

            textPrint.Print(screen,"Hold Hydrofoil Angle: {}".format(joy_button_pressed) )
            textPrint.Print(screen,'')

            joystick_commanded_angle = joystick.get_axis(JOY_AXIS) * MAX_ANGLE_PER_SECOND/FPS
            hydrofoil_commanded_angle += joystick_commanded_angle

            if(hydrofoil_commanded_angle > MAX_ANGLE):
                hydrofoil_commanded_angle = MAX_ANGLE

            if(hydrofoil_commanded_angle < -MAX_ANGLE):
                hydrofoil_commanded_angle = -MAX_ANGLE

            if(joy_button_pressed == True):
                #hydrofoil_set_angle = hydrofoil_commanded_angle
                joy_button_last = True
            else:
                hydrofoil_set_angle =  hydrofoil_commanded_angle
                joy_button_last = False

            textPrint.Print(screen, "Joystick Command Angle: {:>6.1f}".format(joystick_commanded_angle) )
            textPrint.Print(screen, "Hydrofoil Set Angle: {:>6.1f}".format(hydrofoil_set_angle) )

        #Update screen
        pygame.display.flip()

        #Limit to 20 frames per second
        clock.tick(FPS)

#close the window and quit
pygame.quit()


# -*- coding: utf-8 -*-

'''
Entrada:
 (1) Coleta um número, em uma linha, converte em inteiro, pelo int() e atribui para a variável n.
 (2) Cria uma variável factorial com valor n.

Desenvolvimento:
 (1) Inicia um loop for de intervalo [2, n). Dentro desse loop:
 (2) Faz factorial *= i

Saída:
 (1) Imprime factorial na tela
'''

n = int(input())  # Coleta um número inteiro

factorial = n  # Inicia uma variável factorial com valor n

for i in range(2, n):  # Inicia um loop [2, n)
    factorial *= i  # Multiplica o valor de i à variável factorial

print(factorial)  # Imprime o fatorial


# -*- coding: utf-8 -*-
"""
Code to determine the grid from all the ionograms in a folder
using adjusted peak detection (From the weighed sum of all the image plots in a subsubdirectory,
the indices corresponding to the grid values are determined)

"""
# Library imports
import numpy as np
import pandas as pd
import scipy.signal as signal

#Determined from determine_default_grid_values
HZ = [1.5,2.0,2.5,3.5,4.5,5.5,6.5,7.0,7.5,8.5,9.5,10.5,11.5]
DEFAULT_HZ_COORD = [149,273,335,390,496,604,711,837,898,964,1128,1314,1444]
MEAN_HZ = [0.5*(num + DEFAULT_HZ_COORD[i+1])for i, num in enumerate(DEFAULT_HZ_COORD[:-1])]
UPPER_LIMIT_HZ_COORD =[89] + MEAN_HZ
LOWER_LIMIT_HZ_COORD = MEAN_HZ + [1510]

KM_DEFAULT_100 = 55
KM_DEFAULT_200 = 110

def all_stack(df_img):
    """Returns the equally weighed sum of all the correctly extracted ionogram plot areas in a subsubdirectory
    z
    :param df_img: Dataframe contaning all the correctly extracted ionogram plot areas in a subsubdirectory (output of image_segmentation.segment_images_in_subdir.segment_images)
    :type df_img: class: `pandas.core.frame.DataFrame`
    :param cutoff_width: the width of an ionogram should be within cutoff_width of the median width of all the ionogram in a subdirectory (should be the same as the one used in scan2data.image_segmentation.segment_images_in_subdir.segment_images)
    :type cutoff_width: int
    :param cutoff_height: the height of an ionogram should be within cutoff_height of the median height of all the ionogram in a subdirectory (should be the same as the one used in scan2data.image_segmentation.segment_images_in_subdir.segment_images)
    :type cutoff_height: int
    :returns: weighed_sum i.e. equally weighed sum of all the extracted ionogram plot areas in a subsubdirectory
    :rtype: class: `numpy.ndarray`
    """

    # Pad the image if needed
    max_h = df_img["height"].max()
    max_w =  df_img["width"].max()
    median_h = int(np.median(df_img["height"]))
    median_w = int(np.median(df_img["width"]))

    df_img["padded"] = df_img["ionogram"].apply(lambda img: np.pad( img, ((0,max_h-img.shape[0]),(0,max_w-img.shape[1])),mode="constant",constant_values=1))

    #Weighed sum of the ionograms in a subdirectory <-- Q: WHY DO WE HAVE TO TAKE THE WEIGHTED SUM OF ALL OF THE IONOGRAMS? SHOULDN'T EACH IONOGRAM BE ANALYZED INDIVIDUALLY?
    weight = len(df_img.index)
    weighed_sum = weight * np.sum((df_img["padded"]).tolist(), axis = 0)
    median_h = median_h if max_h>median_h else median_h-1

    return weighed_sum[0:-(max_h-median_h),0:-(max_w-median_w)]

def indices_highest_peaks(img, row_or_col,
                      peak_prominence_threshold=0.1, distance_between_peaks=5):
    """Determines and returns the indices of peak median values from the rows or column of an image

    :param img: grayscale image in the form of an 2D uint8 array
    :type img: class: `numpy.ndarray`
    :param row_or_col: 0 for colum or 1 for row
    :type row_or_col: int
    :param peak_prominence_threshold: the threshold to detect peaks that correspond to the grid lines, defaults to 0.1
    :type peak_prominence_threshold: int, optional
    :param distance_between_peaks: the minimum number of samples between subsequent peaks, defaults to 5
    :type distance_between_peaks: int, optional
    :returns: select_peaks i.e. array of the indices of peak median values from the rows or column of an image
    :rtype: class: `numpy.ndarray`
    """

    # Median values along each row or column
    median_values = np.median(img,row_or_col)

    # Normalize median values so they are between 0 and 1
    median_values_normalized = (median_values - np.min(median_values))/(np.max(median_values)-np.min(median_values))

    # Prepare peaks for peak detection function: the peaks should be pointing upwards
    peaks_function = 1 + -1*median_values_normalized

    # Detect all peaks
    select_peaks, _ = signal.find_peaks(peaks_function, distance=distance_between_peaks, prominence=peak_prominence_threshold) #from scipy.signal

    #Remove edges from peaks
    h,w = img.shape
    if row_or_col == 0:
        select_peaks = select_peaks[:w]
    else:
        select_peaks = select_peaks[:h]

    return select_peaks

def adjust_arr_peaks(weighed_sum,arr_peaks,desired_length,row_or_col,
                  distance_between_peaks=30,peak_prominence_threshold=0.1,n_tries=1000,update_amount=0.01):
    """Adjust an array of peaks to the desired length and returns it.

     :param weighed_sum: equally weighed sum of all the image plot areas in a subsubdirectory
     :type weighed_sum: class: `numpy.ndarray`
     :param arr_peaks: array of peaks to adjust to the desired length
     :type arr_peaks: class: `numpy.ndarray`
     :param desired_length: number of elements desired in array
     :type desired_length: int
     :param row_or_col: 0 for colum or 1 for row
     :type row_or_col: int
     :param distance_between_peaks: the minimum number of samples between subsequent peaks, defaults to 30
     :type distance_between_peaks: int, optional
     :param peak_prominence_threshold: the threshold to detect peaks that correspond to the grid lines, defaults to 0.1
     :type peak_prominence_threshold: int, optional
     :param n_tries: the number of maximum tries to adjust arr, defaults to 1000
     :type n_tries: int, optional
     :param update_amount: by how much peak_prominence_threshold is updated for each iteration, defaults to 0.01
     :type update_amount: int, optional
     :returns: select_peaks i.e. adjusted array of the indices of peak median values from the rows or column of an image
     :rtype: class: `numpy.ndarray`
     ..note:: To prevent infinite loops, the script only runs for a maximum of n_tries times
    """

    arr_peaks = indices_highest_peaks(weighed_sum, row_or_col, peak_prominence_threshold, distance_between_peaks)  #Q: WHY IS THE FUNCTION TAKING IN arr_peaks IF IT IS ALREADY CALCULATING IT HERE?

    # Adjust if lenght is not the desired length by re-running indices_highest_peaks with different parameters
    while len(arr_peaks) != desired_length and n_tries !=0:

        if len(arr_peaks) > desired_length:
            # increase peak_prominence_threshold
            peak_prominence_threshold = peak_prominence_threshold + update_amount
            arr_peaks = indices_highest_peaks(weighed_sum, row_or_col,
                                               peak_prominence_threshold, distance_between_peaks)
        else:
            # decrease peak_prominence_threshold
            peak_prominence_threshold = peak_prominence_threshold - update_amount
            arr_peaks = indices_highest_peaks(weighed_sum, row_or_col,
                                               peak_prominence_threshold, distance_between_peaks)
        n_tries = n_tries - 1

    return arr_peaks

def get_grid_mappings(weighed_sum,
                      use_defaults=True,min_index_row_peaks=40):
    """Determines and returns the the mapping between coordinate values and frequency/depth values in a subdirectory

    :param weighed_sum: equally weighed sum of all the image plot areas in a subsubdirectory
    :type weighed_sum: class: `numpy.ndarray`
    :param use_defaults: use default values , defaults to True
    :type use_defaults: bool
    :param min_index_row_peaks: starting index to consider for peaks to determine km lines,defaults to 40
    :type min_index_row_peaks: int, optional
    :returns:  col_peaks,row_peaks,mapping_Hz, mapping_km i.e. one-dimmensional array of detected peaks of ionogram by column, one-dimmensional array of detected  peaks of by row, dictionary mapping of depth (km) to y coordinates  , dictionary mapping of frequency (Hz) to x coordinates
    :rtype: class: `numpy.ndarray`,class: `numpy.ndarray`,class: `dict`, class: `dict`
    :raises Exception: returns np.nan,np.nan,np.nan,np.nan
    """
    # Detect peaks
    col_peaks = indices_highest_peaks(weighed_sum, 0)
    row_peaks = indices_highest_peaks(weighed_sum, 1)

    # Map col_peaks to Hz values
    if len(col_peaks) == len(HZ):
        mapping_Hz = dict(zip(HZ,col_peaks))

    # Map adjusted col_peaks to Hz values
    else:
        try:
            col_peaks = adjust_arr_peaks(weighed_sum, col_peaks, len(HZ), 0)
            mapping_Hz = dict(zip(HZ,col_peaks))

            # Map adjusted HZ values to default coordinates if need be
            if use_defaults:
                for i,key in enumerate(HZ):
                    if mapping_Hz[key] > UPPER_LIMIT_HZ_COORD[i] or mapping_Hz[key] < LOWER_LIMIT_HZ_COORD[i]:
                        mapping_Hz[key] = DEFAULT_HZ_COORD[i]
        except:
            if use_defaults:
                 mapping_Hz = dict(zip(HZ,DEFAULT_HZ_COORD))
            else:
                return np.nan,np.nan,np.nan,np.nan

    row_peaks = row_peaks[row_peaks > min_index_row_peaks]

    try:
        row_100 = row_peaks[0] #Should be around 30 for 100 km
        row_200 = row_peaks[1] #Should be around 30 for 100 km
    except:
        if use_defaults:
            row_100 = KM_DEFAULT_100
            row_200 = KM_DEFAULT_200
        else:
            return np.nan,np.nan,np.nan,np.nan

    if use_defaults:
        if abs(row_100 - KM_DEFAULT_100) > abs(KM_DEFAULT_200 - KM_DEFAULT_100):
            row_100 = KM_DEFAULT_100
        if abs(row_200 - KM_DEFAULT_200) > abs(KM_DEFAULT_200 - KM_DEFAULT_100):
            row_200 = KM_DEFAULT_200

    mapping_km = {100:row_100,200:row_200}

    return col_peaks, row_peaks, mapping_Hz, mapping_km


import numpy as np
from easydict import EasyDict as edict

__C = edict()
cfg = __C

# dataset config
__C.DATASET = edict()
__C.DATASET.USE_MULTI_SWEEPS = False
__C.DATASET.MAX_NUM_SWEEPS = 5
__C.DATASET.NUM_SWEEPS = 3
__C.DATASET.USE_CYLINDER = False
__C.DATASET.POINT_CLOUD_RANGE = [-72, -72, -2, 72, 72, 4.4]
__C.DATASET.VOXEL_SIZE = [0.1, 0.1, 0.1]
__C.DATASET.DIM_POINT = 6
__C.DATASET.USE_IMAGE_FEATURE = False
__C.DATASET.DIM_IMAGE_FEATURE = 28
__C.DATASET.NUM_CLASSES = 22
__C.DATASET.CLASS_NAMES = []
__C.DATASET.CLASS_WEIGHT = []
__C.DATASET.PALETTE = []
__C.DATASET.IGNORE_INDEX = 255

__C.DATASET.AUG_DATA = True
__C.DATASET.AUG_ROT_RANGE = [-0.78539816, 0.78539816]
__C.DATASET.AUG_SCALE_RANGE = [0.95, 1.05]
__C.DATASET.AUG_TRANSLATE_STD = 0.5
__C.DATASET.AUG_SAMPLE_RATIO = 0.95
__C.DATASET.AUG_SAMPLE_RANGE = 50.0
__C.DATASET.AUG_COLOR_DROP_RATIO = 0.5

__C.DATASET.VISUALIZE = False

# model config
__C.MODEL = edict()
__C.MODEL.SEGMENTOR = 'segformer'
__C.MODEL.LOSSES = {'ohem_ce': 1.0, 'lovasz': 1.0}
__C.MODEL.OHEM_KEEP_RATIO = 0.3
__C.MODEL.OHEM_KEEP_THRESH = 0.7
__C.MODEL.AUX_LOSS_WEIGHT = 0.4
__C.MODEL.BATCHING_INFO = [
    {
        '0': {'max_tokens': 16, 'batching_range': [0, 16]},
        '1': {'max_tokens': 64, 'batching_range': [16, 64]},
        '2': {'max_tokens': 256, 'batching_range': [64, 256]},
        '3': {'max_tokens': 800, 'batching_range': [256, 100000]}
    },
    {
        '0': {'max_tokens': 32, 'batching_range': [0, 32]},
        '1': {'max_tokens': 128, 'batching_range': [32, 128]},
        '2': {'max_tokens': 512, 'batching_range': [128, 512]},
        '3': {'max_tokens': 800, 'batching_range': [512, 100000]}
    },
    {
        '0': {'max_tokens': 64, 'batching_range': [0, 64]},
        '1': {'max_tokens': 160, 'batching_range': [64, 160]},
        '2': {'max_tokens': 384, 'batching_range': [160, 384]},
        '3': {'max_tokens': 800, 'batching_range': [384, 100000]}
    },
    {
        '0': {'max_tokens': 128, 'batching_range': [0, 128]},
        '1': {'max_tokens': 256, 'batching_range': [128, 256]},
        '2': {'max_tokens': 512, 'batching_range': [256, 512]},
        '3': {'max_tokens': 800, 'batching_range': [512, 100000]}
    }
]
__C.MODEL.WINDOW_SHAPE = [10, 10, 8]
__C.MODEL.DEPTHS = [3, 4, 8, 3]
__C.MODEL.DROP_PATH_RATE = 0.3

# training config
__C.TRAIN = edict()
__C.TRAIN.OPTIMIZER = 'adamw'
__C.TRAIN.LR = 0.001
__C.TRAIN.WEIGHT_DECAY = 0.01
__C.TRAIN.MOMENTUM = 0.9
__C.TRAIN.LR_SCHEDULER = 'warmup_poly_lr'

def cfg_from_file(filename):
    """Load a config file and merge it into the default options."""
    import yaml
    with open(filename, 'r') as f:
        yaml_cfg = edict(yaml.load(f, Loader=yaml.FullLoader))

    _merge_a_into_b(yaml_cfg, __C)

def _merge_a_into_b(a, b):
    """Merge config dictionary a into config dictionary b, clobbering the
    options in b whenever they are also specified in a.
    """
    if type(a) is not edict:
        return

    for k, v in a.items():
        # a must specify keys that are in b
        if k not in b:
            raise KeyError('{} is not a valid config key'.format(k))
        # the types must match, too
        old_type = type(b[k])
        if old_type is not type(v):
            if isinstance(b[k], np.ndarray):
                v = np.array(v, dtype=b[k].dtype)
            else:
                raise ValueError(('Type mismatch ({} vs. {}) '
                                  'for config key: {}').format(type(b[k]), type(v), k))
        # recursively merge dicts
        if type(v) is edict:
            try:
                _merge_a_into_b(a[k], b[k])
            except:
                print('Error under config key: {}'.format(k))
                raise
        else:
            b[k] = v


# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    # 和上一题层次遍历一样，只不过最后根据level层数进行了翻转
    def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
        if root == None:return []
        res = {}
        level = 0
        queue = [(root,level)]
        while len(queue) != 0:
            cur,level = queue.pop(0)
            if cur == None:
                continue
            else:
                res.setdefault(level,[]).append(cur.val) # 如果当前level还没有生成list，则为赋值为空列表[]
                queue.append((cur.left,level+1))
                queue.append((cur.right,level+1))
        # 找出最大层数
        maxlevel = max(res.keys())
        results = [[] for i in range(maxlevel+1)]
        for key,value in res.items():
            if key % 2 == 1: # 如果层数为奇数，则翻转该层结果
                value.reverse()
            results[key] = value
        return results


#35분 #그래프이론 #그래프탐색 #BFS #DFS
#DFS를 배우기 전, 나만의 방식으로 풀어보았다.
"""
https://www.acmicpc.net/problem/2606
[2606] : 바이러스
"""
def sol(m, arr):
    for _ in range(m):
        for i in range(m):
            if visited[arr[i][0]] == True:
                visited[arr[i][1]] = True
            if visited[arr[i][1]] == True:
                visited[arr[i][0]] = True

    return visited.count(True)

N = int(input())
visited = [False] * (N+1)
visited[1] = True

M = int(input())
arr = []
for i in range(M):
    l = list(map(int, input().split()))
    arr.append(l)

print(sol(M, arr) - 1)

"""
<DFS를 이용한 풀이>

dic = {}
M = int(input())
for i in range(M):
    dic[i+1] = set()

N = int(input())
for i in range(N):
    a, b = map(int, input().split())
    dic[a].add(b)
    dic[b].add(a)

def dfs(start, dic):
    for i in dic[start]:
        if i not in visited:
            visited.append(i)
            dfs(i, dic)

visited = []
dfs(1, dic)
print(len(visited) - 1)
"""


from openerp.osv import fields, orm
import logging
_logger = logging.getLogger(__name__)

class project_issue(orm.Model):
    _inherit = 'project.issue'
    _columns = {
        'ref': fields.char('Reference', size=10, readonly=True, select=True, help="Issue sequence number"),
    }

    def create(self, cr, uid, vals, context={}):
        #Compatible with crm_categ_hierarchy
        def get_categ_sequence_id(categ_res):
            return categ_res.sequence_id.id or (
                     hasattr(categ_res, 'parent_id')
                     and categ_res.parent_id
                     and get_categ_sequence_id(categ_res.parent_id)
                   )

        ret = super(project_issue, self).create(cr, uid, vals, context=context)
        obj = self.browse(cr, uid, ret, context=context)
        if obj.ref:
            _logger.warn('Found a conflicting sequence assignment. Please check customizations made to Project Issues.')
            return res
        seq_id = get_categ_sequence_id(obj.categ_id)
        if seq_id :
            seq_ret = self.pool.get('ir.sequence').next_by_id(cr, uid, seq_id , context=context)
        else:
            #Default sequence, code 'project.issue'
            seq_ret = self.pool.get('ir.sequence').next_by_code(cr, uid, 'project.issue', context=context)
        self.write(cr, uid, [ret], {'ref': seq_ret}, context=context)
        return ret

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:


# -*- coding: utf-8 -*-
#################################################################################

#################################################################################

from odoo import api, fields, models, _

class SaleOrderLine(models.Model):
    _inherit = "sale.order.line"

    product_template_id = fields.Integer(compute='_compute_product_template', string="product template id")
    products_child_readonly = fields.Boolean(string="Simple Service", copy=False,
                                             default=False,
                                             compute='_compute_products_child_readonly')
    products_child = fields.Many2many('product.template', string='Product child',
                                      domain="['|', '&',('product_child_ids', '=',product_template_id),"
                                             "('type', '=', 'service'),('is_shared', '=', True)]",
                                      help='Product child')

    @api.multi
    @api.depends('product_id')
    def _compute_product_template(self):
        for line in self:
            line.product_template_id = line.product_id.product_tmpl_id.id

    @api.multi
    @api.depends('product_id')
    def _compute_products_child_readonly(self):
        for line in self:
            line.products_child_readonly= (line.product_id.is_root in ['simple','no']) \
                                          or (line.product_id.type in ['consu','product'])

    @api.onchange('products_child')
    def _onchange_products_child(self):
        if self.products_child:
            # *self.product_uom_qty
            sum_price = self.price_unit
            product_names = self.product_id.name
            for rec in self.products_child:
                extra_formatting = rec.extra_formatting or '{name}'
                product_names += '\r\n' + extra_formatting.format(name=rec.name, price=rec.list_price)
                if rec.is_auto_price:
                    sum_price = sum_price + rec.list_price

            self.name = product_names
            self.price_unit = sum_price
        else:
            self.name = self.product_id.name
            self.price_unit = self.product_id.list_price

    @api.onchange('product_uom', 'product_uom_qty')
    def product_uom_change(self):
        if not self.product_uom or not self.product_id:
            self.price_unit = 0.0
            return
        if self.order_id.pricelist_id and self.order_id.partner_id:
            sum_extra_price = 0.0
            for rec in self.products_child:
                if rec.is_auto_price:
                    product = rec.with_context(
                        lang=self.order_id.partner_id.lang,
                        partner=self.order_id.partner_id,
                        quantity=self.product_uom_qty,
                        date=self.order_id.date_order,
                        pricelist=self.order_id.pricelist_id.id,
                        uom=self.product_uom.id,
                        fiscal_position=self.env.context.get('fiscal_position'))
                    product_price = self.env['account.tax']._fix_tax_included_price_company(
                        self._get_display_price(product),
                        product.taxes_id, self.tax_id,
                        self.company_id)
                    sum_extra_price = sum_extra_price + product_price

            self.price_unit = self.price_unit + sum_extra_price

    @api.onchange('product_id')
    def _onchange_product_id(self):
        if self.product_id:
            if self.product_id.type == 'service' and self.product_id.is_root != 'simple':
                product_object = self.env['product.template'] \
                    .search(['&', '|',
                             ('id', 'in', self.product_id.product_child_ids.ids),
                             ('is_shared', '=', True), ('is_auto_inserted', '=', True)])
                self.products_child = [(6, 0, product_object.mapped('id'))]


# Functions
# String checker function takes in the question and the list of valid responses
def string_checker(question, to_check):

	Valid = False
	while not Valid:

		# Ask a question and put response into lowercase
		response = input(question).lower()

		if response in to_check:
			return response

		else:
			for item in to_check:
				# Checks if response is the first letter of an item in the list
				if response == item[0]:
					return item

		print("Please answer with either yes or no")
# Main routine
for item in range (0,6):
	want_snacks = string_checker("Do you want any snacks?\n", ["yes", "no"])
	print("You answered", want_snacks ,"to snacks")


import os
import subprocess

from win32com.client import Dispatch
import re
import sys

attention_string = "WARNING: Suspicious keywords were found:\n"
endOfMacro_string = "\n====================== END OF MACRO ======================\n"
noKeywords_string = "INFO: There aren't any suspicous keywords!\n"
horizontal_line = "\n------------------------------------------------------------------------------------------\n"

def check_macro(macro, doc_name, mod_name):
    checkdoc_string = "INFO: Check of document " + doc_name + " and module " + mod_name + " is starting..."
    with open(fileNameKeywords, "a") as outputfile:
        outputfile.write(checkdoc_string)
    print(checkdoc_string)
    p = re.compile("dde|msiexec|winmgmts|Win32_Process|wscript|base64|StrReverse|shell|"
                   "reverse|pwd|password|cred|credentials|http|ssh|ftp|powershell|cmd|&H|\\\\x", re.IGNORECASE)
    m = p.findall(macro)

    if m:
        print(attention_string)
        print("\n".join(m))
        print(endOfMacro_string)
        with open(fileNameKeywords, "a") as outputfile:
            outputfile.writelines(["\n" + attention_string, "\n".join(m), endOfMacro_string])
    else:
        print(noKeywords_string)
        with open(fileNameKeywords, "a") as outputfile:
            outputfile.write("\n" + noKeywords_string)

if __name__ == '__main__':

    # wbpath = 'C:\\Users\\A7808\\PycharmProjects\\MacroChecker\\example.xlsm'

    wbfile = os.path.join(sys.path[0], sys.argv[1])
    wbfile_ext = sys.argv[1].split('.')[1]
    fileNameKeywords = sys.argv[1] + "_keywords.txt"
    fileNameOle = sys.argv[1] + "_OLE.txt"
    no_of_modules = 0
    xl = Dispatch("Excel.Application")
    xl.Visible = 0
    wb = xl.Workbooks.Open(wbfile)

    if wbfile_ext == 'xlsm':

        m_source_code = []
        file = open(fileNameKeywords, 'w+')

        # vbcode = wb.VBProject.VBComponents("Module3").CodeModule
        for module in wb.VBProject.VBComponents:
            # print(module.CodeModule.Lines(1, module.CodeModule.CountOfLines))
            # 1 = VBA Code (https://bettersolutions.com/vba/visual-basic-editor/extensibility-object-model.htm)
            if module.Type == 1:
                module_name = module.CodeModule.Name
                vbcode_object = wb.VBProject.VBComponents(module_name).CodeModule
                # print(vbcode_object.Lines(1, vbcode_object.CountOfLines))
                if vbcode_object.CountOfLines == 0:
                    print("INFO: Module \"%s\" is empty and won't be checked..." % module_name)
                    break
                m_source_code.append(vbcode_object.Lines(1, vbcode_object.CountOfLines))
                check_macro(m_source_code[no_of_modules], wb.Name, module_name)
                no_of_modules += 1
    else:
        bas_file = open(wbfile, 'r')
        check_macro(bas_file.read(), sys.argv[1], '\"bas-File\"')

    # To check if a module name exists (throws an error if not existing)
    # vbcode = wb.VBProject.VBComponents("Module3").Name
    # print(vbcode.Lines(1, vbcode.CountOfLines))

    total_macros = horizontal_line + "There is a total number of " + str(
        no_of_modules) + " VBA Macros in document \"" + wb.Name + "\".\n"
    bas_file_resume = "This is a single bas-file called \"" + wb.Name + "\"."
    if wbfile_ext == "xlsm":
        print(total_macros)
        with open(fileNameKeywords, "a") as myfile:
            myfile.write(total_macros)

    wb.Close(False)

    with open(fileNameOle, 'w', encoding='utf-8') as f:
        process = subprocess.Popen(['oleid', sys.argv[1]], stdout=f, universal_newlines=True)

    # while True:
    #     output = process.stdout.readline()
    #     print(output.strip())
    #     return_code = process.poll()
    #     if return_code is not None:
    #         # Process has finished, read rest of the output
    #         for output in process.stdout.readlines():
    #             print(output.strip())
    #         break


from typing import List

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        n = len(intervals)
        # No sorting, intervals is already sorted.
        # intervals.sort()
        current_interval = []
        current_max = 0
        overlapped = []
        for i,interval in enumerate(intervals):
            current_max = max(current_max, interval[1])
            if i < n - 1 and current_max >= intervals[i + 1][0]:
                # Found an overlap with next interval
                if not current_interval:
                    current_interval = [interval[0]]
            else: # No overlap with next interval
                if current_interval:
                    current_interval.append(current_max)
                    overlapped.append(current_interval)
                    current_interval = []
                    current_max = 0
                else:
                    overlapped.append(interval)
        return overlapped
    def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
        if not intervals:
            return [newInterval]
        if not newInterval and intervals:
            return intervals

        n = len(intervals)
        # Use binary search to find the correct insert index
        start, end, mid = 0, n, 0
        while end - start > 1:
            mid = (end - start)//2
            if newInterval < intervals[start + mid]:
                end = start + mid
            elif newInterval > intervals[start + mid]:
                start += mid

        insertIndex = -1
        if intervals[start][0] < newInterval[0]:
            insertIndex = start + 1
        elif intervals[start][0] > newInterval[0]:
            insertIndex = start
        else: # In case are equals, analize [1] position
            if intervals[start][1] > newInterval[1]:
                insertIndex = start
            else:
                insertIndex = start + 1
        # return
        # Insert newInterval and resolve overlap
        intervals.insert(insertIndex, newInterval)

        return self.merge(intervals)

# Example 1:
intervals = [[1,3],[6,9]]
newInterval = [2,5]
expected = [[1,5],[6,9]]
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)

# Example 2:
intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]]
newInterval = [4,8]
expected = [[1,2],[3,10],[12,16]]
# Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10].
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)

# Example 3: No intervals
intervals = []
newInterval = [2,5]
expected = [[2,5]]
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)

# Example 4: No newInterval and yes intervals.
intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]]
newInterval = []
expected = [[1,2],[3,5],[6,7],[8,10],[12,16]]
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)

# Example 5:
intervals = [[1,5]]
newInterval = [2,3]
expected = [[1,5]]
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)

# Example 6:
intervals = [[1,5]]
newInterval = [5,7]
expected = [[1,7]]
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)

# Example 7:
intervals = [[1,5]]
newInterval = [0,3]
expected = [[0,5]]
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)

# Example 8:
intervals = [[1,5]]
newInterval = [6,8]
expected = [[1,5],[6,8]]
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)

# Example 9:
intervals = [[0,2],[3,5],[6,8],[10,12],[13,15]]
newInterval = [4,7]
expected = [[0,2],[3,8],[10,12],[13,15]]
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)

# Example 10:
intervals = [[0,1],[5,5],[6,7],[9,11]]
newInterval = [12,21]
expected = [[0,1],[5,5],[6,7],[9,11],[12,21]]
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)

# Example 11:
intervals = [[2,3],[5,5],[6,6],[7,7],[8,11]]
newInterval = [6,13]
expected = [[2,3],[5,5],[6,13]]
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)

# Example 12:
intervals = [[3,6],[9,9],[11,13],[14,14],[16,19],[20,22],[23,25],[30,34],[41,43],[45,49]]
newInterval = [29,32]
expected = [[3,6],[9,9],[11,13],[14,14],[16,19],[20,22],[23,25],[29,34],[41,43],[45,49]]
s = Solution()
result = s.insert(intervals, newInterval)
print(result)
print(result == expected)


import sys
import traceback
import os.path
import time
import webbrowser

from PyQt5 import QtWidgets, QtCore, QtGui

import toolbox.utils as ut
import acqGUI
from th260 import th260controller, th260sorter

# put here visual ressources
ICON_OK = ":/icons/ok.png"
ICON_WARNING = ":/icons/warning.png"
VERSION = '1.0'

class MainWindow(QtWidgets.QMainWindow, acqGUI.Ui_MainWindow):
    """ Main instance for the GUI of the hv controller panel """

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)
        self.T2applySetBtn.setEnabled(False)
        self.T2startBtn.setEnabled(False)
        self.settings = QtCore.QSettings('Aalto-Antimatter', 'Pals3D')
        self.warnings = 'No warnings'

        self.th260 = th260controller.TH260Controller()
        self.sortingWorker = th260sorter.SortingWorker()
        self.sortingWorker.COINCRATE.connect(self.updateCoincRates)
        self.sortingThread = QtCore.QThread()
        self.sortingWorker.moveToThread(self.sortingThread)

        self.threadpool = QtCore.QThreadPool()

        # timer for picking counting rates when no acquisition running
        self.countRatesTimer = QtCore.QTimer()
        self.countRatesTimer.setInterval(100)
        self.countRatesTimer.timeout.connect(self.th260.getCountRates)
        self.countRatesTimer.timeout.connect(self.updateCountRates)

        # disconnect the default slots defined in TH260Controller
        self.th260.NEW_OUTPUT.disconnect()
        self.th260.WARNING.disconnect()
#        self.th260.PROGRESS.disconnect()
#        self.th260.DATA.disconnect()
#        self.th260.ACQ_ENDED.disconnect()
#        self.th260.DEVINIT.disconnect()
#        self.th260.UPDATECountRate.disconnect()
#        self.th260.ERROR.disconnect()

        # connecting signals to new slots:
        self.th260.NEW_OUTPUT.connect(self.printOutput)
        self.th260.WARNING.connect(self.updateWarning)
        self.th260.PROGRESS.connect(self.updateProgress)
        self.th260.DATA.connect(self.sortingWorker.sortBuffer,
                                type=QtCore.Qt.QueuedConnection)
        self.th260.ACQ_ENDED.connect(self.sortingWorker.processLastEvents,
                                     type=QtCore.Qt.QueuedConnection)
        self.th260.DEVINIT.connect(self.devInit)
        self.th260.UPDATECountRate.connect(self.updateCountRates)
#        self.th260.ERROR.connect()

        self.sortingWorker.NEW_OUTPUT.connect(self.printOutput)

        self.th260.searchDevices()
        self.setupWidgetLimits()

        self.T2settingDict = {}
        self.restaureSettings()
        self.fetchSettings("T2")

        self.initWk = TH260Thread(self.th260.initialization)
        self.statusbar.showMessage('Initialization of the device ...')
        self.threadpool.start(self.initWk)

        self.warningBtn = QtWidgets.QPushButton()
        self.warningBtn.setText('')
        self.warningBtn.setMaximumSize(100, 100)
        self.iconOk = QtGui.QIcon()
        self.iconOk.addPixmap(QtGui.QPixmap(ICON_OK))
        self.iconWarn = QtGui.QIcon()
        self.iconWarn.addPixmap(QtGui.QPixmap(ICON_WARNING))
        self.warningBtn.setIcon(self.iconOk)
        self.warningBtn.clicked.connect(self.on_warningBtn_clicked)

        self.statusbar.addPermanentWidget(self.warningBtn)

    # ------ Slots and GUI logic ------#
    @QtCore.pyqtSlot()
    def devInit(self):
        self.countRatesTimer.start()
        self.statusbar.clearMessage()
        self.statusbar.showMessage('Device initialized, ready to use', 30000)
        self.T2applySetBtn.setEnabled(True)
        self.T2startBtn.setEnabled(True)

    @QtCore.pyqtSlot(str)
    def updateWarning(self, text):
        self.warnings = text
        if text == 'No warning':
            self.warningBtn.setIcon(self.iconOk)
        else:
            self.warningBtn.setIcon(self.iconWarn)

    @QtCore.pyqtSlot()
    def updateCountRates(self):
        """Update the display of the count rate widgets"""
        self.rateSyncValue.display(self.th260.countRates[0])
        self.rateChn1Value.display(self.th260.countRates[1])
        self.rateChn2Value.display(self.th260.countRates[2])
        self.rateTotalValue.display(self.th260.countRates[3])

    @QtCore.pyqtSlot(int)
    def updateCoincRates(self, count):
        """Update the display of the coincidence count widgets"""
        if self.sortingWorker.sortingType == "2C":
            self.rateDoubleValue.display(self.rateDoubleValue.value() + count)
        else:
            self.rateTripleValue.display(self.rateTripleValue.value() + count)

    @QtCore.pyqtSlot(str, int)
    def updateProgress(self, mode, prog):
        """
        Update the display of the acquisition progress bars

        Parameters:
        -----------
        mode : str
            'file' or 'acq'
        prog : int
            for mode = 'file' time elapsed since the acquisition starts

            for mode = 'acq' number of file recorded up to now
        """
        if mode == "file":
            self.progFileTime = prog
            progRatio = prog*100/self.th260.tacq
            self.acqProgFileBar.setValue(progRatio)
        if mode == "acq":
            self.progAcqNumber = prog
            progRatio = prog*100/self.acqNoFiles
            self.acqProgBar.setValue(progRatio)
        self.progStatus.setText('Progress: {:.1f}/{:.0f}min'
                                ' of the file no {}/{}'
                                .format(self.progFileTime/60000,
                                        self.th260.tacq/60000,
                                        self.progAcqNumber,
                                        self.acqNoFiles))

    @QtCore.pyqtSlot()
    def on_warningBtn_clicked(self):
        self.showWarning(self.warnings)

    @QtCore.pyqtSlot()
    def on_T2filenameBtn_clicked(self):
        """Open a file dialog to select a destination file"""
        self.T2filename, _ = QtWidgets.QFileDialog.getSaveFileName(
                self, caption="choose a file",
                directory=self.T2defaultFileDir,
                filter="""Histogram files (*.hst);;Numpy files(*.npy);;
                          All files (*.*)""")
        self.T2filenameValue.setText(self.T2filename)

    @QtCore.pyqtSlot(int)
    def on_T2chn1Chk_stateChanged(self, state):
        """Enable/disable corresponding widgets when clicked"""
        pass
        # Remove the pass statement above and uncomment the following
        # lines when the code has been modified to handle the case when
        # only one of the two channels is used in the acquisition
#        if state == 0:   # Unchecked
#            ut.disableChildOf(self.T2chn1Frame)
#        if state == 2:  # Checked
#            ut.enableChildOf(self.T2chn1Frame)

    @QtCore.pyqtSlot(int)
    def on_T2chn2Chk_stateChanged(self, state):
        """Enable/disable corresponding widgets when clicked"""
        pass
        # Remove the pass statement above and uncomment the following
        # lines when the code has been modified to handle the case when
        # only one of the two channels is used in the acquisition
#        if state == 0:   # Unchecked
#            ut.disableChildOf(self.T2chn2Frame)
#        if state == 2:  # Checked
#            ut.enableChildOf(self.T2chn2Frame)

    @QtCore.pyqtSlot(bool)
    def on_T2modeDouble_toggled(self, checked):
        """Enable/disable the time resolution gate"""
        if checked:
            self.T2timeGateShortValue.setEnabled(False)
        else:
            self.T2timeGateShortValue.setEnabled(True)

    @QtCore.pyqtSlot()
    def on_T2saveDefaultPrmBtn_clicked(self):
        """Save the CFD settings as default"""
        self.saveSettings()

    @QtCore.pyqtSlot()
    def on_T2applySetBtn_clicked(self):
        """Fetch and apply the CFD settings"""
        self.fetchSettings("T2")
        self.printOutput(
                """Measurement settings:\n
                Sync CFDZeroCross  : {syncCFDZero}
                Sync CFDLevel      : {syncCFDLevel}
                Sync Offset        : {syncOffset}
                Chn1 CFDZeroCross  : {chn1CFDZero}
                Chn1 CFDLevel      : {chn1CFDLevel}
                Chn1 Offset        : {chn1Offset}
                Chn2 CFDZeroCross  : {chn2CFDZero}
                Chn2 CFDLevel      : {chn2CFDLevel}
                Chn2 Offset        : {chn2Offset}
                """.format(syncCFDZero=self.th260.syncCFDZeroCross,
                           syncCFDLevel=self.th260.syncCFDLevel,
                           syncOffset=self.th260.syncOffset,
                           chn1CFDZero=self.th260.inputCFDZeroCross[0],
                           chn1CFDLevel=self.th260.inputCFDLevel[0],
                           chn1Offset=self.th260.inputOffset[0],
                           chn2CFDZero=self.th260.inputCFDZeroCross[1],
                           chn2CFDLevel=self.th260.inputCFDLevel[1],
                           chn2Offset=self.th260.inputOffset[1]))
        self.th260.configureSetting()

    @QtCore.pyqtSlot()
    def on_T2startBtn_clicked(self):
        """Fetch settings for CFD and acquisition, then start measurement"""
        self.T2filename = self.T2filenameValue.text()
        try:
            self.checkExistingFilename(self.T2filename)
        except ValueError:
            return
        self.fetchSettings("T2")
        self.printOutput(
                """Measurement settings:\n
                Sync CFDZeroCross  : {syncCFDZero}
                Sync CFDLevel      : {syncCFDLevel}
                Sync Offset        : {syncOffset}
                Chn1 CFDZeroCross  : {chn1CFDZero}
                Chn1 CFDLevel      : {chn1CFDLevel}
                Chn1 Offset        : {chn1Offset}
                Chn2 CFDZeroCross  : {chn2CFDZero}
                Chn2 CFDLevel      : {chn2CFDLevel}
                Chn2 Offset        : {chn2Offset}
                """.format(syncCFDZero=self.th260.syncCFDZeroCross,
                           syncCFDLevel=self.th260.syncCFDLevel,
                           syncOffset=self.th260.syncOffset,
                           chn1CFDZero=self.th260.inputCFDZeroCross[0],
                           chn1CFDLevel=self.th260.inputCFDLevel[0],
                           chn1Offset=self.th260.inputOffset[0],
                           chn2CFDZero=self.th260.inputCFDZeroCross[1],
                           chn2CFDLevel=self.th260.inputCFDLevel[1],
                           chn2Offset=self.th260.inputOffset[1]))

        self.fetchAcqSettings("T2")
        self.printOutput(
            """Measurement settings:\n
            Acquisition time per file   : {acqTime} sec
            Number of files             : {noFiles}
            Time gate total             : {timeGate} ps
            Time gate (511 keV)         : {timeGate511} ps
            """.format(acqTime=self.th260.tacq,
                       noFiles=self.acqNoFiles,
                       timeGate=self.timeGate,
                       timeGate511=self.timeGate511
                       if self.T2modeTriple.isChecked()
                       else "None (2C mode)"))

        answer = self.showQuestion(""" Acquisition is about to start with:
                            Acquisition time per file   : {acqTime} sec
                            Number of files             : {noFiles}
                            Time gate total             : {timeGate} ps
                            Time gate (511 keV)         : {timeGate511} ps
                            """.format(acqTime=self.th260.tacq,
                                       noFiles=self.acqNoFiles,
                                       timeGate=self.timeGate,
                                       timeGate511=self.timeGate511
                                       if self.T2modeTriple.isChecked()
                                       else "None (2C mode)"))

        if answer == QtWidgets.QMessageBox.Ok:
            self.startAcquisition("T2")
            pass
        elif answer == QtWidgets.QMessageBox.Cancel:
            return

    @QtCore.pyqtSlot()
    def startAcquisition(self, mode):
        """Configure some variable of the TH260 controller and start"""
        # TODO: check param input
        self.saveAcqSettings()
        self.th260.countRates[3] = 0
        self.acqProgBar.setValue(0)
        self.acqProgFileBar.setValue(0)
        self.rateDoubleValue.display(0)
        self.rateTripleValue.display(0)
        self.progFileTime = 0
        self.progAcqNumber = 0
        if mode == "T2":
            self.th260.configureSetting()
            # sorting worker:
            self.sortingWorker.kwargs["nftot"] = self.acqNoFiles
            self.sortingWorker.kwargs["CFDset"] = self.T2settingDict
            self.sortingWorker.kwargs["acqTime"] = self.th260.tacq/60000
            self.sortingWorker.kwargs["filename"] = self.T2filename
            self.sortingWorker.kwargs["sortingType"] = "3C"\
                if self.T2modeTriple.isChecked()\
                else "2C"
            self.sortingWorker.kwargs["timeGate"] = self.timeGate
            if self.T2modeTriple.isChecked():
                self.sortingWorker.kwargs["timeRes"] = self.timeGate511
            else:
                self.sortingWorker.kwargs["timeRes"] = None

            # threads:
            self.acqThread = T2AcquisitionThread(self.th260, self.acqNoFiles)
            self.acqThread.globProgress.connect(self.updateProgress)
            self.acqThread.newMeas.connect(self.sortingWorker.newMeasurement)
            self.acqThread.fileDone.connect(self.sortingWorker.saveData)
            self.acqThread.measDone.connect(self.measEnded)

            self.sortingThread.start()
            self.countRatesTimer.stop()
            self.acqThread.start()

            self.statusbar.showMessage('Measurement running ...')
            self.progStatus = QtWidgets.QLabel('Progress: {}min/X'
                                               ' of the file no{}/N'
                                               .format(self.progFileTime,
                                                       self.progAcqNumber))
            self.statusbar.insertPermanentWidget(0, self.progStatus)
            ut.disableChildOf(self.T2acqGrp, self.T2stopBtn)
            ut.disableChildOf(self.T2settingsGrp)

    @QtCore.pyqtSlot()
    def measEnded(self):
        self.statusbar.removeWidget(self.progStatus)
        self.statusbar.showMessage("The measurement has ended normally",
                                   30000)
        ut.enableChildOf(self.T2acqGrp)
        ut.enableChildOf(self.T2settingsGrp)
        self.countRatesTimer.start()

    @QtCore.pyqtSlot()
    def on_T2stopBtn_clicked(self):
        """Stop the TTTR measurement and enable acq/settings widgets"""
        try:
            self.acqThread.requestInterruption()
        except AttributeError:
            self.showError("No thread to stop")
        else:
            self.th260.stoptttr()
            time.sleep(0.1)
            self.statusbar.removeWidget(self.progStatus)
            self.statusbar.showMessage('Last measurement stopped at {}'
                                       ' min of the file no {}'
                                       .format(self.progFileTime/60000,
                                               self.progAcqNumber+1))
        ut.enableChildOf(self.T2acqGrp)
        ut.enableChildOf(self.T2settingsGrp)
        self.countRatesTimer.start()

    @QtCore.pyqtSlot()
    def on_actionExit_triggered(self):
        self.th260.closeDevices()
        self.close()

    @QtCore.pyqtSlot()
    def on_actionDLL_version_triggered(self):
        self.showMessage("Currently installed library version: {}\n"
                         "\nPals3D has been developped using version: {}"
                         .format(self.th260.libVersion.value.decode("utf-8"),
                                 self.th260.LIB_VERSION))

    @QtCore.pyqtSlot()
    def on_actionPals3D_version_triggered(self):
        self.showMessage("Current version of Pals3D: {}\n"
                         .format(VERSION))

    @QtCore.pyqtSlot()
    def on_actionAbout_triggered(self):
        self.showMessage("Pals3D  Copyright (C) 2018-2019 "
                         "Aurélie Vancraeyenest"
                         "\nThis program comes with ABSOLUTELY NO WARRANTY."
                         "\nThis is free software, and you are welcome to "
                         "redistribute it under certain conditions. "
                         "See LICENSE file for details.")

    @QtCore.pyqtSlot()
    def on_actionPalss3D_help_triggered(self):
        webbrowser.open('http://pals3d.readthedocs.io/')

    def checkExistingFilename(self, filename):
        try:
            filebase, extension = filename.rsplit(sep=".", maxsplit=1)
        except ValueError:
            filebase = filename

        outputFileName = "".join((filebase,
                                  '_000.hst'))
        if os.path.isfile(outputFileName):
            answer = self.showQuestion("Output file {} will be overwritten!\n"
                                       "Do you want to continue?"
                                       .format(outputFileName))
            if answer == QtWidgets.QMessageBox.Cancel:
                raise ValueError()

    def fetchAcqSettings(self, mode):
        """Get the acquisition settings from the GUI widgets"""
        if mode == "T2":
            self.th260.tacq = self.T2acqTimePerFileValue.value()*60000
            self.acqNoFiles = self.T2acqNoFilesValue.value()
            self.timeGate = self.T2timeGateLongValue.value()
            if self.T2modeTriple.isChecked():
                self.timeGate511 = self.T2timeGateShortValue.value()
            else:
                self.timeGate511 = None

    def fetchSettings(self, mode):
        """Get the CFD settings for each channel from the GUI widgets"""
        if mode == "T2":
            self.th260.syncCFDLevel = self.T2syncLevelValue.value()
            self.th260.syncCFDZeroCross = self.T2syncZeroValue.value()
            self.th260.syncOffset = self.T2syncOffsetValue.value()
            self.th260.inputCFDLevel[0] = self.T2chn1LevelValue.value()
            self.th260.inputCFDLevel[1] = self.T2chn2LevelValue.value()
            self.th260.inputCFDZeroCross[0] = self.T2chn1ZeroValue.value()
            self.th260.inputCFDZeroCross[1] = self.T2chn2ZeroValue.value()
            self.th260.inputOffset[0] = self.T2chn1OffsetValue.value()
            self.th260.inputOffset[1] = self.T2chn2OffsetValue.value()
            self.T2settingDict['lev0'] = self.th260.syncCFDLevel
            self.T2settingDict['zero0'] = self.th260.syncCFDZeroCross
            self.T2settingDict['off0'] = self.th260.syncOffset
            self.T2settingDict['lev1'] = self.th260.inputCFDLevel[0]
            self.T2settingDict['zero1'] = self.th260.inputCFDZeroCross[0]
            self.T2settingDict['off1'] = self.th260.inputOffset[0]
            self.T2settingDict['lev2'] = self.th260.inputCFDLevel[1]
            self.T2settingDict['zero2'] = self.th260.inputCFDZeroCross[1]
            self.T2settingDict['off2'] = self.th260.inputOffset[1]

    def saveSettings(self):
        """Store the currents CFD settings as default values"""
        self.settings.setValue('T2chn1LevelValue',
                               self.T2chn1LevelValue.value())
        self.settings.setValue('T2chn1OffsetValue',
                               self.T2chn1OffsetValue.value())
        self.settings.setValue('T2chn1ZeroValue',
                               self.T2chn1ZeroValue.value())

        self.settings.setValue('T2chn2LevelValue',
                               self.T2chn2LevelValue.value())
        self.settings.setValue('T2chn2OffsetValue',
                               self.T2chn2OffsetValue.value())
        self.settings.setValue('T2chn2ZeroValue',
                               self.T2chn2ZeroValue.value())

        self.settings.setValue('T2syncLevelValue',
                               self.T2syncLevelValue.value())
        self.settings.setValue('T2syncOffsetValue',
                               self.T2syncOffsetValue.value())
        self.settings.setValue('T2syncZeroValue',
                               self.T2syncZeroValue.value())

    def saveAcqSettings(self):
        """Store the currents acquisition settings as default values"""
        self.settings.setValue('T2modeDouble',
                               self.T2modeDouble.isChecked())
        self.settings.setValue('T2acqTimePerFileValue',
                               self.T2acqTimePerFileValue.value())
        self.settings.setValue('T2acqNoFilesValue',
                               self.T2acqNoFilesValue.value())
        self.settings.setValue('T2timeGateLongValue',
                               self.T2timeGateLongValue.value())
        self.settings.setValue('T2timeGateShortValue',
                               self.T2timeGateShortValue.value())
        self.settings.setValue('T2filePath',
                               os.path.dirname(self.T2filename))
        self.settings.setValue('T2filename', self.T2filename)

    def restaureSettings(self):
        """Load the default values for CFD and acquisition settings"""
        self.T2chn1LevelValue.setValue(
                self.settings.value('T2chn1LevelValue', type=int))
        self.T2chn1OffsetValue.setValue(
                self.settings.value('T2chn1OffsetValue', type=int))
        self.T2chn1ZeroValue.setValue(
                self.settings.value('T2chn1ZeroValue', type=int))

        self.T2chn2LevelValue.setValue(
                self.settings.value('T2chn2LevelValue', type=int))
        self.T2chn2OffsetValue.setValue(
                self.settings.value('T2chn2OffsetValue', type=int))
        self.T2chn2ZeroValue.setValue(
                self.settings.value('T2chn2ZeroValue', type=int))

        self.T2syncLevelValue.setValue(
                self.settings.value('T2syncLevelValue', type=int))
        self.T2syncOffsetValue.setValue(
                self.settings.value('T2syncOffsetValue', type=int))
        self.T2syncZeroValue.setValue(
                self.settings.value('T2syncZeroValue', type=int))

        self.T2modeDouble.setChecked(
                self.settings.value('T2modeDouble', type=bool))

        self.T2acqTimePerFileValue.setValue(
                self.settings.value('T2acqTimePerFileValue', type=int))
        self.T2acqNoFilesValue.setValue(
                self.settings.value('T2acqNoFilesValue', type=int))

        self.T2timeGateLongValue.setValue(
                self.settings.value('T2timeGateLongValue', type=int))
        self.T2timeGateShortValue.setValue(
                self.settings.value('T2timeGateShortValue', type=int))

        self.T2filename = self.settings.value('T2filename', type=str)
        self.T2filenameValue.setText(self.T2filename)
        self.T2defaultFileDir = self.settings.value('T2filePath', type=str)

    def setupWidgetLimits(self):
        """Set the hardware limits to the corresponding widgets"""

        self.T2chn1LevelValue.setMaximum(self.th260.CFDLVLMAX)
        self.T2chn1LevelValue.setMinimum(self.th260.CFDLVLMIN)
        self.T2chn2LevelValue.setMaximum(self.th260.CFDLVLMAX)
        self.T2chn2LevelValue.setMinimum(self.th260.CFDLVLMIN)
        self.T2syncLevelValue.setMaximum(self.th260.CFDLVLMAX)
        self.T2syncLevelValue.setMinimum(self.th260.CFDLVLMIN)
        self.T2chn1OffsetValue.setMaximum(self.th260.CHANOFFSMAX)
        self.T2chn1OffsetValue.setMinimum(self.th260.CHANOFFSMIN)
        self.T2chn2OffsetValue.setMaximum(self.th260.CHANOFFSMAX)
        self.T2chn2OffsetValue.setMinimum(self.th260.CHANOFFSMIN)
        self.T2syncOffsetValue.setMaximum(self.th260.CHANOFFSMAX)
        self.T2syncOffsetValue.setMinimum(self.th260.CHANOFFSMIN)
        self.T2chn1ZeroValue.setMaximum(self.th260.CFDZCMAX)
        self.T2chn1ZeroValue.setMinimum(self.th260.CFDZCMIN)
        self.T2chn2ZeroValue.setMaximum(self.th260.CFDZCMAX)
        self.T2chn2ZeroValue.setMinimum(self.th260.CFDZCMIN)
        self.T2syncZeroValue.setMaximum(self.th260.CFDZCMAX)
        self.T2syncZeroValue.setMinimum(self.th260.CFDZCMIN)
        self.T2acqTimePerFileValue.setMaximum(self.th260.ACQTMAX/1000)  # in s
        self.T2acqTimePerFileValue.setMinimum(self.th260.ACQTMIN)  # always 1
        self.T2acqNoFilesValue.setMaximum(6000)
        self.T2acqNoFilesValue.setMinimum(0)
        self.T2timeGateLongValue.setMaximum(20000)      # in ps
        self.T2timeGateLongValue.setMinimum(0)          # in ps
        self.T2timeGateShortValue.setMaximum(20000)     # in ps
        self.T2timeGateShortValue.setMinimum(0)         # in ps

    def printOutput(self, text):
        """
        Append command outputs to the text box of the GUI

        Parameters:
        -----------
        text : 'str'
            Message to be printed in the command output widget
        """
        self.commandOutput.appendPlainText(text)

    def showMessage(self, message):
        """
        Display "message" in a "Information" message box with 'OK' button.

        Parameters:
        -----------
        message : 'str'
            Message to be printed in the command output widget
        """

        messageBox = QtWidgets.QMessageBox()
        messageBox.setText(message)
        messageBox.setWindowTitle("Info")
        messageBox.setIcon(QtWidgets.QMessageBox.Information)
        messageBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
        messageBox.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)

        # Show the window
        messageBox.raise_()
        messageBox.exec_()

    def showError(self, message):
        """
        Display "message" in a "Critical error" message box with 'OK' button.

        Parameters:
        -----------
        message : 'str'
            Message to be printed in the command output widget
        """

        messageBox = QtWidgets.QMessageBox()
        messageBox.setText(message)
        messageBox.setWindowTitle("Error")
        messageBox.setIcon(QtWidgets.QMessageBox.Critical)
        messageBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
        messageBox.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)

        # Show the window
        messageBox.raise_()
        messageBox.exec_()

    def showWarning(self, message):
        """
        Display "message" in a "Warning" message box with 'OK' button.

        Parameters:
        -----------
        message : 'str'
            Message to be printed in the command output widget
        """

        messageBox = QtWidgets.QMessageBox()
        messageBox.setText(message)
        messageBox.setWindowTitle("Warning")
        messageBox.setWindowIcon(QtGui.QIcon(QtGui.QPixmap(ICON_WARNING)))
        messageBox.setIcon(QtWidgets.QMessageBox.Warning)
        messageBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
        messageBox.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)

        # Show the window
        messageBox.raise_()
        messageBox.exec_()

    def showQuestion(self, message):
        """
        Display "message" in a "Warning question" message box with
        'OK' and 'Cancel' button

        Parameters:
        -----------
        message : 'str'
            Message to be printed in the command output widget
        """
        self.messageBox = QtWidgets.QMessageBox()
        self.messageBox.setText(message)
        self.messageBox.setWindowTitle("Question")
        self.messageBox.setWindowIcon(QtGui.QIcon(QtGui.QPixmap(ICON_WARNING)))
        self.messageBox.setIcon(QtWidgets.QMessageBox.Warning)
        self.messageBox.setStandardButtons(QtWidgets.QMessageBox.Cancel
                                           | QtWidgets.QMessageBox.Ok)
        self.messageBox.setTextInteractionFlags(
                                            QtCore.Qt.TextSelectableByMouse)

        # Show the window
        self.messageBox.raise_()
        answer = self.messageBox.exec_()
        return answer

# ---------- Thread workers ---------- #
class TH260Thread(QtCore.QRunnable):
    """
    Generic QRunnable that takes a function and its args/kwargs
    as arguments to run it in a threadpool
    """
    def __init__(self, fn, *args, **kwargs):
        """Constructor of the TH260Thread """
        super(TH260Thread, self).__init__()
        self.fn = fn
        self.args = args
        self.kwargs = kwargs

    @QtCore.pyqtSlot()
    def run(self):
        try:
            self.fn(*self.args, **self.kwargs)
        except Exception:
            traceback.print_exc()
            exctype, value = sys.exc_info()[:2]
        else:
            pass

class T2AcquisitionThread(QtCore.QThread):
    """
    Write docstring here

    Supported signals:
    ------------------
    globProgress : str, int
        Emitted at the end of each single acquisition
    fileDone : int
        Emitted when the nth acquisition has ended. Sent with n as
        argument
    newMeas : int
        Emmitted while starting a new measurment with as argument the
        numero of the starting acquisition
    measDone : None
        Not in use
    """
    globProgress = QtCore.pyqtSignal(str, int)
    fileDone = QtCore.pyqtSignal(int)
    newMeas = QtCore.pyqtSignal(int)
    measDone = QtCore.pyqtSignal()

    def __init__(self,  dev, noFiles):
        super(T2AcquisitionThread, self).__init__()
        self.th260device = dev
        self.noFiles = noFiles
        self.abort = False

    def run(self):
        for nof in range(self.noFiles):
            if self.isInterruptionRequested():
                return
            self.newMeas.emit(nof)
            self.th260device.startAcquisition()
            self.fileDone.emit(nof)
            self.globProgress.emit("acq", nof+1)
        self.measDone.emit()

        self.exec_()
        self.__init__(self.th260device, self.noFiles)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    form = MainWindow()
    form.show()
    app.exec()