#!/usr/bin/env python
# coding: utf-8

from __future__ import (unicode_literals, division, absolute_import,
                        print_function)

import hashlib, importlib, os, sys
from datetime import datetime
from functools import partial
from time import mktime

from calibre.constants import islinux, isosx, iswindows
from calibre.devices.usbms.driver import debug_print
from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.gui2.dialogs.message_box import MessageBox
from calibre.gui2 import show_restart_warning
from calibre.gui2.ui import get_gui
from calibre.utils.config import JSONConfig

from calibre_plugins.marvin_manager.appearance import (AnnotationsAppearance,
    default_elements, default_timestamp)

from calibre_plugins.marvin_manager.common_utils import (Logger,
    existing_annotations, get_cc_mapping, get_icon, move_annotations, set_cc_mapping)

try:
    from PyQt5.Qt import (Qt, QCheckBox, QComboBox, QFont, QFontMetrics, QFrame,
                          QGridLayout, QGroupBox, QFileDialog, QIcon,
                          QLabel, QLineEdit, QPlainTextEdit, QPushButton,
                          QSizePolicy, QSpacerItem, QThread, QTimer, QToolButton,
                          QVBoxLayout, QWidget,
                          pyqtSignal)
except ImportError:
    from PyQt4.Qt import (Qt, QCheckBox, QComboBox, QFont, QFontMetrics, QFrame,
                          QGridLayout, QGroupBox, QFileDialog, QIcon,
                          QLabel, QLineEdit, QPlainTextEdit, QPushButton,
                          QSizePolicy, QSpacerItem, QThread, QTimer, QToolButton,
                          QVBoxLayout, QWidget,
                          pyqtSignal)

plugin_prefs = JSONConfig('plugins/Marvin XD')

class ConfigWidget(QWidget, Logger):
    '''
    Config dialog for Marvin Manager
    '''

    WIZARD_PROFILES = {
        'Annotations': {
            'label': 'mm_annotations',
            'datatype': 'comments',
            'display': {},
            'is_multiple': False
        },
        'Collections': {
            'label': 'mm_collections',
            'datatype': 'text',
            'display': {u'is_names': False},
            'is_multiple': True
        },
        'Last read': {
            'label': 'mm_date_read',
            'datatype': 'datetime',
            'display': {},
            'is_multiple': False
        },
        'Locked': {
            'label': 'mm_locked',
            'datatype': 'bool',
            'display': {},
            'is_multiple': False
        },
        'Progress': {
            'label': 'mm_progress',
            'datatype': 'float',
            'display': {u'number_format': u'{0:.0f}%'},
            'is_multiple': False
        },
        'Read': {
            'label': 'mm_read',
            'datatype': 'bool',
            'display': {},
            'is_multiple': False
        },
        'Reading list': {
            'label': 'mm_reading_list',
            'datatype': 'bool',
            'display': {},
            'is_multiple': False
        },
        'Word count': {
            'label': 'mm_word_count',
            'datatype': 'int',
            'display': {u'number_format': u'{0:n}'},
            'is_multiple': False
        }
    }

    def __init__(self, plugin_action):
        QWidget.__init__(self)
        self.parent = plugin_action

        self.gui = get_gui()
        self.icon = plugin_action.icon
        self.opts = plugin_action.opts
        self.prefs = plugin_prefs
        self.resources_path = plugin_action.resources_path
        self.verbose = plugin_action.verbose

        self.restart_required = False

        self._log_location()

        self.l = QGridLayout()
        self.setLayout(self.l)
        self.column1_layout = QVBoxLayout()
        self.l.addLayout(self.column1_layout, 0, 0)
        self.column2_layout = QVBoxLayout()
        self.l.addLayout(self.column2_layout, 0, 1)

        # ----------------------------- Column 1 -----------------------------
        # ~~~~~~~~ Create the Custom fields options group box ~~~~~~~~
        self.cfg_custom_fields_gb = QGroupBox(self)
        self.cfg_custom_fields_gb.setTitle('Custom column assignments')
        self.column1_layout.addWidget(self.cfg_custom_fields_gb)

        self.cfg_custom_fields_qgl = QGridLayout(self.cfg_custom_fields_gb)
        current_row = 0

        # ++++++++ Labels + HLine ++++++++
        self.marvin_source_label = QLabel("Marvin source")
        self.cfg_custom_fields_qgl.addWidget(self.marvin_source_label, current_row, 0)
        self.calibre_destination_label = QLabel("calibre destination")
        self.cfg_custom_fields_qgl.addWidget(self.calibre_destination_label, current_row, 1)
        current_row += 1
        self.sd_hl = QFrame(self.cfg_custom_fields_gb)
        self.sd_hl.setFrameShape(QFrame.HLine)
        self.sd_hl.setFrameShadow(QFrame.Raised)
        self.cfg_custom_fields_qgl.addWidget(self.sd_hl, current_row, 0, 1, 3)
        current_row += 1

        # ++++++++ Annotations ++++++++
        self.cfg_annotations_label = QLabel('Annotations')
        self.cfg_annotations_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_annotations_label, current_row, 0)

        self.annotations_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.annotations_field_comboBox.setObjectName('annotations_field_comboBox')
        self.annotations_field_comboBox.setToolTip('Select a custom column to store Marvin annotations')
        self.cfg_custom_fields_qgl.addWidget(self.annotations_field_comboBox, current_row, 1)

        self.cfg_highlights_wizard = QToolButton()
        self.cfg_highlights_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_highlights_wizard.setToolTip("Create a custom column to store Marvin annotations")
        self.cfg_highlights_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Annotations'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_highlights_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Collections ++++++++
        self.cfg_collections_label = QLabel('Collections')
        self.cfg_collections_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_label, current_row, 0)

        self.collection_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.collection_field_comboBox.setObjectName('collection_field_comboBox')
        self.collection_field_comboBox.setToolTip('Select a custom column to store Marvin collection assignments')
        self.cfg_custom_fields_qgl.addWidget(self.collection_field_comboBox, current_row, 1)

        self.cfg_collections_wizard = QToolButton()
        self.cfg_collections_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_collections_wizard.setToolTip("Create a custom column for Marvin collection assignments")
        self.cfg_collections_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Collections'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Last read ++++++++
        self.cfg_date_read_label = QLabel("Last read")
        self.cfg_date_read_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_date_read_label, current_row, 0)

        self.date_read_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.date_read_field_comboBox.setObjectName('date_read_field_comboBox')
        self.date_read_field_comboBox.setToolTip('Select a custom column to store Last read date')
        self.cfg_custom_fields_qgl.addWidget(self.date_read_field_comboBox, current_row, 1)

        self.cfg_collections_wizard = QToolButton()
        self.cfg_collections_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_collections_wizard.setToolTip("Create a custom column to store Last read date")
        self.cfg_collections_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Last read'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_collections_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Locked ++++++++
        self.cfg_locked_label = QLabel("Locked")
        self.cfg_locked_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_locked_label, current_row, 0)

        self.locked_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.locked_field_comboBox.setObjectName('locked_field_comboBox')
        self.locked_field_comboBox.setToolTip('Select a custom column to store Locked status')
        self.cfg_custom_fields_qgl.addWidget(self.locked_field_comboBox, current_row, 1)

        self.cfg_locked_wizard = QToolButton()
        self.cfg_locked_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_locked_wizard.setToolTip("Create a custom column to store Locked status")
        self.cfg_locked_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Locked'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_locked_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Progress ++++++++
        self.cfg_progress_label = QLabel('Progress')
        self.cfg_progress_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_progress_label, current_row, 0)

        self.progress_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.progress_field_comboBox.setObjectName('progress_field_comboBox')
        self.progress_field_comboBox.setToolTip('Select a custom column to store Marvin reading progress')
        self.cfg_custom_fields_qgl.addWidget(self.progress_field_comboBox, current_row, 1)

        self.cfg_progress_wizard = QToolButton()
        self.cfg_progress_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_progress_wizard.setToolTip("Create a custom column to store Marvin reading progress")
        self.cfg_progress_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Progress'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_progress_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Read flag ++++++++
        self.cfg_read_label = QLabel('Read')
        self.cfg_read_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_read_label, current_row, 0)

        self.read_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.read_field_comboBox.setObjectName('read_field_comboBox')
        self.read_field_comboBox.setToolTip('Select a custom column to store Marvin Read status')
        self.cfg_custom_fields_qgl.addWidget(self.read_field_comboBox, current_row, 1)

        self.cfg_read_wizard = QToolButton()
        self.cfg_read_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_read_wizard.setToolTip("Create a custom column to store Marvin Read status")
        self.cfg_read_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Read'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_read_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Reading list flag ++++++++
        self.cfg_reading_list_label = QLabel('Reading list')
        self.cfg_reading_list_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_reading_list_label, current_row, 0)

        self.reading_list_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.reading_list_field_comboBox.setObjectName('reading_list_field_comboBox')
        self.reading_list_field_comboBox.setToolTip('Select a custom column to store Marvin Reading list status')
        self.cfg_custom_fields_qgl.addWidget(self.reading_list_field_comboBox, current_row, 1)

        self.cfg_reading_list_wizard = QToolButton()
        self.cfg_reading_list_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_reading_list_wizard.setToolTip("Create a custom column to store Marvin Reading list status")
        self.cfg_reading_list_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Reading list'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_reading_list_wizard, current_row, 2)
        current_row += 1

        # ++++++++ Word count ++++++++
        self.cfg_word_count_label = QLabel('Word count')
        self.cfg_word_count_label.setAlignment(Qt.AlignLeft)
        self.cfg_custom_fields_qgl.addWidget(self.cfg_word_count_label, current_row, 0)

        self.word_count_field_comboBox = QComboBox(self.cfg_custom_fields_gb)
        self.word_count_field_comboBox.setObjectName('word_count_field_comboBox')
        self.word_count_field_comboBox.setToolTip('Select a custom column to store Marvin word counts')
        self.cfg_custom_fields_qgl.addWidget(self.word_count_field_comboBox, current_row, 1)

        self.cfg_word_count_wizard = QToolButton()
        self.cfg_word_count_wizard.setIcon(QIcon(I('wizard.png')))
        self.cfg_word_count_wizard.setToolTip("Create a custom column to store Marvin word counts")
        self.cfg_word_count_wizard.clicked.connect(partial(self.launch_cc_wizard, 'Word count'))
        self.cfg_custom_fields_qgl.addWidget(self.cfg_word_count_wizard, current_row, 2)
        current_row += 1

        self.spacerItem1 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.column1_layout.addItem(self.spacerItem1)

        # ----------------------------- Column 2 -----------------------------
        # ~~~~~~~~ Create the CSS group box ~~~~~~~~
        self.cfg_css_options_gb = QGroupBox(self)
        self.cfg_css_options_gb.setTitle('CSS')
        self.column2_layout.addWidget(self.cfg_css_options_gb)
        self.cfg_css_options_qgl = QGridLayout(self.cfg_css_options_gb)

        current_row = 0

        # ++++++++ Annotations appearance ++++++++
        self.annotations_icon = QIcon(os.path.join(self.resources_path, 'icons', 'annotations_hiliter.png'))
        self.cfg_annotations_appearance_toolbutton = QToolButton()
        self.cfg_annotations_appearance_toolbutton.setIcon(self.annotations_icon)
        self.cfg_annotations_appearance_toolbutton.clicked.connect(self.configure_appearance)
        self.cfg_css_options_qgl.addWidget(self.cfg_annotations_appearance_toolbutton, current_row, 0)
        self.cfg_annotations_label = ClickableQLabel("Book notes, Bookmark notes and Annotations")
        self.cfg_annotations_label.clicked.connect(self.configure_appearance)
        self.cfg_css_options_qgl.addWidget(self.cfg_annotations_label, current_row, 1)
        current_row += 1

        # ++++++++ Injected CSS ++++++++
        self.css_editor_icon = QIcon(I('format-text-heading.png'))
        self.cfg_css_editor_toolbutton = QToolButton()
        self.cfg_css_editor_toolbutton.setIcon(self.css_editor_icon)
        self.cfg_css_editor_toolbutton.clicked.connect(self.edit_css)
        self.cfg_css_options_qgl.addWidget(self.cfg_css_editor_toolbutton, current_row, 0)
        self.cfg_css_editor_label = ClickableQLabel("Articles, Vocabulary")
        self.cfg_css_editor_label.clicked.connect(self.edit_css)
        self.cfg_css_options_qgl.addWidget(self.cfg_css_editor_label, current_row, 1)

        """
        # ~~~~~~~~ Create the Dropbox syncing group box ~~~~~~~~
        self.cfg_dropbox_syncing_gb = QGroupBox(self)
        self.cfg_dropbox_syncing_gb.setTitle('Dropbox')
        self.column2_layout.addWidget(self.cfg_dropbox_syncing_gb)
        self.cfg_dropbox_syncing_qgl = QGridLayout(self.cfg_dropbox_syncing_gb)
        current_row = 0

        # ++++++++ Syncing enabled checkbox ++++++++
        self.dropbox_syncing_checkbox = QCheckBox('Enable Dropbox updates')
        self.dropbox_syncing_checkbox.setObjectName('dropbox_syncing')
        self.dropbox_syncing_checkbox.setToolTip('Refresh custom column content from Marvin metadata')
        self.cfg_dropbox_syncing_qgl.addWidget(self.dropbox_syncing_checkbox,
            current_row, 0, 1, 3)
        current_row += 1

        # ++++++++ Dropbox folder picker ++++++++
        self.dropbox_folder_icon = QIcon(os.path.join(self.resources_path, 'icons', 'dropbox.png'))
        self.cfg_dropbox_folder_toolbutton = QToolButton()
        self.cfg_dropbox_folder_toolbutton.setIcon(self.dropbox_folder_icon)
        self.cfg_dropbox_folder_toolbutton.setToolTip("Specify Dropbox folder location on your computer")
        self.cfg_dropbox_folder_toolbutton.clicked.connect(self.select_dropbox_folder)
        self.cfg_dropbox_syncing_qgl.addWidget(self.cfg_dropbox_folder_toolbutton,
            current_row, 1)

        # ++++++++ Dropbox location lineedit ++++++++
        self.dropbox_location_lineedit = QLineEdit()
        self.dropbox_location_lineedit.setPlaceholderText("Dropbox folder location")
        self.cfg_dropbox_syncing_qgl.addWidget(self.dropbox_location_lineedit,
            current_row, 2)
        """

        # ~~~~~~~~ Create the General options group box ~~~~~~~~
        self.cfg_runtime_options_gb = QGroupBox(self)
        self.cfg_runtime_options_gb.setTitle('General options')
        self.column2_layout.addWidget(self.cfg_runtime_options_gb)
        self.cfg_runtime_options_qvl = QVBoxLayout(self.cfg_runtime_options_gb)

        # ++++++++ Temporary markers: Duplicates ++++++++
        self.duplicate_markers_checkbox = QCheckBox('Apply temporary markers to duplicate books')
        self.duplicate_markers_checkbox.setObjectName('apply_markers_to_duplicates')
        self.duplicate_markers_checkbox.setToolTip('Books with identical content will be flagged in the Library window')
        self.cfg_runtime_options_qvl.addWidget(self.duplicate_markers_checkbox)

        # ++++++++ Temporary markers: Updated ++++++++
        self.updated_markers_checkbox = QCheckBox('Apply temporary markers to books with updated content')
        self.updated_markers_checkbox.setObjectName('apply_markers_to_updated')
        self.updated_markers_checkbox.setToolTip('Books with updated content will be flagged in the Library window')
        self.cfg_runtime_options_qvl.addWidget(self.updated_markers_checkbox)

        # ++++++++ Auto refresh checkbox ++++++++
        self.auto_refresh_checkbox = QCheckBox('Automatically refresh custom column content')
        self.auto_refresh_checkbox.setObjectName('auto_refresh_at_startup')
        self.auto_refresh_checkbox.setToolTip('Update calibre custom column when Marvin XD is opened')
        self.cfg_runtime_options_qvl.addWidget(self.auto_refresh_checkbox)

        # ++++++++ Progress as percentage checkbox ++++++++
        self.reading_progress_checkbox = QCheckBox('Show reading progress as percentage')
        self.reading_progress_checkbox.setObjectName('show_progress_as_percentage')
        self.reading_progress_checkbox.setToolTip('Display percentage in Progress column')
        self.cfg_runtime_options_qvl.addWidget(self.reading_progress_checkbox)

        # ~~~~~~~~ Create the Debug options group box ~~~~~~~~
        self.cfg_debug_options_gb = QGroupBox(self)
        self.cfg_debug_options_gb.setTitle('Debug options')
        self.column2_layout.addWidget(self.cfg_debug_options_gb)
        self.cfg_debug_options_qvl = QVBoxLayout(self.cfg_debug_options_gb)

        # ++++++++ Debug logging checkboxes ++++++++
        self.debug_plugin_checkbox = QCheckBox('Enable debug logging for Marvin XD')
        self.debug_plugin_checkbox.setObjectName('debug_plugin_checkbox')
        self.debug_plugin_checkbox.setToolTip('Print plugin diagnostic messages to console')
        self.cfg_debug_options_qvl.addWidget(self.debug_plugin_checkbox)

        self.debug_libimobiledevice_checkbox = QCheckBox('Enable debug logging for libiMobileDevice')
        self.debug_libimobiledevice_checkbox.setObjectName('debug_libimobiledevice_checkbox')
        self.debug_libimobiledevice_checkbox.setToolTip('Print libiMobileDevice diagnostic messages to console')
        self.cfg_debug_options_qvl.addWidget(self.debug_libimobiledevice_checkbox)

        self.spacerItem2 = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.column2_layout.addItem(self.spacerItem2)

        # ~~~~~~~~ End of construction zone ~~~~~~~~
        self.resize(self.sizeHint())

        # ~~~~~~~~ Populate/restore config options ~~~~~~~~
        #  Annotations comboBox
        self.populate_annotations()
        self.populate_collections()
        self.populate_date_read()
        self.populate_locked()
        self.populate_progress()
        self.populate_read()
        self.populate_reading_list()
        self.populate_word_count()

        """
        # Restore Dropbox settings, hook changes
        dropbox_syncing = self.prefs.get('dropbox_syncing', False)
        self.dropbox_syncing_checkbox.setChecked(dropbox_syncing)
        self.set_dropbox_syncing(dropbox_syncing)
        self.dropbox_syncing_checkbox.clicked.connect(partial(self.set_dropbox_syncing))
        self.dropbox_location_lineedit.setText(self.prefs.get('dropbox_folder', ''))
        """

        # Restore general settings
        self.duplicate_markers_checkbox.setChecked(self.prefs.get('apply_markers_to_duplicates', True))
        self.updated_markers_checkbox.setChecked(self.prefs.get('apply_markers_to_updated', True))
        self.auto_refresh_checkbox.setChecked(self.prefs.get('auto_refresh_at_startup', False))
        self.reading_progress_checkbox.setChecked(self.prefs.get('show_progress_as_percentage', False))

        # Restore debug settings, hook changes
        self.debug_plugin_checkbox.setChecked(self.prefs.get('debug_plugin', False))
        self.debug_plugin_checkbox.stateChanged.connect(self.set_restart_required)
        self.debug_libimobiledevice_checkbox.setChecked(self.prefs.get('debug_libimobiledevice', False))
        self.debug_libimobiledevice_checkbox.stateChanged.connect(self.set_restart_required)

        # Hook changes to Annotations comboBox
#         self.annotations_field_comboBox.currentIndexChanged.connect(
#             partial(self.save_combobox_setting, 'annotations_field_comboBox'))
#        self.connect(self.annotations_field_comboBox,
#                     SIGNAL('currentIndexChanged(const QString &)'),
#                     self.annotations_destination_changed)
        self.annotations_field_comboBox.currentIndexChanged.connect(self.annotations_destination_changed)
        # Launch the annotated_books_scanner
        field = get_cc_mapping('annotations', 'field', None)
        self.annotated_books_scanner = InventoryAnnotatedBooks(self.gui, field)
        self.annotated_books_scanner.signal.connect(self.inventory_complete)
        QTimer.singleShot(1, self.start_inventory)

    def annotations_destination_changed(self, qs_new_destination_name):
        '''
        If the destination field changes, move all existing annotations from old to new
        '''
        self._log_location(str(qs_new_destination_name))
        self._log("self.eligible_annotations_fields: %s" % self.eligible_annotations_fields)

        old_destination_field = get_cc_mapping('annotations', 'field', None)
        old_destination_name = get_cc_mapping('annotations', 'combobox', None)

        self._log("old_destination_field: %s" % old_destination_field)
        self._log("old_destination_name: %s" % old_destination_name)

#        new_destination_name = unicode(qs_new_destination_name)
        # Signnls available have changed. Now receivin an indec rather than a name. Can get the name
        # from the combobox
        new_destination_name = unicode(self.annotations_field_comboBox.currentText())
        self._log("new_destination_name: %s" % repr(new_destination_name))

        if old_destination_name == new_destination_name:
            self._log_location("old_destination_name = new_destination_name, no changes")
            return

        if new_destination_name == '':
            self._log_location("annotations storage disabled")
            set_cc_mapping('annotations', field=None, combobox=new_destination_name)
            return

        new_destination_field = self.eligible_annotations_fields[new_destination_name]

        if existing_annotations(self.parent, old_destination_field):
            command = self.launch_new_destination_dialog(old_destination_name, new_destination_name)

            if command == 'move':
                set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name)

                if self.annotated_books_scanner.isRunning():
                    self.annotated_books_scanner.wait()
                move_annotations(self, self.annotated_books_scanner.annotation_map,
                    old_destination_field, new_destination_field)

            elif command == 'change':
                # Keep the updated destination field, but don't move annotations
                pass

            elif command == 'cancel':
                # Restore previous destination
                self.annotations_field_comboBox.blockSignals(True)
                old_index = self.annotations_field_comboBox.findText(old_destination_name)
                self.annotations_field_comboBox.setCurrentIndex(old_index)
                self.annotations_field_comboBox.blockSignals(False)

        else:
            # No existing annotations, just update prefs
            self._log("no existing annotations, updating destination to '{0}'".format(new_destination_name))
            set_cc_mapping('annotations', field=new_destination_field, combobox=new_destination_name)

    def configure_appearance(self):
        '''
        '''
        self._log_location()
        appearance_settings = {
                                'appearance_css': default_elements,
                                'appearance_hr_checkbox': False,
                                'appearance_timestamp_format': default_timestamp
                              }

        # Save, hash the original settings
        original_settings = {}
        osh = hashlib.md5()
        for setting in appearance_settings:
            original_settings[setting] = plugin_prefs.get(setting, appearance_settings[setting])
            osh.update(repr(plugin_prefs.get(setting, appearance_settings[setting])))

        # Display the Annotations appearance dialog
        aa = AnnotationsAppearance(self, self.annotations_icon, plugin_prefs)
        cancelled = False
        if aa.exec_():
            # appearance_hr_checkbox and appearance_timestamp_format changed live to prefs during previews
            plugin_prefs.set('appearance_css', aa.elements_table.get_data())
            # Generate a new hash
            nsh = hashlib.md5()
            for setting in appearance_settings:
                nsh.update(repr(plugin_prefs.get(setting, appearance_settings[setting])))
        else:
            for setting in appearance_settings:
                plugin_prefs.set(setting, original_settings[setting])
            nsh = osh

        # If there were changes, and there are existing annotations,
        # and there is an active Annotations field, offer to re-render
        field = get_cc_mapping('annotations', 'field', None)
        if osh.digest() != nsh.digest() and existing_annotations(self.parent, field):
            title = 'Update annotations?'
            msg = '<p>Update existing annotations to new appearance settings?</p>'
            d = MessageBox(MessageBox.QUESTION,
                           title, msg,
                           show_copy_button=False)
            self._log_location("QUESTION: %s" % msg)
            if d.exec_():
                self._log_location("Updating existing annotations to modified appearance")

                # Wait for indexing to complete
                while not self.annotated_books_scanner.isFinished():
                    Application.processEvents()

                move_annotations(self, self.annotated_books_scanner.annotation_map,
                    field, field, window_title="Updating appearance")

    def edit_css(self):
        '''
        '''
        self._log_location()
        from calibre_plugins.marvin_manager.book_status import dialog_resources_path
        klass = os.path.join(dialog_resources_path, 'css_editor.py')
        if os.path.exists(klass):
            sys.path.insert(0, dialog_resources_path)
            this_dc = importlib.import_module('css_editor')
            sys.path.remove(dialog_resources_path)
            dlg = this_dc.CSSEditorDialog(self, 'css_editor')
            dlg.initialize(self)
            dlg.exec_()

    def get_eligible_custom_fields(self, eligible_types=[], is_multiple=None):
        '''
        Discover qualifying custom fields for eligible_types[]
        '''
        #self._log_location(eligible_types)

        eligible_custom_fields = {}
        for cf in self.gui.current_db.custom_field_keys():
            cft = self.gui.current_db.metadata_for_field(cf)['datatype']
            cfn = self.gui.current_db.metadata_for_field(cf)['name']
            cfim = self.gui.current_db.metadata_for_field(cf)['is_multiple']
            #self._log("cf: %s  cft: %s  cfn: %s cfim: %s" % (cf, cft, cfn, cfim))
            if cft in eligible_types:
                if is_multiple is not None:
                    if bool(cfim) == is_multiple:
                        eligible_custom_fields[cfn] = cf
                else:
                    eligible_custom_fields[cfn] = cf
        return eligible_custom_fields

    def inventory_complete(self, msg):
        self._log_location(msg)

    def launch_cc_wizard(self, column_type):
        '''
        '''
        def _update_combo_box(comboBox, destination, previous):
            '''
            '''
            cb = getattr(self, comboBox)
            cb.blockSignals(True)
            all_items = [str(cb.itemText(i))
                         for i in range(cb.count())]
            if previous and previous in all_items:
                all_items.remove(previous)
            all_items.append(destination)

            cb.clear()
            cb.addItems(sorted(all_items, key=lambda s: s.lower()))

            # Select the new destination in the comboBox
            idx = cb.findText(destination)
            if idx > -1:
                cb.setCurrentIndex(idx)

            cb.blockSignals(False)

        from calibre_plugins.marvin_manager.book_status import dialog_resources_path

        klass = os.path.join(dialog_resources_path, 'cc_wizard.py')
        if os.path.exists(klass):
            #self._log("importing CC Wizard dialog from '%s'" % klass)
            sys.path.insert(0, dialog_resources_path)
            this_dc = importlib.import_module('cc_wizard')
            sys.path.remove(dialog_resources_path)
            dlg = this_dc.CustomColumnWizard(self,
                                             column_type,
                                             self.WIZARD_PROFILES[column_type],
                                             verbose=True)
            dlg.exec_()

            if dlg.modified_column:
                self._log("modified_column: %s" % dlg.modified_column)

                self.restart_required = True

                destination = dlg.modified_column['destination']
                label = dlg.modified_column['label']
                previous = dlg.modified_column['previous']
                source = dlg.modified_column['source']

                if source == "Annotations":
                    _update_combo_box("annotations_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_annotations_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('annotations', combobox=destination, field=label)

                elif source == 'Collections':
                    _update_combo_box("collection_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_collection_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('collections', combobox=destination, field=label)

                elif source == 'Last read':
                    _update_combo_box("date_read_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_date_read_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('date_read', combobox=destination, field=label)

                elif source == 'Locked':
                    _update_combo_box("locked_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_locked_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('locked', combobox=destination, field=label)

                elif source == "Progress":
                    _update_combo_box("progress_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_progress_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('progress', combobox=destination, field=label)

                elif source == "Read":
                    _update_combo_box("read_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_read_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('read', combobox=destination, field=label)

                elif source == "Reading list":
                    _update_combo_box("reading_list_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_reading_list_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('reading_list', combobox=destination, field=label)

                elif source == "Word count":
                    _update_combo_box("word_count_field_comboBox", destination, previous)

                    # Add/update the new destination so save_settings() can find it
                    self.eligible_word_count_fields[destination] = label

                    # Save manually in case user cancels
                    set_cc_mapping('word_count', combobox=destination, field=label)
        else:
            self._log("ERROR: Can't import from '%s'" % klass)

    def launch_new_destination_dialog(self, old, new):
        '''
        Return 'move', 'change' or 'cancel'
        '''
        from calibre_plugins.marvin_manager.book_status import dialog_resources_path
        self._log_location()
        klass = os.path.join(dialog_resources_path, 'new_destination.py')
        if os.path.exists(klass):
            self._log("importing new destination dialog from '%s'" % klass)
            sys.path.insert(0, dialog_resources_path)
            this_dc = importlib.import_module('new_destination')
            sys.path.remove(dialog_resources_path)
            dlg = this_dc.NewDestinationDialog(self, old, new)
            dlg.exec_()
            return dlg.command

    def populate_annotations(self):
        datatype = self.WIZARD_PROFILES['Annotations']['datatype']
        self.eligible_annotations_fields = self.get_eligible_custom_fields([datatype])
        self.annotations_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_annotations_fields.keys(), key=lambda s: s.lower())
        self.annotations_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('annotations', 'combobox')
        if existing:
            ci = self.annotations_field_comboBox.findText(existing)
            self.annotations_field_comboBox.setCurrentIndex(ci)

    def populate_collections(self):
        datatype = self.WIZARD_PROFILES['Collections']['datatype']
        self.eligible_collection_fields = self.get_eligible_custom_fields([datatype],
                                                                          is_multiple=True)
        self.collection_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_collection_fields.keys(), key=lambda s: s.lower())
        self.collection_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('collections', 'combobox')
        if existing:
            ci = self.collection_field_comboBox.findText(existing)
            self.collection_field_comboBox.setCurrentIndex(ci)

    def populate_date_read(self):
        #self.eligible_date_read_fields = self.get_eligible_custom_fields(['datetime'])
        datatype = self.WIZARD_PROFILES['Last read']['datatype']
        self.eligible_date_read_fields = self.get_eligible_custom_fields([datatype])
        self.date_read_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_date_read_fields.keys(), key=lambda s: s.lower())
        self.date_read_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('date_read', 'combobox')
        if existing:
            ci = self.date_read_field_comboBox.findText(existing)
            self.date_read_field_comboBox.setCurrentIndex(ci)

    def populate_locked(self):
        datatype = self.WIZARD_PROFILES['Locked']['datatype']
        self.eligible_locked_fields = self.get_eligible_custom_fields([datatype])
        self.locked_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_locked_fields.keys(), key=lambda s: s.lower())
        self.locked_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('locked', 'combobox')
        if existing:
            ci = self.locked_field_comboBox.findText(existing)
            self.locked_field_comboBox.setCurrentIndex(ci)

    def populate_progress(self):
        #self.eligible_progress_fields = self.get_eligible_custom_fields(['float'])
        datatype = self.WIZARD_PROFILES['Progress']['datatype']
        self.eligible_progress_fields = self.get_eligible_custom_fields([datatype])
        self.progress_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_progress_fields.keys(), key=lambda s: s.lower())
        self.progress_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('progress', 'combobox')
        if existing:
            ci = self.progress_field_comboBox.findText(existing)
            self.progress_field_comboBox.setCurrentIndex(ci)

    def populate_read(self):
        datatype = self.WIZARD_PROFILES['Read']['datatype']
        self.eligible_read_fields = self.get_eligible_custom_fields([datatype])
        self.read_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_read_fields.keys(), key=lambda s: s.lower())
        self.read_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('read', 'combobox')
        if existing:
            ci = self.read_field_comboBox.findText(existing)
            self.read_field_comboBox.setCurrentIndex(ci)

    def populate_reading_list(self):
        datatype = self.WIZARD_PROFILES['Reading list']['datatype']
        self.eligible_reading_list_fields = self.get_eligible_custom_fields([datatype])
        self.reading_list_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_reading_list_fields.keys(), key=lambda s: s.lower())
        self.reading_list_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('reading_list', 'combobox')
        if existing:
            ci = self.reading_list_field_comboBox.findText(existing)
            self.reading_list_field_comboBox.setCurrentIndex(ci)

    def populate_word_count(self):
        #self.eligible_word_count_fields = self.get_eligible_custom_fields(['int'])
        datatype = self.WIZARD_PROFILES['Word count']['datatype']
        self.eligible_word_count_fields = self.get_eligible_custom_fields([datatype])
        self.word_count_field_comboBox.addItems([''])
        ecf = sorted(self.eligible_word_count_fields.keys(), key=lambda s: s.lower())
        self.word_count_field_comboBox.addItems(ecf)

        # Retrieve stored value
        existing = get_cc_mapping('word_count', 'combobox')
        if existing:
            ci = self.word_count_field_comboBox.findText(existing)
            self.word_count_field_comboBox.setCurrentIndex(ci)

    """
    def select_dropbox_folder(self):
        '''
        '''
        self._log_location()
        dropbox_location = QFileDialog.getExistingDirectory(
            self,
            "Dropbox folder",
            os.path.expanduser("~"),
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
        self.dropbox_location_lineedit.setText(unicode(dropbox_location))

    def set_dropbox_syncing(self, state):
        '''
        Called when checkbox changes state, or when restoring state
        Set enabled state of Dropbox folder picker to match
        '''
        self.cfg_dropbox_folder_toolbutton.setEnabled(state)
        self.dropbox_location_lineedit.setEnabled(state)
    """

    def set_restart_required(self, state):
        '''
        Set restart_required flag to show show dialog when closing dialog
        '''
        self.restart_required = True

    """
    def save_combobox_setting(self, cb, index):
        '''
        Apply changes immediately
        '''
        cf = str(getattr(self, cb).currentText())
        self._log_location("%s => %s" % (cb, repr(cf)))

        if cb == 'annotations_field_comboBox':
            field = None
            if cf:
                field = self.eligible_annotations_fields[cf]
            set_cc_mapping('annotations', combobox=cf, field=field)
    """

    def save_settings(self):
        self._log_location()

        # Annotations field
        cf = unicode(self.annotations_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_annotations_fields[cf]
        set_cc_mapping('annotations', combobox=cf, field=field)

        # Collections field
        cf = unicode(self.collection_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_collection_fields[cf]
        set_cc_mapping('collections', combobox=cf, field=field)

        # Date read field
        cf = unicode(self.date_read_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_date_read_fields[cf]
        set_cc_mapping('date_read', combobox=cf, field=field)

        # Locked field
        cf = unicode(self.locked_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_locked_fields[cf]
        set_cc_mapping('locked', combobox=cf, field=field)

        # Progress field
        cf = unicode(self.progress_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_progress_fields[cf]
        set_cc_mapping('progress', combobox=cf, field=field)

        # Read field
        cf = unicode(self.read_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_read_fields[cf]
        set_cc_mapping('read', combobox=cf, field=field)

        # Reading list field
        cf = unicode(self.reading_list_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_reading_list_fields[cf]
        set_cc_mapping('reading_list', combobox=cf, field=field)

        # Word count field
        cf = unicode(self.word_count_field_comboBox.currentText())
        field = None
        if cf:
            field = self.eligible_word_count_fields[cf]
        set_cc_mapping('word_count', combobox=cf, field=field)

        '''
        # Save Dropbox settings
        self.prefs.set('dropbox_syncing', self.dropbox_syncing_checkbox.isChecked())
        self.prefs.set('dropbox_folder', unicode(self.dropbox_location_lineedit.text()))
        '''

        # Save general settings
        self.prefs.set('apply_markers_to_duplicates', self.duplicate_markers_checkbox.isChecked())
        self.prefs.set('apply_markers_to_updated', self.updated_markers_checkbox.isChecked())
        self.prefs.set('auto_refresh_at_startup', self.auto_refresh_checkbox.isChecked())
        self.prefs.set('show_progress_as_percentage', self.reading_progress_checkbox.isChecked())

        # Save debug settings
        self.prefs.set('debug_plugin', self.debug_plugin_checkbox.isChecked())
        self.prefs.set('debug_libimobiledevice', self.debug_libimobiledevice_checkbox.isChecked())

        # If restart needed, inform user
        if self.restart_required:
            do_restart = show_restart_warning('Restart calibre for the changes to be applied.',
                                              parent=self.gui)
            if do_restart:
                self.gui.quit(restart=True)

    def start_inventory(self):
        self._log_location()
        self.annotated_books_scanner.start()

class ClickableQLabel(QLabel):

    clicked = pyqtSignal(object)
    def __init__(self, parent):
        QLabel.__init__(self, parent)

    def mouseReleaseEvent(self, event):
        self.clicked.emit('clicked')

class InventoryAnnotatedBooks(QThread, Logger):

    signal = pyqtSignal(object, object)#SIGNAL()

    def __init__(self, gui, field, get_date_range=False):
        QThread.__init__(self, gui)
        self.annotation_map = []
        self.cdb = gui.current_db
        self.get_date_range = get_date_range
        self.newest_annotation = 0
        self.oldest_annotation = mktime(datetime.today().timetuple())
        self.field = field

    def run(self):
        if self.field is not None:
            self.find_all_annotated_books()
        else:
            self._log_location("No annotations field specified")
        if self.annotation_map and self.get_date_range:
            self.get_annotations_date_range()
        self.signal.emit("inventory_complete", "{0} annotated books".format(len(self.annotation_map)))

    def find_all_annotated_books(self):
        '''
        Find all annotated books in library
        '''
        self._log_location("field: {0}".format(self.field))
        cids = self.cdb.search_getting_ids('formats:EPUB', '')
        for cid in cids:
            mi = self.cdb.get_metadata(cid, index_is_id=True)
            raw = mi.get_user_metadata(self.field, False)
            if raw['#value#'] is not None:
                soup = BeautifulSoup(raw['#value#'])
                if soup.find('div', 'user_annotations') is not None:
                    self.annotation_map.append(mi.id)

    def get_annotations_date_range(self):
        '''
        Find oldest, newest annotation in annotated books
        initial values of self.oldest, self.newest are reversed to allow update comparisons
        if no annotations, restore to correct values
        '''
        annotations_found = False

        for cid in self.annotation_map:
            mi = self.cdb.get_metadata(cid, index_is_id=True)
            if self.field == 'Comments':
                soup = BeautifulSoup(mi.comments)
            else:
                soup = BeautifulSoup(mi.get_user_metadata(self.field, False)['#value#'])

            uas = soup.findAll('div', 'annotation')
            for ua in uas:
                annotations_found = True
                timestamp = float(ua.find('td', 'timestamp')['uts'])
                if timestamp < self.oldest_annotation:
                    self.oldest_annotation = timestamp
                if timestamp > self.newest_annotation:
                    self.newest_annotation = timestamp

        if not annotations_found:
            temp = self.newest_annotation
            self.newest_annotation = self.oldest_annotation
            self.oldest_annotation = temp

# For testing ConfigWidget, run from command line:
# cd ~/Documents/calibredev/Marvin_Manager
# calibre-debug config.py
# Search 'Marvin'
if __name__ == '__main__':
    try:
        from PyQt5.Qt import QApplication
    except ImportError:
        from PyQt4.Qt import QApplication

    from calibre.gui2.preferences import test_widget
    app = QApplication([])
    test_widget('Advanced', 'Plugins')


import pygame
import math

from skill import Skill

class Bolt(Skill):
  def fire(self, position):
    if self.cooldown_countdown <= 0 and not self.fired:
      src_x, src_y = self.center = self.caster.center
      dst_x, dst_y = position
      delta_x = dst_x - src_x
      delta_y = dst_y -  src_y

      theta = math.atan2(delta_y, delta_x)
      self.y_speed = math.sin(theta) * self.move_speed
      self.x_speed = math.cos(theta) * self.move_speed

      self.fired = True
      return self
    else:
      return None


#!/usr/bin/env python3
import asyncio
import sys

class DateProtocol(asyncio.SubprocessProtocol):
    def __init__(self, exit_future):
        self.exit_future = exit_future
        self.output = bytearray()

    def pipe_data_received(self, fd, data):
        self.output.extend(data)

    def process_exited(self):
        self.exit_future.set_result(True)

def on_pid(future):
    pid = future.result()
    print('pid={}'.format(pid))

def on_date(future):
    date = future.result()
    print('date="{}"'.format(date.decode('ascii').rstrip()))

class job:
    async def get_date(self, loop):
        date = asyncio.Future(loop=loop)
        date.add_done_callback(on_date)
        await self.exit_future

        # Close the stdout pipe
        self.transport.close()

        # Read the output which was collected by the pipe_data_received()
        # method of the protocol
        date.set_result(bytes(self.protocol.output))

    async def get_pid(self, loop):
        self.exit_future = asyncio.Future(loop=loop)
        self.pid_future = asyncio.Future(loop=loop)
        self.pid_future.add_done_callback(on_pid)

        # Create the subprocess controlled by the protocol DateProtocol,
        # redirect the standard output into a pipe
        create = loop.subprocess_exec(lambda: DateProtocol(self.exit_future),
                                      'test1.sh',
                                      stdin=None, stderr=None)
        transport, protocol = await create
        self.transport = transport
        self.protocol = protocol
        pid = transport.get_pid()
        self.pid_future.set_result(pid)

if sys.platform == "win32":
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)
else:
    loop = asyncio.get_event_loop()

j = job()

asyncio.ensure_future(j.get_pid(loop))
results = loop.run_until_complete(j.get_date(loop))
loop.close()


# # A very simple Flask Hello World app for you to get started with...

# from flask import Flask

# app = Flask(__name__)

# @app.route('/')
# def hello_world():
#     return 'Hello from Flask!'

from flask_restful import Resource, Api
from flask import Flask,request
from Flask_server_files.DBoperations import *
from Flask_server_files.exposed_values import *
# from .DBoperations import *
# from .exposed_values import *

app = Flask(__name__)
api = Api(app)

def get_cur_read():
    return current_val

class TodoSimple(Resource):
    def get(self, todo_id):
        global current_val
        return {"value":current_val,"a array":a}

    def post(self, todo_id):
        # 'data' is like a name variable
        # hence in request it must be present as
        # {'data':'<actual data>'}
        # todos[todo_id] = request.form['data']
        global a,current_val
        current_val=request.form['reading']
        print("Captured reading is ",current_val)
        a.append(current_val)
        print("data saved!!!")
        # return {todo_id: todos[todo_id]}
        return {"status":a}

class Linear(Resource):
    # def __init__(self):
    #
    #     super(Linear, self).__init__()

    def get(self,reading):
        global last_val,captured_values,stand_by_value,captured_values
        return {"reading":last_val,"captured":captured_values}

    def post(self,reading):
        global last_val, captured_values, stand_by_value, captured_values,cnt
        # reading -> if a number use as value  if "reset" clear all values in self.captured values

        # current_val = request.form['reading']
        current_val = reading
        print("current value")

        if current_val=="reset":
            print("reset in linear triggered!")
            captured_values=[]
            stand_by_value=fetch_from_linear()
            last_val=stand_by_value

        current_val=float(current_val)
        print("linear reading is ",current_val)

        if(last_val==current_val):
            cnt+=1
        #
        # if self.cnt==30:
        #     print("last standy value updated to",self.last_val)
        # write_to_linear(self.last_val)

        last_val=current_val
        captured_values.append(last_val)
        if len(captured_values)>100:
            captured_values.pop(0)

        # call update graph value herec
        # debug purpose
        return {"captured":captured_values}

# @app.route('/lin_stand')
# def linear_standby():
#     pass

class VFD_control(Resource):
    # def __init__(self):
    #
    #     super(VFD_control, self).__init__()

    def get(self):
        global current,freq,voltage
        return {"cur":current,"freq":freq,"vol":voltage}

    def post(self):
        global current, freq, voltage
        mode=request.form['mode']
        if mode=="control":
            self.control_module()
            return {"vfd feed":f"control updated {mode}"}

        value = request.form['value']
        if mode=="freq":
            freq=value
        if mode=="current":
            current=value
        if mode=="voltage":
            voltage=value

        write_to_vfd(current,voltage,freq)
        # update graph here
        print("Values written")
        return {"vfd stat":f"{mode}:{value}"}

    def control_module(self):
        pass
        #write code for control value passing

class Rotary(Resource):
    # def __init__(self):

    def get(self):
        global pulse_values,pulse_read
        return {"pulses":pulse_read}

    def post(self):
        global pulse_read,pulse_values
        val=request.form['pulses']
        pulse_read+=float(val)
        # fro debugging
        # write_to_rotary(val)
        pulse_values.append(val)
        if len(pulse_values)==100:
            # remove one value
            pulse_values.pop(0)
        # add update graph function here

api.add_resource(TodoSimple,'/<string:todo_id>')
api.add_resource(VFD_control,'/vfd')
# use lin/<any number> to get the value
# use lin/<reading> to put the value
api.add_resource(Linear,'/lin/<string:reading>')
api.add_resource(Rotary,'/rot')
# api.add_resource(TodoSimple,'/')

print("server started!!")

if __name__ == '__main__':
    app.run(debug=True)
    print("server started!!")


################################################################################
## Student Name: Daniel Navarro                      Programming Fundamentals ##
## program name: Formulas.py                         CS V11                   ##
## Comment     : Covering Chapter 2 Assingment       Summer of 2016           ##
##               More on print command               Ventura College          ##
##                                                   Professor RM Polito      ##
################################################################################

import  sys
import   os
import math
from random import *

def ClearTheScreen():
    os.system("cls")

def Heading():
    print ("\t\t  MATH AND SCIENCE DIVISION OF VENTURA COLLEGE")
    print ("\t\t\t  Computer Scinece Department\n")
    print ("\t\t       CS V11 - Programming Fundamentals")
    print ("\t\t\t\t Daniel Navarro\n\n\n")

def FormulaOne():
    print ("\t\t Performing FormulaOne:\n\n")
    A = 1
    B = 3
    C = 5
    D = 2
    E = 1.*(A + B) / (C - D) # Instead of using float, use (1.*) to get decimals
    print("\t\t\tThe value of E = ", round(E,3))

def FormulaTwo():
    print ("\n\n\t\t Performing FormulaTwo:\n\n")
    A = 4.3
    B = 3.1
    C = 2.1
    D = 6.5
    E = (A*B - C/D) / (A + B/C) # Since given all floats (non integers) there is no need for 1.0*
    print (" When A =", A," and B =",B," and C =",C," and D =",D,"then FormulaTwo =",round(E,2))

def FormulaThree(): # print #'s: 3,5,7,9...21
    print ("\n\n\t\tPerforming FormulaThree:\n")
    for k in range(10):
        print ("\t\t\t\t\t",2*k+3)

def FormulaFour(): # print #'s: 95,90,85,80...50
    print ("\n\n\t\tPerforming FormulaFour:\n")
    for k in range(10):
        print ("\t\t\t\t\t",95-k*5)

def FormulaFive(): # print #'s: 1,2,4,8...1024
    print ("\n\n\t\tPerforming FormulaFive:\n")
    for k in range(11):
        print ("\t\t\t\t\t",2**k)

def FormulaSix(): # print #'s: 1,2,4,8...1024
    print ("\n\n\t\t Performing FormulaSix:\n")
    for k in range(11):
        print ("\t\t\t\t\t",(-2)**k)

def FormulaSeven(): # I will perform the square root of A
    print ("\n\n\t\tPerforming FormulaSeven:\n\n")
    A = 144
    B = math.sqrt(A)
    print ("\t\t\tThe square root of",A," is equal to",B,"\n\n")

def FormulaEight(): # We will solve the Ideal Gas Formula PV = nRT(T+273). Find formula for n?
    P = 100
    V = 44.8
    R = 833
    T = 100
    n = P*V/(R*(T+273))
    print("\n\n\t\tPerforming FormulaEigth:\n\n")
    print("\t\t\tThe value of n =", round(n,3))

def FormulaNine():
    V = 1000
    r = math.sqrt(3*V/(4*math.pi))**(1./3)
    print("\n\n\t\t Performing FormulaNine:\n\n")
    print("\t\t\tThe value of r =", round(r,3))

def FormulaTen(): # P = Deposit(1+R)^n. Solve for n?
    P = 200000
    D = 5000
    r = 0.09
    n = (math.log(P) - math.log(D)) / math.log(1+r)
    print("\n\n\t\t  Performing FormulaTen:\n\n")
    print("\t\t\tThe value of n =", round(n,3))
    print("\n\n")

def HarderOne(): # generate 10 random numbers that are two-digit and EVEN
    C = 0
    while C < 10:
        Numbers = int(random()*10000)
        if Numbers > 9 and Numbers < 100 and Numbers % 2 == 0:
            print ("\t\t\t\tThe numbers are:", Numbers)
            C = C + 1

def HarderTwo(): # Generate 17 #'s 4-digit ODD and divisible by 7
    A = 0
    while A < 17:
        Numbers = int(random()*10000)
        if Numbers > 999 and Numbers < 10000 and Numbers % 7 == 0 and Numbers % 2 != 0:
            print ("\t\t\t\tThe numbers are:", Numbers)
            A = A + 1

def main():
    ClearTheScreen()
    Heading()
    FormulaOne()
    FormulaTwo()
    FormulaThree()
    FormulaFour()
    FormulaFive()
    FormulaSix()
    FormulaSeven()
    FormulaEight()
    FormulaNine()
    FormulaTen()
    HarderOne()
    HarderTwo()


from pydub import AudioSegment
import numpy as np

def find_max(list_):
    index=0
    for i in range(0,len(list_)):
        if(list_[i]>list_[index]):
            index=i
    return index

src = "EverlastingTruth.mp3"
audio = AudioSegment.from_file(src,"mp3")
#raw_audio_data = audio.raw_data
#decibel = audio[4900:5000].dBFS
#data = np.fromstring(audio._data, np.int16)
data_length = len(audio)
print(data_length)

#每500ms=0.5s算一个音
note_time = 500
bar = data_length//note_time + 1
note_decibel = []

for i in range(0,bar):
    note_decibel.append((audio[500*i:500*(i+1)]).dBFS)
note_num=len(note_decibel)
print(note_num)

#设置每7s为一个小节
piece_time = 7000
note_every_piece = piece_time // note_time
piece_num = 500*len(note_decibel)//piece_time
piece_max=[]
point_max=[]

for i in range(0,piece_num):
    piece_max.append(max(note_decibel[i*note_every_piece:(i+1)*note_every_piece]))
    point_max.append(find_max(note_decibel[i*note_every_piece:(i+1)*note_every_piece]))

print(len(piece_max))
print(piece_max)

stress_time=[]
print("Stress time:")
for i in range(0,len(point_max)):
    stress_time.append(i*7+0.5*point_max[i])
    print(i*7+0.5*point_max[i],"s")

#print(max(note_decibel))
#
#decibel_time = 500
#print(len(audio._data))
#print(len(raw_audio_data))

"""
print(decibel)
loudness = audio.dBFS
print(loudness)
data = np.fromstring(loudness,np.int16)
print(data)
length = len(data)
for i in data:
    if(i!=0):
        print(i)
print(data[100:200])

"""


from ModelHelper.Common.CommonUtils import get
import numpy as np
import os
import cv2

class Quadrangle:
    def __init__(self, **kwargs):
        """
        pos_str: "x0,y0,x1,y1,x2,y2,x3,y3"
        pos_list: [[x0, y0], [x1, y1], [x2, y2], [x3, y3]]
        :param kwargs:
        """
        self.pos_str = get('pos_str', kwargs)
        self.pos_array = get('pos_array', kwargs)
        if self.pos_str is None and self.pos_array is None:
            raise RuntimeError('pos_str is None and pos_list is None!')
        if self.pos_str is None:
            self.pos_array = self.pos_array.astype(np.int32)
            self.pos_str = self.__pos_array2str()
        if self.pos_array is None:
            self.pos_array = self.__pos_str2pos_array()
            self.pos_array = self.pos_array.astype(np.int32)
        self.left = self.pos_array[:, 0].min()
        self.right = self.pos_array[:, 0].max()
        self.top = self.pos_array[:, 1].min()
        self.bottom = self.pos_array[:, 1].max()
        self.area = (self.bottom - self.top) * (self.right - self.left)

    def to_stander(self):
        """
        顺时针排列，第一个点为左上角
        :return: None
        """
        poly = np.array(self.pos_array)
        edge = [
            (poly[1][0] - poly[0][0]) * (poly[1][1] + poly[0][1]),
            (poly[2][0] - poly[1][0]) * (poly[2][1] + poly[1][1]),
            (poly[3][0] - poly[2][0]) * (poly[3][1] + poly[2][1]),
            (poly[0][0] - poly[3][0]) * (poly[0][1] + poly[3][1])
        ]
        poly_area = np.sum(edge) / 2.
        if abs(poly_area) < 1:
            print('invalid poly')
            self.pos_array = None
            self.pos_str = None
        else:
            # 保证poly 顺时针排列
            if poly_area > 0:
                # print('poly in wrong direction')
                poly = poly[(0, 3, 2, 1), :]
            score0 = np.sum(poly[0, :])
            score1 = np.sum(poly[1, :])
            score2 = np.sum(poly[2, :])
            score3 = np.sum(poly[3, :])
            # 保证poly 第一个坐标点为左上角
            min_score = min(score0, score1, score2, score3)
            if min_score == score0:
                pass
            elif min_score == score1:
                poly = poly[(1, 2, 3, 0), :]
            elif min_score == score2:
                poly = poly[(2, 3, 0, 1), :]
            elif min_score == score3:
                poly = poly[(3, 0, 1, 2,), :]
            self.pos_array = poly
            self.pos_str = self.__pos_array2str()

    def __pos_array2str(self):
        pos_str = str(self.pos_array[0][0]) + ',' + str(self.pos_array[0][1]) + ',' \
                  + str(self.pos_array[1][0]) + ',' + str(self.pos_array[1][1]) + ',' \
                  + str(self.pos_array[2][0]) + ',' + str(self.pos_array[2][1]) + ',' \
                  + str(self.pos_array[3][0]) + ',' + str(self.pos_array[3][1])
        return pos_str

    def __pos_str2pos_array(self):
        info = self.pos_str.split(',')
        if len(info) >= 8:
            x0 = int(float(info[0]))
            y0 = int(float(info[1]))
            x1 = int(float(info[2]))
            y1 = int(float(info[3]))
            x2 = int(float(info[4]))
            y2 = int(float(info[5]))
            x3 = int(float(info[6]))
            y3 = int(float(info[7]))

            pos_array = np.zeros((4, 2), dtype=np.int32)
            pos_array[0][0] = x0
            pos_array[0][1] = y0
            pos_array[1][0] = x1
            pos_array[1][1] = y1
            pos_array[2][0] = x2
            pos_array[2][1] = y2
            pos_array[3][0] = x3
            pos_array[3][1] = y3

            return pos_array
        else:
            print('quadrangle label should look like: "x0,y0,x1,y1,x2,y2,x3,y3", but the input is : {}'.format(
                self.pos_str))

def txt2list(txt_path, encoding='utf-8'):
    assert os.path.exists(txt_path)
    txt = open(txt_path, 'r', encoding=encoding)
    quadrangle_list = list()
    for line in txt.readlines():
        line = line.strip()
        line = line.replace('\n', '')
        if line == '':
            continue
        info = line.split(',')
        if len(info) >= 8:
            qua = Quadrangle(pos_str=line)
            quadrangle_list.append(qua)
        else:
            print('quadrangle label should look like: "x0,y0,x1,y1,x2,y2,x3,y3", but the input is : {}'.format(line))
    txt.close()
    return quadrangle_list

def list2txt(quadrangle_list, txt_path, encoding='utf-8'):
    if os.path.exists(txt_path):
        os.remove(txt_path)
    txt = open(txt_path, 'a', encoding=encoding)
    for qua in quadrangle_list:
        txt.write(qua.pos_str + '\n')
    txt.close()

def txt2array(txt_path, encoding='utf-8'):
    assert os.path.exists(txt_path)
    txt = open(txt_path, 'r', encoding=encoding)
    lines = txt.readlines()
    txt.close()
    filtered_lines = list()
    for line in lines:
        line = line.strip()
        line = line.replace('\n', '')
        if line == '':
            continue
        info = line.split(',')
        if len(info) >= 8:
            filtered_lines.append(line)
        else:
            print('quadrangle label should look like: "x0,y0,x1,y1,x2,y2,x3,y3", but the input is : {}'.format(line))
    qua_num = len(filtered_lines)
    quadrangle_array = np.zeros((qua_num, 4, 2), dtype=np.int32)
    idx = 0
    for line in filtered_lines:
        qua = Quadrangle(pos_str=line)
        quadrangle_array[idx, :, :] = qua.pos_array
        idx += 1

    return quadrangle_array

def array2txt(quadrangle_array, txt_path, encoding='utf-8'):
    if os.path.exists(txt_path):
        os.remove(txt_path)
    txt = open(txt_path, 'a', encoding=encoding)
    qua_num = quadrangle_array.shape[0]
    for idx in range(qua_num):
        qua = quadrangle_array[idx, :, :]
        qua = Quadrangle(pos_array=qua)
        txt.write(qua.pos_str + '\n')
    txt.close()

def compute_iou(qua1, qua2):
    top = min(qua1.top, qua2.top)
    left = min(qua1.left, qua2.left)
    bottom = max(qua1.bottom, qua2.bottom)
    right = max(qua1.right, qua2.right)

    qua1_array = qua1.pos_array
    qua1_array[:, 0] -= left
    qua1_array[:, 1] -= top
    qua2_array = qua2.pos_array
    qua2_array[:, 0] -= left
    qua2_array[:, 1] -= top

    qua1_draw = np.zeros((1, qua1_array.shape[0], 2), dtype=np.int32)
    qua1_draw[0, :, :] = qua1_array
    qua1_poly = np.zeros((bottom - top, right - left), dtype=np.int32)
    cv2.fillPoly(qua1_poly, qua1_draw, 1)
    # cv2.imwrite('qua1.png', qua1_poly*255)
    qua1_sum = qua1_poly.sum()

    qua2_draw = np.zeros((1, qua2_array.shape[0], 2), dtype=np.int32)
    qua2_draw[0, :, :] = qua2_array
    qua2_poly = np.zeros((bottom - top, right - left), dtype=np.int32)
    cv2.fillPoly(qua2_poly, qua2_draw, 1)
    # cv2.imwrite('qua2.png', qua2_poly*255)
    qua2_sum = qua2_poly.sum()

    inter = qua1_poly * qua2_poly
    # cv2.imwrite('inter.png', inter*255)

    inter = inter.sum()
    union = qua1_sum + qua2_sum - inter
    iou = inter / union
    return iou

def resize(qua_array, ratio):
    (ratio_h, ratio_w) = ratio
    qua_array[:, :, 0] *= ratio_w
    qua_array[:, :, 1] *= ratio_h
    return qua_array

def flip(qua_array, type, img_shape):
    """
    flip
    :param txt_path:
    :param type: type=0, vertical flip, type=1, horizontal flip
    :return:
    """

    assert type == 0 or type == 1
    (h, w) = img_shape[:2]
    if type == 0:
        qua_array[:, :, 1] = h - qua_array[:, :, 1]
    else:
        qua_array[:, :, 0] = w - qua_array[:, :, 0]

    return qua_array


'''
This file is part of Hyphan.
Hyphan is free software: you can redistribute it and/or modify
it under the terms of the GNU Afferno General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Hyphan is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Afferno General Public License for more details.

You should have received a copy of the GNU Afferno General Public
License along with Hyphan.  If not, see
https://www.gnu.org/licenses/agpl-3.0.html>.
'''
from telegram import ParseMode
from telegram.ext import CommandHandler

def devchannel(bot, update, args):
    """Post to the Hyphan development channel"""
    if API.api.is_sender_admin(update):
        channel = API.get_config("channel")
        bot.sendMessage(chat_id=channel, text=" ".join(args), parse_mode=ParseMode.MARKDOWN)
    else:
        bot.sendMessage(chat_id=update.message.chat_id, text="*You're not admin.*",
                        parse_mode=ParseMode.MARKDOWN)

# Development commands
class Dispatch(object):
    """Defines the metadata for hyphan"""
    def __init__(self, api, updater):
        self.api = api
        self.updater = updater
        self.define_commands()
        self.define_help()
        self.set_api()

    def set_api(self):
        """Set the api for use in the logic"""
        global API
        API = self.api

    def define_commands(self):
        """Bind the commands"""
        dispr = self.updater.dispatcher
        dispr.add_handler(CommandHandler("devpost", devchannel, pass_args=True))

    def define_help(self):
        """Set the help messages"""
        self.api.set_help('devpost', 'Send a message to the development channel.\n/devpost" \
        " [message]')


import trep
from trep import rx, ry, rz, tx, ty, tz, const_txyz
import numpy as np
from parameters import *

class FixedAngle(trep.Constraint):
    """ Defines a constraint on an angle that should remain at a fixed value. """
    def __init__(self, system, angle_name, reference, name):
        trep.Constraint.__init__(self, system, name=name)
        self.angle = system.get_config(angle_name)
        self.reference = reference

    def h(self):
        return self.angle.q - self.reference

    def h_dq(self, q1):
        if q1 == self.angle:
            return 1.0
        return 0.0

    def h_dqdq(self, q1, q2):
        return 0.0

# Define the constraint on the base angle.
class BaseAngle(trep.Constraint):
    """ This defines a constraint on the base motion of the whisker.
        It is used to drive the whisking motion. """
    def __init__(self, system, angle_name, h_func, hdt_func, name=None):
        trep.Constraint.__init__(self, system, name=name)
        self.base_angle = system.get_config(angle_name)
        self.h_func = h_func
        self.hdt_func = hdt_func

    def h(self):
        return self.base_angle.q - self.h_func(self.system.t)

    def h_dq(self, q1):
        if self.base_angle == q1:
            return 1.0
        return 0.0

    def h_dqdq(self, q1, q2):
        return 0.0

    def h_dt(self):
        return self.hdt_func(self.system.t)

class Whisker2D(trep.System):
    """ Defines a single, two dimensional whisker. """
    def __init__(self, parameters, base_motion):
        trep.System.__init__(self)

        self.num_links = parameters['N']
        self.lengths = parameters['L']
        self.import_frames(self.whisker_frames(parameters['a']))
        self.add_base_constraints(base_motion)

        self.add_node_springs(parameters['k'])
        self.add_node_damping(parameters['c'])
        self.set_masses(parameters['m'])
        self.satisfy_constraints()

    def whisker_frames(self, curvature):
        """ Creates a list of frames that define the whisker. """
        if curvature is None:
            ref_angles = np.zeros(self.num_links)
        else:
            ref_angles = get_angles_from_curvature(self.lengths, curvature)

        frames = []
        for j in reversed(range(1, self.num_links)):
            frames = [ty(self.lengths[j], name='Link-%d' %j), frames]
            frames = [rx('theta-%d' %j), frames]
            frames = [rx(ref_angles[j]), frames]
        frames = [ty(self.lengths[0], name='Link-0'), frames]
        frames = [tz('z'), [ty('y', name='Base_Point'), frames]]
        frames = [rx('theta-0'), frames]
        frames = [rx(ref_angles[0]), frames]
        return frames

    def set_masses(self, m):
        """ Sets masses at each of the whisker's nodes. """
        for j in range(self.num_links):
            self.get_frame('Link-%d' %j).set_mass((m[j],0,0,0))
        self.get_frame('Base_Point').set_mass((m[0],0,0,0))

    def add_node_springs(self, k):
        """ Adds configuration (torsional) springs at each node. """
        for j in range(self.num_links):
            trep.potentials.ConfigSpring(self, 'theta-%d' %j, k=k[j], name='spring-%d' %j)

    def add_node_damping(self, c, default=0.0):
        """ Adds damping at each node. """
        damp_dict = {}
        for j in range(self.num_links):
            damp_dict.update({'theta-%d' %j: c[j]})
        trep.forces.Damping(self, default, damp_dict)

    def add_base_constraints(self, base_motion):
        """ Adds constraints on the rotation and translation at whisker's base. """
        BaseAngle(self, 'theta-0', base_motion['h'], base_motion['hdt'], name='M')
        trep.constraints.PointOnPlane(self, 'Base_Point', (0,0,1), 'World', name='FZ')
        trep.constraints.PointOnPlane(self, 'Base_Point', (0,1,0), 'World', name='FY')

class Whisker3D(trep.System):
    """ Defines a single, three dimensional whisker. """
    def __init__(self, parameters, base_motion, name=None):
        trep.System.__init__(self)

        self.name = name
        self.num_links = parameters['N']
        self.lengths = parameters['L']
        self.import_frames(self.whisker_frames(parameters['a'],
                           parameters['base_pos'], parameters['base_rot']))
        self.add_base_constraints(base_motion)

        self.add_node_springs(parameters['k'])
        self.add_node_damping(parameters['c'])
        self.set_masses(parameters['m'])
        self.satisfy_constraints()

    def whisker_frames(self, curvature, base_pos, base_rot):
        """ Creates a list of frames that define the whisker. """
        if curvature is None:
            ref_angles = np.zeros(self.num_links)
        else:
            ref_angles = get_angles_from_curvature(self.lengths, curvature)

        frames = []
        for j in reversed(range(1, self.num_links)):
            frames = [tx(self.lengths[j], name='Link-%d' %j), frames]
            frames = [rz('theta-%d_z' %j), frames]
            frames = [ry('theta-%d_y' %j), frames]
            #frames = [rx('theta-%d_x' %j), frames]
            frames = [rz(-ref_angles[j]), frames]
        frames = [tx(self.lengths[0], name='Link-0'), frames]
        frames = [rz('theta-0_z', name='Moving Base Point'), frames]
        frames = [ry('theta-0_y'), frames]
        frames = [rx('theta-0_x'), frames]
        frames = [tz('z'), [ty('y'), [tx('x'), frames]]]

        (X, Y, Z)          = base_pos
        (theta, phi, zeta) = base_rot

        # Rotate to the correct position.
        frames = [rz(theta), [ry(phi), [rx(zeta), frames]]]

        # Place the whisker at the correct spot on the mystacial pad and add an angle
        #   that drives the whisking motion.
        frames = [tx(X), [ty(Y),[ tz(Z, name='Base Point'),
                 [rz('Driving Angle', name="Driving Angle"), frames]]]]

        frames = [ rz(0.0, name='Head'), frames]

        return frames

    def set_masses(self, m):
        """ Sets masses at each of the whisker's nodes. """
        for j in range(self.num_links):
            self.get_frame('Link-%d' %j).set_mass((m[j],0,0,0))
        self.get_frame('Base Point').set_mass((m[0],0,0,0))

    def add_node_springs(self, k):
        """ Adds configuration (torsional) springs at each node. """
        for j in range(self.num_links):
            trep.potentials.ConfigSpring(self, 'theta-%d_z' %j, k=k[j],
                    name='spring-%d_z' %j)
            trep.potentials.ConfigSpring(self, 'theta-%d_y' %j, k=k[j],
                    name='spring-%d_y' %j)
            if j == 0:
                trep.potentials.ConfigSpring(self, 'theta-%d_x' %j, k=k[j],
                        name='spring-%d_x' %j)

    def add_node_damping(self, c, default=0.0):
        """ Adds damping at each node. """
        damp_dict = {}
        for j in range(self.num_links):
            damp_dict.update({'theta-%d_y' %j: c[j],
                'theta-%d_z' %j: c[j]})
            if j==0:
                damp_dict.update({'theta-%d_x' %j: c[j]})
        trep.forces.Damping(self, default, damp_dict)

    def add_base_constraints(self, base_motion):
        """ Adds constraints on the rotation and translation at whisker's base. """
        BaseAngle(self, 'Driving Angle', base_motion['h'], base_motion['hdt'])
        trep.constraints.PointOnPlane(self, 'Moving Base Point', (1,0,0), 'Base Point',
                name='FX')
        trep.constraints.PointOnPlane(self, 'Moving Base Point', (0,1,0), 'Base Point',
                name='FY')
        trep.constraints.PointOnPlane(self, 'Moving Base Point', (0,0,1), 'Base Point',
                name='FZ')
        FixedAngle(self, 'theta-0_x', 0.0, name='MX')
        FixedAngle(self, 'theta-0_y', 0.0, name='MY')
        FixedAngle(self, 'theta-0_z', 0.0, name='MZ')


from django.db import models

class Coupon(models.Model):
    code = models.CharField(max_length=50, unique=True)
    value = models.IntegerField()
    active = models.BooleanField(default=True)
    num_available = models.IntegerField(default=1)
    num_used = models.IntegerField(default=0)

    def __str__(self):
        return self.code

    def can_use(self):
        is_active = True

        if self.active == False:
            is_active = False

        if self.num_used >= self.num_available and self.num_available != 0:
            is_active = False

        return is_active

    def use(self):
        self.num_used = self.num_used + 1

        if self.num_used == self.num_available:
            self.active = False

        self.save()


import tkinter
from tkinter import *
import personal_y
import name

class FullDates:

    def __init__(self):
        self.real_year = None
        self.real_month = None
        self.full_list = None
        self.shana_ishit = None
        self.bd_month = None
        self.forth_pick_end = None
        self.forth_pick_start = None
        self.third_pick_start = None
        self.second_pick_start = None
        self.first_pick_start = None
        self.forth_challenge = None
        self.third_challenge = None
        self.second_challenge = None
        self.first_challenge = None
        self.first_pick = None
        self.second_pick = None
        self.third_pick = None
        self.forth_pick = None
        self.final_number_destiny = None
        self.p_year = None
        self.p_month = None
        self.p_day = None
        self.full_date_short = None
        self.date_button_canvas = None
        self.date_button = None
        self.second_photo = None
        self.first_photo = None
        self.entry_canvas_last_name = None
        self.entry_last_name = None
        self.entry_canvas_first_name = None
        self.entry_first_name = None
        self.entry_canvas_date = None
        self.canvas = None
        self.entry_date = None
        self.final_number = None
        self.day_convert_list = None
        self.month_convert_list = None
        self.year_convert_list = None
        self.year = None
        self.month = None
        self.day = None
        self.fulldate_list = None
        self.down_day_convert_list = None

#צמצום המספרים למספר אחד
    def short_numbers(self, args):
        lista = [x for x in str(args)]
        list_map = map(int, lista)
        convert_list = list(list_map)
        sum_number = sum(convert_list)
        if sum_number > 9:
            self.short_numbers(sum_number)
        else:
            self.final_number = sum_number

#צמצום היעוד ל2 ספרות עבור שיעורי הנשמה
    def short_numbers_for_destiny(self, args):
        lista = [x for x in str(args)]
        list_map = map(int, lista)
        convert_list = list(list_map)

        self.final_number = sum(convert_list)

    def print_final_details(self):
        self.canvas.delete(self.entry_canvas_date, self.entry_canvas_first_name,
                           self.entry_canvas_last_name,self.date_button_canvas)
        self.second_photo = PhotoImage(file="image/second_page.png")
        self.canvas.create_image(0, 0, image=self.second_photo, anchor="nw")

        print_full_date_short = self.canvas.create_text(815, 150, fill="white", text=self.full_date_short,
                                                        font=("Ariel", 25, "italic"))
        self.short_numbers(self.day)
        self.p_day = self.final_number

        self.short_numbers(self.month)
        self.p_month = self.final_number

        self.short_numbers(self.year)
        self.p_year = self.final_number

        self.short_numbers(self.final_number_destiny)
        self.final_number_destiny = self.final_number

        # ------------------------------הדפסה של תאריכים ----------------------------

        print_full_date = self.canvas.create_text(580,100, fill='white', text=f" {self.p_day} / {self.p_month} / {self.p_year}", font=("Ariel", 25, "italic"))
        #print:
        print_destiny_final = self.canvas.create_text(770, 150, fill="white", text=f" {self.final_number_destiny} /", font=("Ariel", 25, "italic",))
        self.shana_ishit = personal_y.This_Year().shana_ishit(day=self.p_day, month=self.p_month, bd_month=self.bd_month)
        print_shana_ishit = self.canvas.create_text(790, 425, fill="white", text=self.shana_ishit, font=("Ariel", 25, "italic",))
        print_shana_nisteret = self.canvas.create_text(790, 479, fill="white", text=f" {personal_y.This_Year().shana_nisteret(self.final_number_destiny, self.shana_ishit)} / {self.final_number_destiny}", font=("Ariel", 25, "italic",))
        print_age = self.canvas.create_text(795, 525, fill= "white", text= personal_y.This_Year().calculet_age(bd_month=self.real_month, year_of_birth=self.real_year),font=("Ariel", 25, "italic",))

#------------------------------הדפסה של   פסגות והאתגרים ----------------------------

        p_peak1 = self.canvas.create_text(90, 135, fill='white', text=name.NamesData().sum_num(self.first_pick),  font=("Ariel", 25, "italic"))
        p_peak2 = self.canvas.create_text(90, 185, fill='white', text=name.NamesData().sum_num(self.second_pick), font=("Ariel", 25, "italic"))
        p_peak3 = self.canvas.create_text(90, 235, fill='white', text=name.NamesData().sum_num(self.third_pick), font=("Ariel", 25, "italic"))
        p_peak4 = self.canvas.create_text(90, 285, fill='white', text=name.NamesData().sum_num(self.forth_pick), font=("Ariel", 25, "italic"))
        p_challenge1 = self.canvas.create_text(230, 135, fill='white', text=abs(name.NamesData().sum_num(self.first_challenge)), font=("Ariel", 25, "italic"))
        p_challenge2 = self.canvas.create_text(230, 185, fill='white', text=abs(name.NamesData().sum_num(self.second_challenge)), font=("Ariel", 25, "italic"))
        p_challenge3 = self.canvas.create_text(230, 235, fill='white', text=abs(name.NamesData().sum_num(self.third_challenge)), font=("Ariel", 25, "italic"))
        p_challenge4 = self.canvas.create_text(230, 285, fill='white', text=abs(name.NamesData().sum_num(self.forth_challenge)), font=("Ariel", 25, "italic"))

#------------------------------הדפסה של גילאי תחילת הפסגות והאתגרים ----------------------------
        self.first_pick_start = int(27 - self.final_number_destiny)
        self.second_pick_start = int(self.first_pick_start + 9)
        self.third_pick_start = int(self.second_pick_start + 9)
        self.forth_pick_start = int(self.third_pick_start + 9)
        self.forth_pick_end = int(self.forth_pick_start + 9)

        p_psagot_date1 = self.canvas.create_text(370, 135, fill='white', text=f" {self.first_pick_start} - {self.second_pick_start} ", font=("Ariel", 25, "italic"))
        p_psagot_date2 = self.canvas.create_text(370, 185, fill='white', text=f" {self.second_pick_start} - {self.third_pick_start} ", font=("Ariel", 25, "italic"))
        p_psagot_date3 = self.canvas.create_text(370, 235, fill='white', text=f" {self.third_pick_start} - {self.forth_pick_start} ", font=("Ariel", 25, "italic"))
        p_psagot_date4 = self.canvas.create_text(370, 285, fill='white', text=f" {self.forth_pick_start} - {self.forth_pick_end} ", font=("Ariel", 25, "italic"))

    # ------------------------------הדפסה של נתוני השם, עיצורים והאוי ----------------------------
        n_data = name.NamesData()
        first_name_str = self.entry_first_name.get()
        first_name_str = n_data.letter(first_name_str)
        full_name_str = self.entry_last_name.get() + self.entry_first_name.get()
        full_name_print = n_data.letter(full_name_str)

        p_first_name = self.canvas.create_text(800, 209, fill='white', text=first_name_str, font=("Ariel", 25, "italic"))
        p_full_name = self.canvas.create_text(635, 265, fill='white', text=full_name_print, font=("Ariel", 25, "italic"))

        itzurim_str = n_data.letter(list(filter(n_data.itzurim, full_name_str)))
        aiv_print =n_data.letter(list(filter(n_data.aiv, full_name_str)))
        p_itzurim = self.canvas.create_text(825, 320, fill='white', text=itzurim_str, font=("Ariel", 25, "italic"))
        p_aiv = self.canvas.create_text(870, 375, fill='white', text=aiv_print, font=("Ariel", 25, "italic"))

    def bd_split(self):
        self.fulldate_list = [x for x in str(self.entry_date.get())]
        self.day = self.fulldate_list[0:2]
        self.day = map(int, self.day)
        self.day = sum(self.day)

        self.month = self.fulldate_list[2:4]
        self.month = map(int, self.month)
        self.month = sum(self.month)

        self.year = self.fulldate_list[4:]
        self.year = map(int, self.year)
        self.year = sum(self.year)

        self.bd_month = self.fulldate_list[2:4]
        self.bd_month = f"{self.bd_month[0]}{self.bd_month[1]}"
        self.bd_month = int(self.bd_month)

        #חישוב שנה מלאה וחודש מלא
        self.full_list = [x for x in (self.entry_date.get())]
        self.real_month = self.full_list[2:4]
        self.real_month = f"{self.real_month[0]}{self.real_month[1]}"
        self.real_month = int(self.real_month)
        self.real_year = self.full_list[4:]
        self.real_year = f"{self.real_year[0]}{self.real_year[1]}{self.real_year[2]}{self.real_year[3]}"
        self.real_year = int(self.real_year)

        #חישוב פסגות
        self.short_numbers(self.day)
        self.p_day = self.final_number

        self.short_numbers(self.month)
        self.p_month = self.final_number

        self.short_numbers(self.year)
        self.p_year = self.final_number
        self.first_pick = int(self.p_day + self.p_month)
        self.second_pick = int(self.p_day + self.p_year)
        self.third_pick = int(self.first_pick + self.second_pick)
        self.forth_pick = int(self.p_month + self.p_year)

        #חישוב אתגרים

        self.first_challenge = abs(self.p_day - self.p_month)
        self.second_challenge = abs(self.p_day - self.p_year)
        self.third_challenge = abs(self.first_challenge - self.second_challenge)
        self.forth_challenge = self.p_month - self.p_year

        self.full_date_short = self.fulldate_list
        self.full_date_short = map(int, self.full_date_short)
        self.full_date_short = sum(self.full_date_short)

        self.final_number_destiny = self.full_date_short
        self.print_final_details()

    def get_date(self):
        window = Tk()

        self.first_photo = PhotoImage(file="image/first_page.png")
        self.canvas = Canvas(width=1000, height=600)
        self.canvas.create_image(0, 0, image=self.first_photo, anchor="nw")
        self.canvas.grid(row=0, column=0, columnspan=2, sticky=N+S+E+W)

        # Entry
        var = tkinter.StringVar()

        max_len = 8

        def on_write(*args):
            s = var.get()

            if len(s) > max_len:
                var.set(s[:max_len])

        var.trace_variable("w", on_write)

        #Entry
        self.entry_date = Entry(window, font=('Ariel 20'), textvariable=var)
        self.entry_canvas_date = self.canvas.create_window(600, 140, window=self.entry_date, width=135, height=40)
        self.entry_first_name = Entry(window, font=('Ariel 20'), )
        self.entry_canvas_first_name = self.canvas.create_window(600, 230, window=self.entry_first_name, width=135, height=40)
        self.entry_last_name = Entry(window, font=('Ariel 20'))
        self.entry_canvas_last_name = self.canvas.create_window(600, 300, window=self.entry_last_name, width=135, height=40)

        # Button
        self.date_button = Button(window, command=self.bd_split, text="לחץ לאישור", width=10, height=2)
        self.date_button_canvas = self.canvas.create_window(500, 410, window=self.date_button)

        #----------להגדיר אנטר----------
        # window.bind('<Return>', self.bd_split())

        window.mainloop()


from openpyxl import load_workbook

class HandleExcel:

    def __init__(self,file_path,sheet_name):
        self.wb = load_workbook(file_path)
        self.sh = self.wb[sheet_name]

    def __read_titles(self):
        titles = []
        for item in list(self.sh.rows)[0]:  # 遍历第1行当中每一列
            titles.append(item.value)
        return titles

    def read_all_datas(self):
        all_datas = []
        titles = self.__read_titles()
        for item in list(self.sh.rows)[1:]:  # 遍历数据行
            values = []
            for val in item:  # 获取每一行的值
                values.append(val.value)
            res = dict(zip(titles, values))  # title和每一行数据，打包成字典
            all_datas.append(res)
        return all_datas

    def close_file(self):
        self.wb.close()


# 0. 사용할 패키지 불러오기
from keras.utils import np_utils
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation
import numpy as np
import keras
from keras.models import model_from_json
from konlpy.tag import Okt as Twitter
import re
import json

# token2index에서는 '0'외에는 token들로 구성되어 있으며, index2vector값에서는 각 token들 index와, '0'의 index값과 마지막 index로 np.zeros(300)이 들어있다.
class Predict():

    def __init__(self, sent="나는 오늘 화가 많이 났다."):
            # 1.데이터 준비하기
            self.sentence = sent
            # 2. 모델 불러오기
            json_file = open('model.json','r')
            loaded_model_json = json_file.read()
            json_file.close()
            self.model = model_from_json(loaded_model_json)
            self.model.load_weights('Attention_weight.h5')
            #이거 필요없을것 같은데
            self.index2vec = np.fromfile('index2vec.dat',dtype='float32')
#             self.index2vec = np.fromfile('index2vec.dat')
            self.max_sentence = 100
            #"토큰당 인덱스가 들어있는파일"
            with open('token2index.json','r') as dictionary_file:
                self.token2index = json.load(dictionary_file)

    def tokenizer(self):
        token = []
        twitter = Twitter()
        self.sentence = re.sub(r"[^ㄱ-힣a-zA-Z0-9]+", ' ', self.sentence).strip().split()

        token.extend(twitter.pos(str(self.sentence), norm=True, stem=True))
        print(token)
        print(type(token))
        return token

    def t2index(self, t_list):
        index_list = []
        for token in t_list:
            if str(token).replace(" ", "") in self.token2index:
                index_list.append(self.token2index[str(token).replace(" ", "")])
            else:
                index_list.append(self.token2index['0'])
        #padding
        if(len(index_list)> self.max_sentence):
            return index_list[0:99]
        else:
            #token2index의key길이==zero가 있는 index이다. index2vec의 마지막 index와 len(token2index)같으면 된다.
            #-1 지금 붙여논 상황
            index_list.extend([len(self.token2index)-1]*(self.max_sentence-len(index_list)))
            print(len(self.token2index))
            print(len(self.index2vec))
#         100개의 index 토큰으로 분류된 문장 return
        return index_list

from keras import backend as K

if __name__ == '__main__':
    sentence = input("문장을 입력하시오: ")
    predict = Predict(sentence)
    tok = predict.tokenizer()
    #토큰화 시킨 후 토큰화 된 단어를 token2index에 넣을떄, ()의 공백을 없애주어야 한다.
    #1.  in word_tmp_dict
    #2. 또한 x_test의 데이터타입과 token2index의 key데이터 타입이 같은지 확인해 주어야한다.
    # for i in token2index.keys(): print(type(i)), tuple(x_test) or str(x_test)
    x_test = predict.t2index(tok)
    #3. token2index파트에서, 전해진 tok 데이터 타입이 int가 되도록 해주어, index2vec의 index데이터 타입과 같도록 만들어준다.
    #4. 넘파이화 시켜주어서, 이후 plot 찍어줄수 있도록한다.
    x_test = np.asarray([x_test])
#     print(x_test)
#     print(x_test.shape)
#     # 3. 모델 사용하기
#     #5. yhat이 [0.4,0.8,0.9.....]이런식으로 나오는데, threshold(0.5)를 넘기면 모두 1이 되도록 만들어주고 그게 아니면 0이되도록 변환시켜준다.
#     yhat = predict.model.predict(x_test,batch_size=1)
    yhat = predict.model.predict(x_test)
    yhat = yhat[0]

    #yhat중 1인 녀석 index를 뽑아낸다.
    columns = ["분노","기대","혐오","두려움","기쁨","사랑","낙관","비관","슬픔","놀라움","믿음"]
    y_index = list()
    for i,e in enumerate(yhat):
        y_index = [i for i, e in enumerate(yhat) if e > 0.3]
    for i in y_index:
        print("'{0}'가{1:.2f}% 발현되었습니다.".format(str(columns[i]),(yhat[i])*100))


file1 = open('.\\2_day\\text.txt', 'r')
Lines = file1.readlines()

strategy = { "A": 0, "X": 0, "B": 1, "Y": 1, "C": 2, "Z": 2 }
count = 0

for line in Lines:
    elf, resault = line.replace("\n", "").split(" ")
    elf = strategy[elf] -1 #-1 protože změna logiky
    resault = strategy[resault]

    count += resault * 3 #lineární průběh => X == prohra, Y == remíza a Z == výhra
    count += ((resault + elf) % 3) + 1  # podle výsledku vím jakou kartu jsem hrál

print(count)


import pprint
import yfinance as yf
import matplotlib.pyplot as plt
import wallstreet as ws

# CALLS
callsimpv = []
callsbid = []
callsask = []
callschange = []

# PUTS
putsimpv = []
putsbid = []
putsask = []
putschange = []

# STRIKE PRICE
StrikeC = []
StrikeP = []

ticker = input('Choose a ticker: ').upper()
print(yf.Ticker(ticker).options)
expiry1 = input('Choose an expiry: ')
try:
    stock = yf.Ticker(ticker.upper())
    expiry = stock.option_chain(expiry1)
    Strike_priceC = expiry.calls['strike']
    Strike_priceP = expiry.puts['strike']

    # Calls
    calls_impliedv = expiry.calls['impliedVolatility']
    calls_bid = expiry.calls['bid']
    calls_ask = expiry.calls['ask']
    calls_change = expiry.calls['change']

    # Puts
    puts_impliedv = expiry.puts['impliedVolatility']
    puts_bid = expiry.puts['bid']
    puts_ask = expiry.puts['ask']
    puts_change = expiry.puts['change']

    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
    fig.suptitle('Options Data ' + ticker + ' $' + str(ws.Stock(ticker).price))

    # CALLS
    for x in range(len(calls_impliedv)):
        callsimpv.append(calls_impliedv[x])
        StrikeC.append(Strike_priceC[x])
        callsbid.append(calls_bid[x])
        callsask.append(calls_ask[x])
        callschange.append(calls_change[x])

    # PUTS
    for i in range(len(puts_impliedv)):
        putsimpv.append(puts_impliedv[i])
        StrikeP.append(Strike_priceP[i])
        putsbid.append(puts_bid[i])
        putsask.append(puts_ask[i])
        putschange.append(puts_change[i])

    # CALLS
    # Bid/Ask
    ax1.set_title('CALLS Bid/Ask')
    ax1.plot(StrikeC, callsbid)
    ax1.plot(StrikeC,callsask)
    ax1.legend(["Bid", "Ask"])

    ax3.set_title('CALLS Change%')
    ax3.plot(StrikeC, callschange)
    ax3.legend(["Change%"])

    # PUTS
    # Bid/Ask
    ax2.set_title('PUTS Bid/Ask')
    ax2.plot(StrikeP, putsbid)
    ax2.plot(StrikeP, putsask)
    ax2.legend(["Bid", "Ask"])

    ax4.set_title('PUTS Change%')
    ax4.plot(StrikeP, putschange)
    ax4.legend(["Change%"])

    # function to show the plot
    plt.show()
except Exception:
    print('Choose an valid ticker')


from opentrons import protocol_api

metadata = {
    'protocolName': 'Wash Tips',
    'author': 'Will <wobrien@neb.com>',
    'apiLevel': '2.13'
}

#list of functionss which wash tip in reservoir

def run(ctx):
    #SET UP

    #300 ul pipette and tips [RIGHT MOUNT, SLOT 11]
    tips300 = [ctx.load_labware("opentrons_96_tiprack_300ul", 11, "Tips300")]
    m300 =ctx.load_instrument('p300_multi_gen2', 'right', tip_racks = tips300)
    #20 ul pipette and tips [LEFT MOUNT, SLOT 4]
    tips20 = [ctx.load_labware("opentrons_96_tiprack_20ul", 4, "Tips20B")]
    m20 = ctx.load_instrument('p20_multi_gen2', 'left', tip_racks = tips20)

    #reservoir [Slot 3]
    reservoir = ctx.load_labware("agilent_1_reservoir_290ml", 3, "Water")

    #wash command
    def wash20():
        m20.mix(10, 15, reservoir.wells()[0], rate = 2.95)
    def wash300():
        m300.mix(10, 150, reservoir.wells()[0], rate = 2.95)
    m20.pick_up_tip()
    wash20()
    m300.pick_up_tip()
    wash300()


import sys
input = sys.stdin.readline

max = x = y = 0

for i in range(9):
    arr = list(map(int, input().split()))
    for j in range(9):
        if arr[j] == 100:
            max = arr[j]
            x = i
            y = j
            break
        if arr[j] > max:
            max = arr[j]
            x = i
            y = j
    if max == 100:
        break
print(max)
print(x+1, y+1)