import os
import io
import codecs
import sys
import html
import time
import sublime
import sublime_plugin
import subprocess
import shutil
import signal
import threading
from subprocess import Popen, PIPE, STDOUT
from os import path

version = '1.3.2'  # build 0
os.environ["PACKAGE_VERSION"] = version

from .imports.utils import * # should be imported after environ["PACKAGE_VERSION"] is set
new_deployment = (not os.path.isdir(path.join(bin_dest+'cs-script_v'+version)))

# -----------------
bash_string = r'''#!/bin/bash
dotnet /home/user/.config/sublime-text/Packages/User/cs-script/cs-script_v1.2.3/cscs.dll -help > /home/user/.config/sublime-text/Packages/User/cs-script/help.txt
'''
# output = subprocess.check_output(bash_string, shell=True, executable='/bin/bash')
# -----------------

if sys.version_info < (3, 3): raise RuntimeError('CS-Script.ST3 works with Sublime Text 3 only')

# -------------------------
def is_script_file(file):
    return file.endswith(".cs") or file.endswith(".csx")

def settings():
    return sublime.load_settings("cs-script.sublime-settings")

def save_settings():
    return sublime.save_settings("cs-script.sublime-settings")

def on_plugin_loaded():
    Runtime.init(version, new_deployment)
    check_environment(False);
    print_config()

# -------------------------
class CodeViewTextCommand(sublime_plugin.TextCommand):
    # -----------------
    def is_enabled(self):
         return is_csharp(self.view)
    # -----------------
    def is_visible(self):
        panel_name = self.view.window().active_panel()
        if panel_name and panel_name == 'output.CS-Script':
             panel = self.view.window().find_output_panel(panel_name[len('output.'):])
             return not(panel is not None and panel.id() == self.view.id())
        else:
            return True

# -------------------------
class csscript_check_deployment(sublime_plugin.TextCommand):
    def run(self, edit):
        check_environment(True)
# -------------------------
def clear_old_versions_but(version):

    if new_deployment:
        try:
            if os.name == 'nt':
                os.system('taskkill /f /im VBCSCompiler.exe') # stop roslyn server if it is runningif os.name == 'nt':
                os.system('taskkill /f /im syntaxer.exe')     # stop syntaxer
        except:
            pass

    def delete_file(file):
        if os.path.isfile(file):
            os.remove(file)

    # old cs-script engine, prior .NET 6 migration
    delete_file(path.join(bin_dest, 'cscs.exe'))
    delete_file(path.join(bin_dest, 'css_config.xml'))
    delete_file(path.join(bin_dest, 'CSSRoslynProvider.dll'))

    sub_dirs = [name for name in os.listdir(bin_dest)
           if os.path.isdir(os.path.join(bin_dest, name))]

    for dir in sub_dirs:
        if dir.startswith('syntaxer') and not dir.endswith(version):
            try:
                shutil.rmtree(path.join(bin_dest, dir))
            except:
                pass

    for dir in sub_dirs:
        if dir.startswith('cs-script') and not dir.endswith(version):
            try:
                shutil.rmtree(path.join(bin_dest, dir))
            except:
                pass
# -------------------------
def ensure_default_config(csscriptApp):

    config_file = path.join(path.dirname(Runtime.cscs_path), 'css_config.xml')

    if not path.exists(config_file):
        subprocess.Popen(to_args([Runtime.cscs_path, '-config:create']),
                         stdout=subprocess.PIPE,
                         cwd=path.dirname(Runtime.cscs_path),
                         shell=True).wait()

        updated_config = ''

        with open(config_file, "r") as f:
            updated_config  = f.read()

        if os.name == 'nt':
            updated_config = updated_config.replace("<useAlternativeCompiler></useAlternativeCompiler>",
                                                    "<useAlternativeCompiler>CSSRoslynProvider.dll</useAlternativeCompiler>")

            need_explicit_tuple_ref = False
            if need_explicit_tuple_ref:
                updated_config = updated_config.replace("</defaultRefAssemblies>",
                                                        " %syntaxer_dir%"+os.sep+"System.ValueTuple.dll</defaultRefAssemblies>")
            else:
                updated_config = updated_config.replace(" %syntaxer_dir%"+os.sep+"System.ValueTuple.dll", "")

            updated_config = updated_config.replace("<roslynDir></roslynDir>",
                                                    "<roslynDir>%syntaxer_dir%</roslynDir>")

        with open(config_file, "w") as file:
            file.write(updated_config)

    else:

        # update existing config to be compatible with the current cscs.exe
        if os.getenv("new_deployment") == 'true':
            with open(config_file, "r") as f:
                updated_config  = f.read()
            updated_config = updated_config.replace("%syntaxer_dir%"+os.sep+"System.ValueTuple.dll", "")
            with open(config_file, "w") as file:
                file.write(updated_config)

# -------------------------
def ensure_default_roslyn_config(csscriptApp):
    if os.getenv("new_deployment") == 'true':
        if os.name == 'nt':
            subprocess.Popen(to_args([Runtime.cscs_path, '-config:set:RoslynDir="'+current_bin_dest+'"']),
                             stdout=subprocess.PIPE,
                             cwd=path.dirname(Runtime.cscs_path),
                             shell=True).wait()
            subprocess.Popen(to_args([Runtime.cscs_path, '-config:set:useAlternativeCompiler=CSSRoslynProvider.dll']),
                             stdout=subprocess.PIPE,
                             cwd=path.dirname(Runtime.cscs_path),
                             shell=True).wait()

# -------------------------

def deploy_shadow_dir(src_subdir, dest_subdir):

    def copy_and_overwrite(from_path, to_path):
        if os.path.exists(to_path):
            shutil.rmtree(to_path)
        shutil.copytree(from_path, to_path)

    if not path.exists(bin_dest):
        os.makedirs(bin_dest)

    dest_dir = bin_dest
    if dest_subdir:
        dest_dir = path.join(dest_dir, dest_subdir)

    if path.exists(dest_dir):
        return
    else:
        os.makedirs(dest_dir)

    src = path.join(bin_src, src_subdir)
    dest = path.join(bin_dest, dest_subdir)

    try:
        # print('deploying', dest)
        copy_and_overwrite(src, dest)
    except Exception as ex :
        print('deploy_shadow_bin', ex)
        pass
    return dest

    shutil.copytree(dir_name, dest)

deploy_shadow_dir('cs-script',"cs-script_v"+version)
deploy_shadow_dir('cs-syntaxer', "syntaxer_v"+version)
clear_old_versions_but(version)

showTooltipOverGutter = settings().get('show_tooltip_over_gutter', True)
# -------------------------
def print_config():

    print('----------------')
    print('cscs: ', Runtime.cscs_path)
    syntaxer_print_config()
    print('syntaxcheck_on_save: ', settings().get('syntaxcheck_on_save', True))
    print('server_autostart: ', settings().get('server_autostart', True))
    print('----------------')
    # start_syntax_server()

    sublime.status_message('Starting syntaxer server...')

    args = ['dotnet']
    args.append(Runtime.syntaxer_path)
    args.append('-listen')
    args.append('-port:'+str(Runtime.syntaxer_port))
    args.append('-timeout:3000')
    args.append('-client:{0}'.format(os.getpid()))
    args.append('-cscs_path:{0}'.format(Runtime.cscs_path))
    args = to_args(args)

    start = time.time()
    subprocess.Popen(args, shell=True)
    print('>', args)
    print('> Syntaxer server started:', time.time()-start, 'seconds')

# -------------------------
from .imports.syntaxer import *
from .imports.setup import *

csscript_setup.version = version

# =================================================================================
# TODO
# Detect upgrade and fresh install for showing readme/help
# csscript_execute_and_wait should only be available on windows
# =================================================================================
formatted_views = {}
def is_formatted(view):
    if view.id() in formatted_views.keys():
        last_format_time = formatted_views[view.id()]
        return time.time() - last_format_time < 2
    return False

def mark_as_formatted(view):
    formatted_views[view.id()] = time.time()

def is_sdk_available():
    try:

        def onOutput(line):
            global result
            result =  line.strip()

        execute(['dotnet', "--list-sdks"], onOutput)

        # 6.0.101 [C:\Program Files\dotnet\sdk]
        # 6.0.201 [/snap/dotnet-sdk/158/sdk]
        print(result)
        return len(result) > 0

    except Exception as e:
        print(e)
        return False
# =================================================================================
# C#/CS-Script plugin "new script" service
# =================================================================================
class csscript_new(sublime_plugin.TextCommand):
    # -----------------
    def run(self, edit):
        backup_file = None
        if os.path.exists(new_file_path):
            backup_file = new_file_path+'.bak'
            if os.path.exists(backup_file):
                os.remove(backup_file)
            os.rename(new_file_path, backup_file)

        content = ''
        if backup_file:
            content = '// The previous content of this file has been saved into: '+backup_file+' \n\n'

        sample_type = ''
        if is_sdk_available():
            content += "//css_engine csc\n"
            sample_type = 'toplevel'
        else:
            sample_type = 'console'

        subprocess\
            .Popen(to_args(['dotnet', Runtime.cscs_path, '-new:'+sample_type, new_file_path]), stdout=subprocess.PIPE, shell=True)\
            .wait()

        with open(new_file_path, "r") as f:
            content = content + f.read()

        with open(new_file_path, "w") as file:
            file.write(content)

        if os.path.exists(new_file_path):
            sublime.active_window().open_file(new_file_path)
# =================================================================================
# C#/CS-Script plugin help service
# =================================================================================
class csscript_help(sublime_plugin.TextCommand):
    # -----------------
    def run(self, edit):
        file = csscript_setup.prepare_readme()
        if os.path.exists(file):
            sublime.active_window().open_file(file)
        else:
            sublime.error_message('Cannot find '+file)

# =================================================================================
# C#/CS-Script CS-Script help service
# =================================================================================
class csscript_css_help(sublime_plugin.TextCommand):
    # -----------------
    def run(self, edit):

        if is_linux() and settings().get('allow_large_stdout_on_linux', False) != True:
            execute_in_terminal(["dotnet", Runtime.cscs_path, "-help"])
            output_view_show(out_panel)
            output_view_clear(out_panel)
            output_view_write_line(out_panel, "It has been observed that Sublime Text Python interpreter (on some specs of Linux in virtual environments) exhibits difficulties "\
                                              "capturing subprocess output of a significant size. Thus the automatic generation of the documentation is ST is disabled.")
            output_view_write_line(out_panel, "You can access CS-Script help by executing the following manual command in the terminal:")
            output_view_write_line(out_panel, "")
            output_view_write_line(out_panel, "dotnet "+Runtime.cscs_path+" -help")
            output_view_write_line(out_panel, "")
            output_view_write_line(out_panel, "You can stil enable the automatic documentation generation by setting the plugin setting `allow_large_stdout_on_linux` to `true`.")

            # The next line works perfectly in terminal and in ST
            # retval = os.system('ls -l > /home/user/.config/sublime-text/Packages/User/cs-script/help.txt ')

            # The next 3 lines work perfectly in terminal but none in ST
            # retval = os.system('dotnet /home/user/.config/sublime-text/Packages/User/cs-script/cs-script_v1.2.3/cscs.dll -help -out:/home/user/.config/sublime-text/Packages/User/cs-script/help.txt ')
            # retval = os.system('dotnet /home/user/.config/sublime-text/Packages/User/cs-script/cs-script_v1.2.3/cscs.dll -help > /home/user/.config/sublime-text/Packages/User/cs-script/help.txt ')
            # retval = os.system('dotnet /home/user/.config/sublime-text/Packages/User/cs-script/cs-script_v1.2.3/cscs.dll -help ')

            # The next line aslso works perfectly in terminal and in ST. The only difference between `--version` and `-help` is the larger amout of the output for `-help`
            # retval = os.system('dotnet /home/user/.config/sublime-text/Packages/User/cs-script/cs-script_v1.2.3/cscs.dll --version > /home/user/.config/sublime-text/Packages/User/cs-script/help.txt')
            # output_view_write_line(out_panel, str(retval))

            return

        file = os.path.join(plugin_dir, 'cs-script.help.txt')

        with open(file, "w") as out:
            def onOutput(line): out.write(line)

            execute(['dotnet', Runtime.cscs_path, '-help'], onOutput)

        if os.path.exists(file):
            sublime.active_window().open_file(file)
        else:
            sublime.error_message('Cannot find '+file)
# =================================================================================
# C#/CS-Script syntaxer restart service
# =================================================================================
class settings_listener(sublime_plugin.EventListener):
    hooked = False
    # -----------------
    def on_activated(self, view):
        if not settings_listener.hooked:
            settings_listener.hooked = True

            on_plugin_loaded()

            self.callback()
            os.environ['CSSCRIPT_SYNTAXER_PORT'] = str(Runtime.syntaxer_port)
            settings().add_on_change("cscs_path", self.callback)
            settings().add_on_change("server_port", self.on_port_changed)

    def on_port_changed(self):

        # may be fired when setting are not available yet
        try:
            port = settings().get('server_port', 18000)
            if Runtime.syntaxer_port != port:
                Runtime.syntaxer_port = port
                os.environ['CSSCRIPT_SYNTAXER_PORT'] = str(Runtime.syntaxer_port)
        except :
            pass

    def callback(self):
        sublime.set_timeout_async(self.process_settings_change, 100)

    def process_settings_change(self):

        # may be fired when setting are not available yet
        try:
            if Runtime.cscs_path != settings().get('cscs_path', '<none>'):
                read_engine_config()

                # sublime.error_message('About to send '+Runtime.cscs_path)
                set_engine_path(Runtime.cscs_path)

                if settings().get('suppress_embedded_nuget_execution', False):
                    # the default nuget app on Linux (e.g. Mint 18) is incompatible with std.out redirection.
                    # This is valid for both both Python and .NET apps hosted by ST3. So suppress execution of 'nuget'
                    # by cscs.exe internally for resolving packages.
                    if os.name != 'nt':
                        os.environ["NUGET_INCOMPATIBLE_HOST"] = 'true'
                else:
                    try:
                        os.unsetenv('NUGET_INCOMPATIBLE_HOST')
                    except Exception as e:
                        pass

                ensure_default_config(Runtime.cscs_path)
                ensure_default_roslyn_config(Runtime.cscs_path)

                if os.getenv("new_deployment") != 'true' and os.getenv("engine_preloaded") != 'true':
                    os.environ["engine_preloaded"] = 'true'
                    # Preloading only improves the initial overhead for compiling but not for the intellisense.
                    # Important: must to wait a bit to allow Roslyn binaries to be done copied (if they ar being moved)
                    sublime.set_timeout(preload_engine, 5000)

        except:
            pass

# =================================================================================
# C#/CS-Script completion service
# =================================================================================
class csscript_listener(sublime_plugin.EventListener):
    test_count = 0
    suppress_post_save_checking = False
    post_save_jobs = []
    # -----------------
    def __init__(self, *args, **kwargs):
        sublime_plugin.EventListener.__init__(self, *args, **kwargs)
    # -----------------
    def on_activated(self, view):
        pass
    # -----------------
    def on_window_command(self, vindow, command_name, args):
        pass
    # -----------------
    def on_hover(self, view, point, hover_zone):
        if is_output_panel(view) and view == sublime.active_window().find_output_panel(out_panel):
            csscript_show_tooltip(view, point).do_output_panel()
        elif is_csharp(view):
            if hover_zone == sublime.HOVER_TEXT:
                csscript_show_tooltip(view, point).do()
            if showTooltipOverGutter and hover_zone == sublime.HOVER_GUTTER:
                csscript_show_tooltip(view, point).do_gutter()
    # -----------------
    def on_post_text_command(self, view, command_name, args):
        if command_name == 'drag_select':
            if is_output_panel(view):
                if 'by' in args.keys() and args['by'] == 'words':
                    try:
                        point = view.sel()[0].begin()
                        line_region = view.line(point)
                        line_text = view.substr(line_region)

                        view.sel().clear()
                        view.sel().add(line_region)

                        sublime.status_message('Navigating to clicked item...')

                        navigate_to_file_ref(line_text)
                    except:
                        pass
    # -----------------
    def on_load_async(self, view):
        csscript_syntax_check.show_errors()

        # view.assign_syntax('Packages/C#/C#.tmLanguage')
        # view.assign_syntax('Packages/Text/Plain text.tmLanguage')
    # -----------------
    def on_modified(self, view):

        if is_csharp(view):
            # >>> view.scope_name(view.sel()[0].begin())

            # string scope
            # 'source.cs meta.class.source.cs meta.class.body.source.cs meta.method.source.cs meta.method.body.source.cs meta.method-call.source.cs string.quoted.double.source.cs '

            # comment scope
            # 'source.cs meta.class.source.cs meta.class.body.source.cs meta.method.source.cs meta.method.body.source.cs comment.line.double-slash.source.cs '

            # comment.line.double-slash.source.cs
            # string.quoted.double.source.cs
            point = view.sel()[0].begin()
            scope = view.scope_name(point)
            inside_of_string = "string.quoted" in scope
            inside_of_comment = "comment.line" in scope or "comment.block" in scope

            if not inside_of_string and not inside_of_comment:
                typed_char = view.substr(point-1)
                if '.' == typed_char:
                    if settings().get('auto_trigger_autocomplete', True):
                        view.window().run_command("auto_complete")
                elif ('(' == typed_char or ',' == typed_char) :
                    if settings().get('auto_trigger_tooltip', True):
                        view.window().run_command("csscript_pop_tooltip")
    # -----------------
    def on_post_save(self, view):
        # if not is_csharp(view):m
        #     print('---------------')
        # clear_console()
        # sublime.log_commands(True)

        if is_csharp(view):
            # print('> on_post_save')

            # view may be engaged in 'focus changing' activity (e.g. CodeMap)
            # for i in range(5):
            #     if not is_csharp(sublime.active_window().active_view()):
            #         time.sleep(1)

            active_view = sublime.active_window().active_view()
            if active_view == view:
                if not is_formatted(view) and settings().get('format_on_save', True):
                    # print('> formatting')
                    mark_as_formatted(view)
                    view.run_command("csscript_format_code")
                    view.run_command("save")
                elif settings().get('syntaxcheck_on_save', True):
                    # print('>>>> syntax check')
                    view.window().run_command("csscript_syntax_check", {'skip_saving':True})
    # -----------------
    def is_enabled(self):
        return is_csharp(sublime.active_window().active_view())
    # -----------------
    def on_query_completions(self, view, prefix, locations):
        curr_doc = view.file_name()

        if is_script_file(curr_doc):

            completions = []

            if not is_valid_selection(view):
                sublime.status_message('Incompatible selection')
                return completions

            (curr_doc, location, as_temp_file) = get_saved_doc(view)

            response = send_completion_request(curr_doc, location)
            if as_temp_file:
                os.remove(curr_doc)

            completions = self.parse_response(response)
            if completions:
                return (completions, sublime.INHIBIT_EXPLICIT_COMPLETIONS | sublime.INHIBIT_WORD_COMPLETIONS)
    # -----------------
    def parse_response(self, response):

        if not response:
            return None

        completions = []
        error = None

        for line in response.split('\n'):
            if line.startswith('<error>'):
                error = "Error: cannot get C# completion from the syntax server\n"
            if not error:
                parts = line.strip().split('|')
                if len(parts) == 2:
                    completions.append((parts[0], parts[1]))
            else:
                error += line.replace('<error>', '')

        if error:
            print(error)

        return completions

# =================================================================================
# CS-Script code formatter service
# =================================================================================
class csscript_show_config(sublime_plugin.TextCommand):
    # -----------------
    def run(self, edit):
        run_cscs(\
            ['-config'], \
            handle_line = lambda line: output_view_write_line(out_panel, line), \
            on_done=lambda: sublime.show(1, 1), \
            header= 'CS-Script configuration (see `https://github.com/oleg-shilo/cs-script/wiki/CS-Script---Command-Line-Interface#-configoption`)')

# =================================================================================
# CS-Script code formatter service
# =================================================================================
class csscript_format_code(CodeViewTextCommand):
    # -----------------
    def run(self, edit):
        mark_as_formatted(self.view)
        sublime.status_message('Formatting script "'+self.view.file_name()+'"')

        # position in text and in file mey not be the same depending on line endings type
        text_location = -1
        if len(self.view.sel()) > 0:
            text_location = self.view.sel()[0].begin()

        (curr_doc, file_location, as_temp_file) = get_saved_doc(self.view)

        response = send_formatting_request(curr_doc, file_location)

        if as_temp_file:
            os.remove(curr_doc)

        if response:
            if response.startswith('<error>'):
                print('Formatting error:', response.replace('<error>', ''))
            else:
                parts = response.split('\n', 1)

                new_file_location = int(parts[0])
                formatted_code = parts[1]

                new_text_location = to_text_pos(formatted_code, new_file_location)
                new_text = formatted_code.replace('\r', '')

                self.view.replace(edit, sublime.Region(0, self.view.size()), new_text)
                # with open(self.view.file_name(), "w") as file:
                #     file.write(formatted_code)

                # surprisingly mapping of selection is not required. ST3 does it by itself
                # self.view.sel().clear()
                # self.view.sel().add(sublime.Region(new_text_location, new_text_location))

                # print('formatting done')
                # sublime.active_window().run_command("save")

# =================================================================================
# CS-Script async replecement service
# =================================================================================
# DONE
class csscript_resolve_using_async(CodeViewTextCommand):
    # -----------------
    def run(self, edit, **args):
        parts = args['region'].split(',')
        region = sublime.Region(int(parts[0]), int(parts[1]))
        replacement = args['replacement']
        self.view.replace(edit, region, replacement)

# =================================================================================
# CS-Script resolve missing usings service
# =================================================================================
# DONE
class csscript_resolve_using(CodeViewTextCommand):
    inprogress= False
    # -----------------
    def run(self, edit, **args):
        view = self.view
        self.edit = edit
        self.point = None

        self.first_suffestion = False

        if 'point' in args.keys():
            self.point = int(args['point'])
            self.first_suffestion = True

        if self.point == None:
            if len(view.sel()) == 0:
                return
            else:
                self.point = view.sel()[0].begin()

        busy_indicator.show('Analyzing')
        sublime.set_timeout(self.do, 10)
    # -----------------
    def do(self):
        try:
            csscript_resolve_using.inprogress = True

            view = self.view

            word_region = view.word(self.point)
            word_to_resolve = view.substr(word_region)
            # print('view', view.file_name())

            (saved_doc, location, as_temp_file) = get_saved_doc(view)

            response = send_resolve_using_request(saved_doc, word_to_resolve)
            busy_indicator.hide()

            if as_temp_file:
                os.remove(saved_doc)

            if response == '<null>':
                pass
            elif response.startswith('<error>'):
                print(response.replace('<error>', 'CS-Script error: '))
            else:
                items = response.split('\n')
                def on_done(index):
                    if index != -1:
                        for region in self.view.lines(sublime.Region(0,self.view.size())):
                            line  = self.view.substr(region)
                            if not line.startswith("//"):
                                # cannot use 'self.view.replace(self.edit...' as edit is already invalid
                                # so need to start a new command that ctreats 'edit'; Remember, 'edit' cannot be created from code
                                # self.view.replace(self.edit, sublime.Region(start, start), 'using '+items[index]+';'+'\n')
                                region = str(region.begin())+','+str(region.begin())
                                replacement = 'using '+items[index]+';'+'\n'
                                sublime.active_window().run_command("csscript_resolve_using_async", {'replacement':replacement, 'region':region})
                                sublime.active_window().run_command('save')
                                break

                if self.first_suffestion:
                    on_done(0)
                else:
                    self.view.show_popup_menu(items, on_done, 1)

        except Exception as err:
            print(err)

        busy_indicator.hide()
        csscript_resolve_using.inprogress = False

# =================================================================================
# CS-Script tooltip service (for invoking via hotkeys)
# =================================================================================
class csscript_pop_tooltip(CodeViewTextCommand):
    # -----------------
    def run(self, edit):
        self.view.hide_popup()
        point = self.view.sel()[0].begin()

        left_char = self.view.substr(point-1)
        right_char = self.view.substr(point)
        line = self.view.line(point)
        line_str = self.view.substr(line)

        new_point = -1

        if line_str.startswith('//css'):
            csscript_show_tooltip(self.view,  line.begin()).do()
            return

        if left_char == ' ' or left_char == '(' or left_char == ',' or right_char == ')':
            new_point = point - 1
            while self.view.substr(new_point) != '(':
                new_point = new_point - 1
                if new_point < line.begin():
                    new_point = -1
                    break

        if new_point != -1:
            hint = self.view.substr(sublime.Region(point, new_point))
            csscript_show_tooltip(self.view,  new_point+1, hint).do()
        else:
            csscript_show_tooltip(self.view,  point).do()

# =================================================================================
# CS-Script references search service
# =================================================================================
class csscript_find_references(CodeViewTextCommand):
    # -----------------
    def run(self, edit):
        clear_and_print_result_header(self.view.file_name())
        sublime.set_timeout(self.do, 100)

    def do(self):
        (saved_doc, location, as_temp_file) = get_saved_doc(self.view)

        response = send_resolve_references(saved_doc, location)

        if as_temp_file:
            os.remove(saved_doc)
            response = response.replace(saved_doc, self.view.file_name())

        if response == '<null>':
            pass
        elif response.startswith('<error>'):
            print(response.replace('<error>', 'CS-Script error: '))
        else:
            output_view_write_line(out_panel, response)

# =================================================================================
# CS-Script project UI marshaling service (not in use yet)
# =================================================================================
class dispatcher(CodeViewTextCommand):
    queue = []
    # -----------------
    def invoke(edit, action):
        sublime.active_window().run_command('dispatcher', {'action': action})
        pass
    # -----------------
    def run(self, edit, **args):
        if 'action' in args.keys():
            action = args['action']
            try:
                action(self, edit)
            except Exception as ex:
                print("dispatcher:", ex)
        pass

# =================================================================================
# CS-Script project output panel service
# =================================================================================
class csscript_show_output(sublime_plugin.TextCommand):
    # -----------------
    def run(self, edit):
         output_view_show(out_panel)

# =================================================================================
# CS-Script project resolver service
# =================================================================================
class csscript_about(sublime_plugin.TextCommand):
    # -----------------
    def run(self, edit):
        run_cscs(\
            [], \
            handle_line = lambda line: output_view_write_line(out_panel, line), \
            on_done=lambda: sublime.show(1, 1), \
            header= 'CS-Script.ST3 - C# intellisense and execution plugin (v'+version+')')

# =================================================================================
# CS-Script project resolver service
# =================================================================================
# class csscript_load_proj(CodeViewTextCommand):
class csscript_list_proj_files(CodeViewTextCommand):
    # -----------------
    def handle_line(self, line):
        curr_prefix = line.split(':', 1)[0]
        if curr_prefix != self.prefix:
            self.prefix = curr_prefix
            # output_view_write_line(out_panel, '-------')
        output_view_write_line(out_panel, line.replace(curr_prefix+':', curr_prefix+": "))
    # -----------------
    def run(self, edit):
        view = self.view
        self.prefix = 'file'

        sublime.status_message('Checking script deficiencies for "'+self.view.file_name()+'"')

        if self.view.is_dirty():
            sublime.active_window().run_command("save")
            sublime.set_timeout(self.do, 100)
        else:
            self.do()
    # -----------------
    def do(self):

        def on_done():
            output_view_write_line(out_panel, "---------------------\n[Script dependencies]")

        run_doc_in_cscs(['-l', "-proj:dbg"], self.view, self.handle_line, on_done)
# =================================================================================
# CS-Script project (sources only) resolver service
# =================================================================================
class csscript_list_proj_sources(CodeViewTextCommand):
    # -----------------
    def handle_line(self, line):
        curr_prefix = line.split(':', 1)[0]
        if curr_prefix != self.prefix:
            self.prefix = curr_prefix
            # don't separate for now
            # output_view_write_line(out_panel, '-------')

        if not line.endswith('dbg.cs'):
            if curr_prefix.startswith('file'):
                text = line.replace("file:", '')
                if self.prev_line:
                    output_view_write_line(out_panel, item_boxed_prefix + self.prev_line)
                else:
                    output_view_write_line(out_panel, 'Sources')

                self.prev_line = text
    # -----------------
    def run(self, edit):
        view = self.view
        self.prefix = 'file'
        self.prev_line = None
        sublime.status_message('Checking script dependencies for "'+self.view.file_name()+'"')

        if self.view.is_dirty():
            sublime.active_window().run_command("save")
            sublime.set_timeout(self.do, 100)
        else:
            self.do()
    # -----------------
    def do(self):

        def on_done():
            if self.prev_line:
                output_view_write_line(out_panel, last_item_boxed_prefix + self.prev_line)
            self.prev_line = None

            output_view_write_line(out_panel, "---------------------\n[Script sources]")

        run_doc_in_cscs(['-l', "-proj:dbg"], self.view, self.handle_line, on_done)

# =================================================================================
# CS-Script syntax check service
# =================================================================================
# DONE
class csscript_syntax_check(CodeViewTextCommand):
    errors = {}
    instance = None
    # -----------------
    def run(self, edit, **args):

        view = self.view

        sublime.status_message('Checking syntax of "'+view.file_name()+'"')

        if view.is_dirty() and not 'skip_saving' in args.keys():
            sublime.active_window().run_command("save")

        curr_doc = view.file_name()

        clear_and_print_result_header(curr_doc)

        if not path.exists(Runtime.cscs_path):
            print('Error: cannot find CS-Script launcher - ', Runtime.cscs_path)
        elif not curr_doc:
            print('Error: cannot find out the document path')
        else:

            clear_and_print_result_header(curr_doc)

            if '//css_nuget' in view.substr(sublime.Region(0, view.size())):
                output_view_write_line(out_panel, "Resolving NuGet packages may take time...")
            csscript_syntax_check.clear_errors()

            proc = popen_redirect(['dotnet', Runtime.cscs_path, '-l', "-check", curr_doc])

            first_result = True
            for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"):
                line = line.strip()
                if first_result:
                    first_result = False
                    clear_and_print_result_header(curr_doc)

                output_view_write_line(out_panel, line)
                error_info = extract_location(line.strip())
                if error_info:

                    file, line, column, context = error_info
                    file = os.path.abspath(file)
                    if file not in csscript_syntax_check.errors.keys():
                        csscript_syntax_check.errors[file] = []

                    csscript_syntax_check.errors[file].append((line, column, context))
            output_view_write_line(out_panel, "[Syntax check]")

            csscript_syntax_check.show_errors()

    # -----------------
    def has_errors(file):
        for key in csscript_syntax_check.errors.keys():
            if key.lower() == file.lower():
                return True
        return False
    # -----------------
    def get_errors(file, line): # line is 1-based
        errors = []
        for key in csscript_syntax_check.errors.keys():
            if key.lower() == file.lower():
                for error_info in csscript_syntax_check.errors[key]:
                    ln, col, cxt = error_info
                    if ln == line:
                        errors.append(error_info)

                if len(errors) > 0:
                    break

        return errors
    # -----------------
    def clear_errors():

        for view in sublime.active_window().views():
            if view.file_name():
                view.erase_regions("cs-script.errors")

        csscript_syntax_check.errors.clear()
    # -----------------
    def show_errors():
        error_strong_appearence = settings().get('error_strong_appearence', False)
        error_strong_appearence = True
        for file in csscript_syntax_check.errors.keys():
            view = find_file_view(file)
            if view:
                view.erase_regions("cs-script.errors")

                regions = []

                for line, column, context in csscript_syntax_check.errors[file]:
                    pt = view.text_point(line-1, column-1)
                    regions.append(view.word(pt))

                # scope = settings().get('cs-script.syntaxerror_scope')
                # https://www.sublimetext.com/docs/3/scope_naming.html
                # http://docs.sublimetext.info/en/latest/reference/color_schemes.html
                scope = 'invalid'
                icon = 'Packages/'+plugin_name+'/images/error.png'
                # icon = 'Packages/cs-script-sublime/images/error.png'
                if error_strong_appearence:
                    flags = 0
                else:
                    flags = sublime.DRAW_SQUIGGLY_UNDERLINE|sublime.DRAW_NO_FILL|sublime.DRAW_NO_OUTLINE
                view.add_regions("cs-script.errors", regions, scope, icon, flags)

# =================================================================================
# C#/CS-Script kill running script service
# =================================================================================
class csscript_kills_script(sublime_plugin.TextCommand):
    # -----------------
    def is_enabled(self):
         return csscript_execute_and_redirect.running_process != None
    # -----------------
    def is_visible(self):
        panel_name = self.view.window().active_panel()
        if panel_name and panel_name == 'output.CS-Script':
             panel = self.view.window().find_output_panel(panel_name[len('output.'):])
             return panel is not None and panel.id() == self.view.id()
        else:
            return False
    # -----------------
    def run(self, edit):
        if csscript_execute_and_redirect.running_process:
            try:
                pid = csscript_execute_and_redirect.running_process.pid

                sublime.status_message('Terminating...')

                # extremely important to kill all process children
                if os.name == 'posix':
                    subprocess.Popen(['pkill', '-TERM', '-P', str(pid)])
                else:
                    send_pkill_request(pid, 'cscs')
            except:
                pass

# =================================================================================
# CS-Script tooltip service
# =================================================================================
class csscript_show_tooltip():
    def __init__(self, view, point, hint=''):
        self.view = view
        self.point = point
        self.location = point
        self.hint = hint
    # -----------------
    def do_output_panel(self):
        only_over_process_line = True

        if only_over_process_line:
            mouse_line, mouse_column = self.view.rowcol(self.point)
            mouse_region = self.view.line(self.point)
            line = self.view.substr(mouse_region)
        else:
            line_reg = self.view.line(self.view.text_point(2, 0))
            line = self.view.substr(line_reg)

        # print('>>>>>>', line)
        if line.startswith('[Started pid: ') and csscript_execute_and_redirect.running_process:
            try:
                pid = int(line.replace('[Started pid: ','').replace(']',''))

                link =  '<a href="'+str(self.point)+'">Terminate process '+str(pid)+'</a>'
                def terminate(arg):
                    sublime.status_message('Terminating...')
                    self.view.hide_popup()

                    # extremely important to kill all process children
                    if os.name == 'posix':
                        subprocess.Popen(['pkill', '-TERM', '-P', str(pid)])
                    else:
                        send_pkill_request(pid, 'cscs')

                html = """
                    <body id=show-scope>
                        <style>
                        body { margin: 0; padding: 5; }
                         p { margin-top: 0;}
                         </style>
                        %s
                    </body>
                """ % (link)

                self.view.show_popup(html, location=self.point, flags=sublime.HIDE_ON_MOUSE_MOVE_AWAY, max_width=600, on_navigate=terminate)
            except :
                pass
    # -----------------
    def do_gutter(self):
        if csscript_resolve_using.inprogress: return

        file = self.view.file_name()
        line, column = self.view.rowcol(self.point)

        errors = ''
        for line, column, error in csscript_syntax_check.get_errors(file, line+1):
            errors = errors + error.strip() + '<br>'
        html = """
            <body id=show-scope>
                <style>
                body { margin: 0; padding: 5; }
                 p { margin-top: 0;}
                 </style>
                <p>%s</p>
            </body>
        """ % (errors)

        self.view.show_popup(html, location=self.point, flags=sublime.HIDE_ON_MOUSE_MOVE_AWAY, max_width=600)

    # -----------------
    def do(self):

        if csscript_resolve_using.inprogress: return

        # check if we are over the error region
        # NOTE: view.text_point and view.rowcol operate in 0-based units and C# compiler errors are reported in 1-based ones
        mouse_line, mouse_column = self.view.rowcol(self.point)
        mouse_region = self.view.word(self.point)

        for line, column, error in csscript_syntax_check.get_errors(self.view.file_name(), mouse_line+1):
            error_region = self.view.word(self.view.text_point(line-1,column-1))
            if error_region == mouse_region:

                link = ''
                # doesn't work yet
                if 'CS0103' in error:
                    link =  '<a href="'+str(self.point)+'">Try to fix it</a>'

                html = """
                        <body id=show-scope>
                            <style>
                            body { margin: 0; padding: 5; }
                             p { margin-top: 0;}
                             </style>
                            <p>%s</p>
                            %s
                        </body>
                    """ % (error, link)
                # html = '<body id=show-scope>'+error+'</body>'
                self.view.show_popup(html, location=self.point, flags=sublime.HIDE_ON_MOUSE_MOVE_AWAY, max_width=600, on_navigate=lambda x: self._try_to_fix(x))
                return

        (curr_doc, location, as_temp_file) = get_saved_doc(self.view, self.location)
        text = send_tooltip_request(curr_doc, location, self.hint, settings().get('auto_tooltip_light_content', False))

        if as_temp_file:
            os.remove(curr_doc)

        if text:
            if text == '<null>':
                # print('tooltip null')
                return
            elif text.startswith('<error>'):
                print(text.replace('<error>', 'CS-Script generate tooltip error: '))
            else:
                self._show(text, self.point)
    # -----------------
    def _try_to_fix(self, point):
        self.view.hide_popup()
        sublime.set_timeout(lambda : sublime.active_window().run_command("csscript_resolve_using", {'point': point}), 100)
        pass
    # -----------------
    def _go_to_definition(self):

        self.view.hide_popup()

        #unfortunately setting selection doesn't work when invoked from OnPopupHide callback
        # self.view.sel().clear()
        # self.view.sel().add(sublime.Region(self.location, self.location))
        # sublime.active_window().run_command("csscript_goto_definition")

        (curr_doc, location, as_temp_file) = get_saved_doc(self.view, self.location)
        csscript_goto_definition.do(curr_doc, location, as_temp_file)
    # -----------------
    def decorate(self, text):
        text = text.replace('\r', '')

        def deco_section(text, style):
            parts = text.split(':', 1)

            if len(parts) > 1:
                title = parts[0]
                rest = parts[1]
                return '<span class="title">'+title+':</span><span class="'+style+'">'+rest+'</span>\n'
            else:
                return '<span class="'+style+'">'+text+'</span>\n'

        parts = text.split('\n', 1)

        if len(parts) > 1:
            sugnature = deco_section(parts[0],'sig')
            doc = parts[1].strip()
            exc = ''
            pos = doc.find('Exceptions:')
            if pos != -1:
                exc = '<br>'+deco_section(doc[pos:], 'exc')+'<br>\n'
                doc = doc[:pos].strip()

            doc = '<br><span class="doc">'+doc+'</span><br>'
            text = sugnature + doc + exc
        else:
            text = deco_section(text,'sig')

        return text
    # -----------------
    def _show(self, text, text_point):
        import html
        if self.view.is_popup_visible():
            return

        text_value = html.escape(text, quote=False)
        text_value = self.decorate(text_value)
        text_value = text_value.replace('\n', '<br>')

        html = """
            <body id=show-scope>
                <style>
                    body { margin: 0; padding: 5;  }
                    p { margin-top: 0; }
                    a {
                        font-family: sans-serif;
                        font-size: 1.05rem;
                    }
                    span.title {
                        font-style: italic;
                        font-weight: bold;
                        font-size: 1.1rem;
                        padding: 0px;
                    }
                    span.doc {
                        font-style: italic;
                    }
                    span.exc {
                        padding: 15px;
                    }
                    span.sig {
                    }
                </style>
                <p>%s</p>
                $go_def_link
            </body>
        """ % (text_value)

        if self.hint:
            html = html.replace('$go_def_link', '')
        else:
            html = html.replace('$go_def_link','<a href="dummy">Go to Definition</a>')

        # print('---------------------')
        # print(html)
        # print('---------------------')
        flg = sublime.HIDE_ON_MOUSE_MOVE|sublime.HIDE_ON_MOUSE_MOVE_AWAY # testing
        flg = sublime.HIDE_ON_MOUSE_MOVE_AWAY

        self.view.show_popup(html, flags=flg, location=text_point, max_width=600, on_navigate=lambda x: self._go_to_definition())

# =================================================================================
# CS-Script execute service with STD out redirection
# =================================================================================
class csscript_execute_and_redirect(CodeViewTextCommand):
    running_process = None
    # -----------------
    def run(self, edit):

        if csscript_execute_and_redirect.running_process:
            if os.name == 'nt':
                try:
                    os.kill(p.pid, signal.SIGKILL)
                except:
                    pass
            else:
                print("Previous C# script is still running...")
                return

        sublime.status_message('Executing script "'+self.view.file_name()+'"')

        if self.view.is_dirty():
            csscript_listener.suppress_post_save_checking = True
            sublime.active_window().run_command("save")

        curr_doc = self.view.file_name()

        def run():
            script = curr_doc
            clear_and_print_result_header(self.view.file_name())

            def on_process_start(p):
                output_view_write_line(out_panel, '[Started pid: '+str(p.pid)+']', True)
                csscript_execute_and_redirect.running_process = p

            def on_process_output_line(line):
                if line != '':
                    output_view_append(out_panel, line)

            try:
                execute(['dotnet', Runtime.cscs_path, script], on_process_output_line, on_process_start)
            except:
                print("Error Encountered while running script")

            output_view_write_line(out_panel, "[Execution completed]")

            csscript_execute_and_redirect.running_process = None
            return

            # process = subprocess.Popen(to_args(['dotnet', Runtime.cscs_path, script]),  stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)

            # output_view_write_line(out_panel, '[Started pid: '+str(process.pid)+']', True)

            # csscript_execute_and_redirect.running_process = process

            # def process_line(output, ignore_empty = False):
            #     try:
            #         output = output.decode('utf-8').rstrip()
            #         if not ignore_empty or output != '':
            #     except UnicodeDecodeError:
            #         append_output('<Decoding error. You may want to adjust script output encoding in settings.>')
            #         # process.terminate()

            # while process.poll() is None: # may not read the last few lines of output
            #     output = process.stdout.readline()
            #     process_line(output)

            # while (True): # drain any remaining data
            #     try:
            #         output = process.stdout.readline()
            #         if output == b'':
            #             output = process.stderr.readline()
            #             break;
            #         process_line(output, ignore_empty=True)
            #     except :
            #         pass

            # csscript_execute_and_redirect.running_process = None
            # output_view_write_line(out_panel, "[Execution completed]")

        #must be done in a separate thread otherwise line rendering is suspended until process exits
        sublime.set_timeout_async(run, 10)

# =================================================================================
# CS-Script build executable from the script
# =================================================================================
# +done
class csscript_build_exe(CodeViewTextCommand):
    # -----------------
    def run(self, edit):
        view = self.view
        self.prefix = 'file'

        sublime.status_message('Building executable from the script "'+self.view.file_name()+'"')

        if self.view.is_dirty():
            sublime.active_window().run_command("save")
            sublime.set_timeout(self.do, 100)
        else:
            self.do()
    # -----------------
    def do(self):
        script_file = self.view.file_name()
        pre, ext = os.path.splitext(script_file)
        exe_file = pre + '.exe'

        def handle_line(line):
            output_view_write_line(out_panel, line)

        def on_done():
            if path.exists(exe_file):
                output_view_write_line(out_panel,'Script is converted into executable ' + exe_file)
            output_view_write_line(out_panel, "---------------------\n[Build exe]")
            sublime.show(1, 1)

        run_doc_in_cscs(['-l', '-e'], self.view, handle_line, on_done)

# =================================================================================
# CS-Script execute service. Shell remains visible after the process termination
# =================================================================================
# +done
class csscript_execute_and_wait(CodeViewTextCommand):
    # -----------------
    def run(self, edit):

        sublime.active_window().run_command("save")
        curr_doc = self.view.file_name()

        if not path.exists(Runtime.cscs_path):
            print('Error: cannot find CS-Script launcher - ', Runtime.cscs_path)
        else:
            execute_in_terminal(['dotnet', Runtime.cscs_path, '-l', '-wait', curr_doc])

            return

            # older algorithm that may need to be reactivated in the future if the current one stops working on Linux
            if os.name == 'nt':
                os.system('dotnet "' + Runtime.cscs_path + '" -l -wait "'+ curr_doc + '"')
            else:
                # Linux and Mac
                env = os.environ.copy()
                env['SCRIPT_FILE'] = curr_doc

                cwd = os.path.dirname(curr_doc)

                css_command = ' dotnet "' + Runtime.cscs_path + '" -l -wait "'+ curr_doc + '"'

                command = "bash -c \"{0} ; exec bash\"".format(css_command)
                args =[TerminalSelector.get(), '-e', command]

                if 'NUGET_INCOMPATIBLE_HOST' in env:
                    del env['NUGET_INCOMPATIBLE_HOST']

                print(args)
                subprocess.Popen(args, cwd=cwd, env=env)

# =================================================================================
# CS-Script go-to-next-result service
# =================================================================================
class csscript_next_result(sublime_plugin.WindowCommand):
    # -----------------
    def run(self):
        view_name = sublime.active_window().active_panel()
        if not view_name:
            return

        if view_name == 'output.exec':
            self.window.run_command('next_result')
        else:
            if view_name.startswith('output.'):
                view_name = view_name.replace('output.', '')

            view = sublime.active_window().find_output_panel(view_name)

            if not view or not view.window():
                return

            caret_point = view.sel()[0].begin()
            caret_line_region = view.line(caret_point)
            line_regions = view.lines(sublime.Region(0, view.size()))

            next_location_index = -1
            locations = []

            for rg in line_regions:
                line = view.substr(rg).strip()
                info = extract_location(line)
                if info:
                    if next_location_index == -1 and rg == caret_line_region:
                        next_location_index = len(locations)
                    locations.append((rg, line))

            if len(locations) > 0:
                next_location_index = next_location_index + 1

                if next_location_index >= len(locations):
                    next_location_index = 0

                line_region, line_text = locations[next_location_index]

                view.sel().clear()
                view.sel().add(line_region)
                view.run_command('append', {'characters': ''}) # to force repainting the selection

                navigate_to_file_ref(line_text)

# =================================================================================
# CS-Script go-to-definition service
# =================================================================================
class csscript_goto_definition(CodeViewTextCommand):
    # -----------------
    def run(self, edit):
        view = self.view
        curr_doc = self.view.file_name()
        if is_script_file(curr_doc):

            if not is_valid_selection(self.view):
                sublime.status_message('Incompatible selection')
                return

            (curr_doc, location, as_temp_file) = get_saved_doc(view)

            csscript_goto_definition.do(curr_doc, location, as_temp_file)
    # -----------------
    def do(curr_doc, location, as_temp_file):
            response = send_resolve_request(curr_doc, location)

            if as_temp_file:
                os.remove(curr_doc)

            path = csscript_goto_definition.parse_response(response)
            if path:
                fiel_name = os.path.basename(path).split(':')[0].lower()

                if fiel_name.endswith('.dll') or fiel_name.endswith('.exe'):
                    dir_path = os.path.dirname(path)
                    sublime.active_window().run_command('open_dir', { 'dir': dir_path })
                else:
                    sublime.active_window().open_file(path, sublime.ENCODED_POSITION)
    # -----------------
    def parse_response(response):
        if not response:
            return None

        error = None
        fileName = None
        lineNum = None

        for line in response.split('\n'):
            if line.startswith('<error>'):
                error = "Error: cannot resolve C# symbol\n"
            if not error:
                if line.startswith('file:'):
                    fileName = line[5:].strip()
                if line.startswith('line:'):
                    lineNum = line[5:].strip()
            else:
                error += line.replace('<error>', '')

        if error:
            print(error)
        elif fileName:
            if fileName.endswith('.tmp'):
                possible_oriuginal_file = fileName[:-4]
                if os.path.exists(possible_oriuginal_file):
                    fileName = possible_oriuginal_file

            # print("{0}:{1}:0".format(fileName, lineNum))
            return "{0}:{1}:0".format(fileName, lineNum)

# =================================================================================
# CS-Script go-to-definition service
# =================================================================================
# +done
class csscript_show_output_panel(sublime_plugin.WindowCommand):
    # -----------------
    def run(self):
        view = sublime.active_window().active_view()
        if sublime.active_window().active_panel() == 'output.'+out_panel:
            output_view_hide(out_panel)
        else:
            output_view_show(out_panel)


from flask import render_template,redirect,url_for
import flask_login
from . import main
from ..models import Pickup,PickupComments,PickupLikes,PickupDislikes,Interview,InterviewComments,InterviewLikes,InterviewDislikes,Promotion,PromotionComments,PromotionLikes,PromotionDislikes
from .forms import PickupLineForm, InterviewForm, PromotionForm, CommentForm
from flask_login import current_user, login_required

# LANDING PAGE
@main.route('/')
def index():

    '''
    View root page function that returns the index page and its data
    '''
    return render_template('index.html')

# PICKUP LINES PAGE
@main.route('/pickup-lines', methods = ['GET','POST'])
@login_required
def pickup():
    '''
    View root page function that returns the pickup lines page and its data
    '''

    #Create an instance of the PickupLineForm class and name it pickup_line_form
    pickup_line_form = PickupLineForm()

    #The method returns True when the form is submitted and all the data has been verified by the validators
    if pickup_line_form.validate_on_submit():
        # If True we gather the data from the form input fields
        pickupLine = pickup_line_form.pickupLine.data

        #Create a new pickup_line object and save it
        new_pickup_line = Pickup(pickupLine = pickupLine, user=current_user)
        new_pickup_line.save_pickup_line()

        return redirect(url_for('main.pickup'))

    #Get all the Pickup Lines
    pickup_lines = Pickup.get_pickup_lines()

    return render_template('pickup-lines.html', pickup_line_form = pickup_line_form, pickup_lines = pickup_lines)

#PICKUP COMMENTS PAGE
@main.route('/comments/<int:pickup_id>', methods = ['GET','POST'])
@login_required
def comments(pickup_id):
    '''
    View root page function that returns the pickup comments page and its data
    '''

    pline = Pickup.query.filter_by(id = pickup_id).first().pickupLine
    print(pline)
    pline_author = Pickup.query.filter_by(id = pickup_id).first().user.username
    print(pline_author)
    pline_postedDate = Pickup.query.filter_by(id = pickup_id).first().postedDate
    print(pline_postedDate)
    pline_likes = PickupLikes.query.filter_by(pickup_id = pickup_id).count()
    print(pline_likes)
    pline_dislikes = PickupDislikes.query.filter_by(pickup_id = pickup_id).count()
    print(pline_dislikes)

    #Create an instance of the CommentForm class and name it comments_form
    comments_form = CommentForm()

    #The method returns True when the form is submitted and all the data has been verified by the validators
    if comments_form.validate_on_submit():
        #If True we gather the data from the form input fields
        comment = comments_form.comment.data

        #Create a new comment object and save it
        new_comment = PickupComments(comment=comment, user=current_user, pickup_id=pickup_id )
        new_comment.save_comment()

        return redirect(url_for('main.comments',pickup_id=pickup_id))

    #Get all the Comments
    all_comments = PickupComments.get_comments(pickup_id)

    return render_template('pickup-comments.html', comments_form = comments_form, all_comments = all_comments, pickup_id = pickup_id, pline = pline, pline_author = pline_author, pline_postedDate = pline_postedDate, pline_likes = pline_likes, pline_dislikes = pline_dislikes)

#PICKUP LINE UPVOTES
@main.route('/like/<int:id>', methods = ['GET', 'POST'])
def pline_upvotes(id):
    pline_upvotes = PickupLikes.get_pickuplikes(id)

    valid_string = f'{current_user.id}:{id}'

    for pline in pline_upvotes:
        to_str = f'{pline}'

        print(valid_string+" "+to_str)

        if valid_string == to_str:
            return redirect(url_for('main.pickup',id=id))
        else:
            continue

    new_pline_upvote = PickupLikes(user = current_user, pickup_id=id)
    new_pline_upvote.save_pickuplike()

    return redirect(url_for('main.pickup',id=id))

#PICKUP LINE DOWNVOTES
@main.route('/dislike/<int:id>', methods = ['GET', 'POST'])
def pline_downvotes(id):
    pline_downvotes = PickupDislikes.get_pickupdislikes(id)

    valid_string = f'{current_user.id}:{id}'

    for pline in pline_downvotes:
        to_str = f'{pline}'

        print(valid_string+" "+to_str)

        if valid_string == to_str:
            return redirect(url_for('main.pickup',id=id))
        else:
            continue

    new_pline_downvote = PickupDislikes(user = current_user, pickup_id=id)
    new_pline_downvote.save_pickupdislike()

    return redirect(url_for('main.pickup',id=id))

# INTERVIEW PITCHES PAGE
@main.route('/interview', methods = ['GET','POST'])
@login_required
def interview():
    '''
    View root page function that returns the interview pitches page and its data
    '''

    #Create an instance of the InterviewForm class and name it interview_form
    interview_form = InterviewForm()

    #The method returns True when the form is submitted and all the data has been verified by the validators
    if interview_form.validate_on_submit():
        #If True we gather the data from the form input fields
        interview = interview_form.interview.data

        #Create a new interview object and save it
        new_interview = Interview(interview = interview, user=current_user)
        new_interview.save_interview()

        return redirect(url_for('main.interview'))

    #Get all the Interview Pitches
    interview_pitches = Interview.get_interviews()

    return render_template('interview-pitches.html', interview_form = interview_form, interview_pitches = interview_pitches)

#INTERVIEW PITCHES COMMENTS PAGE
@main.route('/interviewcomments/<int:interview_pitch_id>', methods = ['GET','POST'])
@login_required
def interviewComments(interview_pitch_id):
    '''
    View root page function that returns the interview pitch comments page and its data
    '''

    interview_pitch = Interview.query.filter_by(id = interview_pitch_id).first().interview
    print(interview_pitch)
    interview_author = Interview.query.filter_by(id = interview_pitch_id).first().user.username
    print(interview_author)
    interview_postedDate = Interview.query.filter_by(id = interview_pitch_id).first().postedDate
    print(interview_postedDate)
    interview_likes = InterviewLikes.query.filter_by(interview_pitch_id = interview_pitch_id).count()
    print(interview_likes)
    interview_dislikes = InterviewDislikes.query.filter_by(interview_pitch_id = interview_pitch_id).count()
    print(interview_dislikes)

    #Create an instance of the CommentForm class and name it comments_form
    comments_form = CommentForm()

    #The method returns True when the form is submitted and all the data has been verified by the validators
    if comments_form.validate_on_submit():
        #If True we gather the data from the form input fields
        comment = comments_form.comment.data

        #Create a new comment object and save it
        new_comment = InterviewComments(comment=comment, user=current_user, interview_pitch_id=interview_pitch_id )
        new_comment.save_interviewComment()

        return redirect(url_for('main.interviewComments',interview_pitch_id=interview_pitch_id))

    #Get all the Comments
    all_comments = InterviewComments.get_interviewComments(interview_pitch_id)

    return render_template('interview-comments.html', comments_form = comments_form, all_comments = all_comments, interview_pitch_id = interview_pitch_id, interview_pitch = interview_pitch, interview_author = interview_author, interview_postedDate = interview_postedDate, interview_likes = interview_likes, interview_dislikes = interview_dislikes)

#INTERVIEW PITCHES UPVOTES
@main.route('/interviewlike/<int:id>', methods = ['GET', 'POST'])
def interview_upvotes(id):
    interview_upvotes = InterviewLikes.get_interviewlikes(id)

    valid_string = f'{current_user.id}:{id}'

    for interview in interview_upvotes:
        to_str = f'{interview}'

        print(valid_string+" "+to_str)

        if valid_string == to_str:
            return redirect(url_for('main.interview',id=id))
        else:
            continue

    new_interview_upvote = InterviewLikes(user = current_user, interview_pitch_id=id)
    new_interview_upvote.save_interviewlike()

    return redirect(url_for('main.interview',id=id))

#INTERVIEW PITCHES DOWNVOTES
@main.route('/interviewdislike/<int:id>', methods = ['GET', 'POST'])
def interview_downvotes(id):
    interview_downvotes = InterviewDislikes.get_interviewdislikes(id)

    valid_string = f'{current_user.id}:{id}'

    for interview in interview_downvotes:
        to_str = f'{interview}'

        print(valid_string+" "+to_str)

        if valid_string == to_str:
            return redirect(url_for('main.interview',id=id))
        else:
            continue

    new_interview_downvote = InterviewDislikes(user = current_user, interview_pitch_id=id)
    new_interview_downvote.save_interviewdislike()

    return redirect(url_for('main.interview',id=id))

# PROMOTION PITCHES PAGE
@main.route('/promotion', methods = ['GET','POST'])
@login_required
def promotion():
    '''
    View root page function that returns the promotion pitches page and its data
    '''

    #Create an instance of the PromotionForm class and name it promotion_form
    promotion_form = PromotionForm()

    #The method returns True when the form is submitted and all the data has been verified by the validators
    if promotion_form.validate_on_submit():
        #If True we gather the data from the form input fields
        promotion = promotion_form.promotion.data

        #Create a new promotion object and save it
        new_promotion = Promotion(promotion = promotion, user=current_user)
        new_promotion.save_promotion()

        return redirect(url_for('main.promotion'))

    #Get all the Promotion Pitches
    promotion_pitches = Promotion.get_promotions()

    return render_template('promotion-pitches.html', promotion_form = promotion_form, promotion_pitches = promotion_pitches)

#PROMOTION PITCHES COMMENTS PAGE
@main.route('/promotioncomments/<int:promotion_pitch_id>', methods = ['GET','POST'])
@login_required
def promotionComments(promotion_pitch_id):
    '''
    View root page function that returns the promotion pitch comments page and its data
    '''

    promotion_pitch = Promotion.query.filter_by(id = promotion_pitch_id).first().promotion
    print(promotion_pitch)
    promotion_author = Promotion.query.filter_by(id = promotion_pitch_id).first().user.username
    print(promotion_author)
    promotion_postedDate = Promotion.query.filter_by(id = promotion_pitch_id).first().postedDate
    print(promotion_postedDate)
    promotion_likes = PromotionLikes.query.filter_by(promotion_pitch_id = promotion_pitch_id).count()
    print(promotion_likes)
    promotion_dislikes = PromotionDislikes.query.filter_by(promotion_pitch_id = promotion_pitch_id).count()
    print(promotion_dislikes)

    #Create an instance of the CommentForm class and name it comments_form
    comments_form = CommentForm()

    #The method returns True when the form is submitted and all the data has been verified by the validators
    if comments_form.validate_on_submit():
        #If True we gather the data from the form input fields
        comment = comments_form.comment.data

        #Create a new comment object and save it
        new_comment = PromotionComments(comment=comment, user=current_user, promotion_pitch_id=promotion_pitch_id )
        new_comment.save_promotionComment()

        return redirect(url_for('main.promotionComments',promotion_pitch_id=promotion_pitch_id))

    #Get all the Comments
    all_comments = PromotionComments.get_promotionComments(promotion_pitch_id)

    return render_template('promotion-comments.html', comments_form = comments_form, all_comments = all_comments, promotion_pitch_id = promotion_pitch_id, promotion_pitch = promotion_pitch, promotion_author = promotion_author, promotion_postedDate = promotion_postedDate, promotion_likes = promotion_likes, promotion_dislikes = promotion_dislikes)

#PROMOTION PITCHES UPVOTES
@main.route('/promotionlike/<int:id>', methods = ['GET', 'POST'])
def promotion_upvotes(id):
    promotion_upvotes = PromotionLikes.get_promotionlikes(id)

    valid_string = f'{current_user.id}:{id}'

    for promotion in promotion_upvotes:
        to_str = f'{promotion}'

        print(valid_string+" "+to_str)

        if valid_string == to_str:
            return redirect(url_for('main.promotion',id=id))
        else:
            continue

    new_promotion_upvote = PromotionLikes(user = current_user, promotion_pitch_id=id)
    new_promotion_upvote.save_promotionlike()

    return redirect(url_for('main.promotion',id=id))

#PROMOTION PITCHES DOWNVOTES
@main.route('/promotiondislike/<int:id>', methods = ['GET', 'POST'])
def promotion_downvotes(id):
    promotion_downvotes = PromotionDislikes.get_promotiondislikes(id)

    valid_string = f'{current_user.id}:{id}'

    for promotion in promotion_downvotes:
        to_str = f'{promotion}'

        print(valid_string+" "+to_str)

        if valid_string == to_str:
            return redirect(url_for('main.promotion',id=id))
        else:
            continue

    new_promotion_downvote = PromotionDislikes(user = current_user, promotion_pitch_id=id)
    new_promotion_downvote.save_promotiondislike()

    return redirect(url_for('main.promotion',id=id))


"""
Weather stations: search nearby stations.
"""

import bisect
import datetime
import os
import collections
from ftplib import FTP

import pandas as pd
import Geohash
from geopy.distance import vincenty

import util

def load_stations(dir_raw, country='US', state=None, date_weather_end=None):
    """
    Load weather stations in the specified region that are alive before weather_end_time.
    :param country:
    :param state:
    :param date_weather_end:    datetime.datetime
    :return df_stations:        pandas data frame
    """
    station_alive_time = int((datetime.datetime.now() - datetime.timedelta(days=30)).strftime('%Y%m%d'))
    # todo (1) necessary to check station latest report time? (2) notify users about the latest available data
    if date_weather_end:
        station_alive_time = min(station_alive_time, int(date_weather_end.strftime('%Y%m%d')))

    if not os.path.exists(dir_raw):
        os.makedirs(dir_raw)
    stations_csv_file = '%sisd-history.csv' % dir_raw

    file_create_time = 0 if not os.path.isfile(stations_csv_file) \
        else int(datetime.datetime.fromtimestamp(os.path.getmtime(stations_csv_file)).strftime('%Y%m%d'))
    if file_create_time <= station_alive_time:
        util.logger.debug('downloading weather stations updates ...')
        ftp = FTP('ftp.ncdc.noaa.gov')
        ftp.login()
        ftp.cwd('pub/data/noaa/')
        ftp.retrbinary('RETR isd-history.csv', open(stations_csv_file, 'wb').write)
        ftp.quit()

    df = pd.read_csv(stations_csv_file)
    i_alive = df['END'] >= station_alive_time
    i_ctry = df['CTRY'] == country if country else True
    i_state = df['STATE'] == state if state else True
    df_stations = df.loc[i_alive & i_ctry & i_state]
    return df_stations

def search_stations(gps, dir_raw, country='US', state='None', date_end='None', station_num=5, radius_miles=15,
                    station_option='usaf_wban'):
    """
    Search nearby stations for a given GPS.
    :param gps:                 (lat, lng) GPS point to search stations
    :param country:             country to search
    :param state:               state to search
    :param date_end:            only search stations that are alive before date_end
    :param station_num:         number of nearby stations to return
    :param radius_miles:        searching radius
    :return:                    OrderedDict() of station ids, ordered by distance
    """

    df_stations = load_stations(dir_raw, country, state, date_end)

    geo2row = dict()
    for row in xrange(len(df_stations)):
        lat, lng = df_stations.iloc[row][['LAT', 'LON']]
        geo2row[Geohash.encode(lat, lng)] = (row, lat, lng)
    geo_list = sorted(geo2row.keys())

    geo_hash = Geohash.encode(gps[0], gps[1])
    pos = bisect.bisect_left(geo_list, geo_hash)
    row2distance = dict()
    for g in geo_list[max(pos-station_num, 0):min(pos+station_num+1, len(geo_list))]:
        row, lat, lng = geo2row[g]
        row2distance[row] = int(vincenty((lat, lng), gps).miles)
    sorted_rows = sorted(row2distance, key=row2distance.get)

    station2location = collections.OrderedDict()
    for i, row in enumerate(sorted_rows):
        distance = row2distance[row]
        if i >= 1 and distance > radius_miles:
            continue
        if len(station2location) >= station_num:
            continue
        usaf, wban, lat, lng, name = df_stations.iloc[row][['USAF', 'WBAN', 'LAT', 'LON', 'STATION NAME']]
        station2location['%06d-%05d' % (usaf, wban)] = {
            'distance': distance,
            'lat': lat,
            'lng': lng,
            'name': _adjust_station_name(name)
        }

    if station_option is not None:
        # re-sort: prioritize high quality stations
        station_score = {}
        for usaf_wban, location in station2location.items():
            score = 100 - location['distance']
            usaf, wban = usaf_wban.split('-')
            if usaf == '999999':
                score -= 15
            if wban == '99999':
                score -= 10
            if 'airport' in location['name'].lower():
                score += 5
            if 'international' in location['name'].lower() or 'intl' in location['name'].lower():
                score += 30
            station_score[usaf_wban] = score
        station2location = collections.OrderedDict(
            sorted(station2location.items(), key=lambda x: station_score[x[0]], reverse=True))

    return station2location

def _adjust_station_name(name):
    # replace '/' otherwise misunderstood as directory separator
    return name.replace('/', ' ')


import asyncio
from time import sleep

import httpx
from django.http import HttpResponse

# 异步视图 - 无异步或同步任务
async def index(request):
    return HttpResponse("this is django3.* async Django views")

# 异步任务
async def http_call_async():
    for num in range(6):
        await asyncio.sleep(1)
        print(num)
    async with httpx.AsyncClient() as client:
        r = await client.get("https://httpbin.org/")
        print(r)

# 同步任务
def http_call_sync():
    for num in range(6):
        sleep(1)
        print(num)
    r = httpx.get("https://httpbin.org/")
    print(r)

# 异步视图 - 调用异步任务
async def async_view(request):
    loop = asyncio.get_event_loop()
    loop.create_task(http_call_async())
    return HttpResponse('async view call async request')

# 同步视图 - 调用普通同步任务
def sync_view(request):
    http_call_sync()
    return HttpResponse('Blocking HTTP request')

# 导入 同步转异步包
from asgiref.sync import async_to_sync, sync_to_async
# async_to_sync 将异步转同步
# sync_to_async 将同步转异步

# 同步转异步(sync to async)
async def async_with_sync_view(request):
    loop = asyncio.get_event_loop()
    async_function = sync_to_async(http_call_sync)
    loop.create_task(async_function())
    return HttpResponse("Non-blocking HTTP request sync to async")


from decimal import Decimal
from typing import List
import requests
import os

import location

base_url = "https://eu1.locationiq.com"
api_key = os.getenv('GC_KEY')

def encode(postal_address: str) -> (Decimal, Decimal):
    response = requests.get(f'{base_url}/v1/search', params={'key': api_key, 'q': postal_address, 'format': 'json', 'limit': '1'})
    if response.ok:
        data = response.json()[0]
        return Decimal(data['lat']), Decimal(data['lon'])
    else:
        return None, None

def decode(lat: Decimal, lon: Decimal) -> str:
    response = requests.get(f'{base_url}/v1/reverse', params={'key': api_key, 'lat': lat, 'lon': lon, 'format': 'json', 'postaladdress': '1'})
    if response.ok:
        return response.json()['postal_address']
    else:
        raise Exception(response.reason)

def routing_matrix(locations: List[location.Location]) -> List[List]:
    SEP = ';'
    locations_string = SEP.join([f'{location.lon},{location.lat}' for location in locations])
    response = requests.get(f'{base_url}/v1/matrix/driving/{locations_string}', params={'key': api_key})
    if response.ok:
        return response.json()['durations']
    else:
        raise Exception(response.reason)


from django.urls import path

from ..views import order_views

from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns=[

    path("add/",order_views.addOrderItems,name="add_order"),
    path("<str:pk>/",order_views.getOrder,name="get_order"),
]


import cv2 as cv
import numpy as np
from matplotlib.patches import Rectangle
from matplotlib.patches import Circle
from matplotlib import pyplot as plt
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
from mtcnn.mtcnn import MTCNN
import math
import pandas as pd
import os

from PyQt5.QtGui import QImage
from PyQt5 import QtCore, QtGui, QtWidgets
dir_path = 's12/'
dir_path2 = 'pic/'
res = []
res2 = []
for path in os.listdir(dir_path):
    # check if current path is a file
    if os.path.isfile(os.path.join(dir_path, path)):
        res.append(dir_path + path)

for path2 in os.listdir(dir_path2):
    # check if current path is a file
    if os.path.isfile(os.path.join(dir_path2, path2)):
        res2.append(dir_path2 + path2)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(970, 841)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.comboBox = QtWidgets.QComboBox(self.centralwidget)
        self.comboBox.setGeometry(QtCore.QRect(550, 590, 161, 21))
        self.comboBox.setObjectName("comboBox")
        self.Template_but = QtWidgets.QPushButton(self.centralwidget)
        self.Template_but.setGeometry(QtCore.QRect(30, 650, 281, 51))
        self.Template_but.setStyleSheet("font: 16pt \"Lucida Console\";\n"
"background-color: rgb(0, 170, 0);")
        self.Template_but.setObjectName("Template_but")
        self.comboBox_2 = QtWidgets.QComboBox(self.centralwidget)
        self.comboBox_2.setGeometry(QtCore.QRect(170, 590, 151, 21))
        self.comboBox_2.setObjectName("comboBox_2")
        self.result = QtWidgets.QLabel(self.centralwidget)
        self.result.setGeometry(QtCore.QRect(40, 280, 741, 281))
        self.result.setObjectName("result")
        self.tmplate = QtWidgets.QLabel(self.centralwidget)
        self.tmplate.setGeometry(QtCore.QRect(10, 40, 241, 221))
        self.tmplate.setObjectName("tmplate")
        self.pictures = QtWidgets.QLabel(self.centralwidget)
        self.pictures.setGeometry(QtCore.QRect(340, 30, 361, 221))
        self.pictures.setObjectName("pictures")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(300, 230, 101, 21))
        font = QtGui.QFont()
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.label_4.setFont(font)
        self.label_4.setStyleSheet("\n"
"background-color: rgb(85, 170, 255);")
        self.label_4.setFrameShape(QtWidgets.QFrame.Box)
        self.label_4.setLineWidth(-1)
        self.label_4.setObjectName("label_4")
        self.label_5 = QtWidgets.QLabel(self.centralwidget)
        self.label_5.setGeometry(QtCore.QRect(20, 10, 101, 21))
        font = QtGui.QFont()
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.label_5.setFont(font)
        self.label_5.setStyleSheet("\n"
"background-color: rgb(85, 170, 255);")
        self.label_5.setFrameShape(QtWidgets.QFrame.Box)
        self.label_5.setLineWidth(-1)
        self.label_5.setObjectName("label_5")
        self.label_6 = QtWidgets.QLabel(self.centralwidget)
        self.label_6.setGeometry(QtCore.QRect(350, 10, 101, 21))
        font = QtGui.QFont()
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.label_6.setFont(font)
        self.label_6.setStyleSheet("\n"
"background-color: rgb(85, 170, 255);")
        self.label_6.setFrameShape(QtWidgets.QFrame.Box)
        self.label_6.setLineWidth(-1)
        self.label_6.setObjectName("label_6")
        self.label_7 = QtWidgets.QLabel(self.centralwidget)
        self.label_7.setGeometry(QtCore.QRect(160, 550, 181, 21))
        font = QtGui.QFont()
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.label_7.setFont(font)
        self.label_7.setStyleSheet("\n"
"background-color: rgb(85, 170, 255);")
        self.label_7.setFrameShape(QtWidgets.QFrame.Box)
        self.label_7.setLineWidth(-1)
        self.label_7.setObjectName("label_7")
        self.label_8 = QtWidgets.QLabel(self.centralwidget)
        self.label_8.setGeometry(QtCore.QRect(550, 550, 181, 21))
        font = QtGui.QFont()
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.label_8.setFont(font)
        self.label_8.setStyleSheet("\n"
"background-color: rgb(85, 170, 255);")
        self.label_8.setFrameShape(QtWidgets.QFrame.Box)
        self.label_8.setLineWidth(-1)
        self.label_8.setObjectName("label_8")
        self.Viola_but = QtWidgets.QPushButton(self.centralwidget)
        self.Viola_but.setGeometry(QtCore.QRect(340, 650, 281, 51))
        self.Viola_but.setStyleSheet("font: 16pt \"Lucida Console\";\n"
"background-color: rgb(0, 170, 0);")
        self.Viola_but.setObjectName("Viola_but")
        self.Sim_but = QtWidgets.QPushButton(self.centralwidget)
        self.Sim_but.setGeometry(QtCore.QRect(670, 650, 281, 51))
        self.Sim_but.setStyleSheet("font: 16pt \"Lucida Console\";\n"
"background-color: rgb(0, 170, 0);")
        self.Sim_but.setObjectName("Sim_but")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 970, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

        ##кнопка для темплейт метода
        self.Template_but.clicked.connect(self.showPicts)

        ##кнопка для виола  метода
        self.Viola_but.clicked.connect(self.showViola)

        ##кнопка для вывода симметрии
        self.Sim_but.clicked.connect(self.showSim)
        ##add items
        self.comboBox_2.addItems(res)

        ##add items
        self.comboBox.addItems(res2)
        ##Вывод изображение из выпадающего окна
        pixmap = QtGui.QPixmap(self.comboBox.currentText())
        pixmap2 = pixmap.scaled(256, 256, QtCore.Qt.KeepAspectRatio)
        self.tmplate.setPixmap(pixmap2)
        ##Вывод изображение из выпадающего окна
        self.pictures.setPixmap(QtGui.QPixmap(self.comboBox_2.currentText()))

        ##Вывод изображение из выпадающего окна
        self.comboBox.currentIndexChanged.connect(self.on_combobox_changed)
    def on_combobox_changed(self):
        pixmap = QtGui.QPixmap(self.comboBox.currentText())
        pixmap2 = pixmap.scaled(256, 256, QtCore.Qt.KeepAspectRatio)
        self.pictures.setPixmap(pixmap2)

        ##Вывод изображение из выпадающего окна
        self.comboBox_2.currentIndexChanged.connect(self.on_combobox_changed_2)
    def on_combobox_changed_2(self):
        pixmap = QtGui.QPixmap(self.comboBox_2.currentText())
        pixmap2 = pixmap.scaled(256, 256, QtCore.Qt.KeepAspectRatio)
        self.tmplate.setPixmap(pixmap2)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.Template_but.setText(_translate("MainWindow", "Template метод"))
        self.result.setText(_translate("MainWindow", "Result"))
        self.tmplate.setText(_translate("MainWindow", "template"))
        self.pictures.setText(_translate("MainWindow", "pictures"))
        self.label_4.setText(_translate("MainWindow", "результат"))
        self.label_5.setText(_translate("MainWindow", "Шаблон"))
        self.label_6.setText(_translate("MainWindow", "Картинка"))
        self.label_7.setText(_translate("MainWindow", "Выбрать картинку 1"))
        self.label_8.setText(_translate("MainWindow", "Выбрать картинку 2"))
        self.Viola_but.setText(_translate("MainWindow", "Метод Виолы-Джонса"))
        self.Sim_but.setText(_translate("MainWindow", "Линии симметрии"))
    ##Показать темлейт метод
    def showPicts(self):
        self.template(self.comboBox_2.currentText(),self.comboBox.currentText())

    ##Показать  метод виолы джонсон
    def showViola(self):
        self.Viola_J(self.comboBox_2.currentText())

    def showSim(self):
        self.sim(self.comboBox_2.currentText())
    def template(self,temp,pic):
        img_rgb = cv.imread(pic)
        img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
        template = cv.imread(temp,0)
        # crop_img = template[40:95,10:80]
        w, h = template.shape[::-1]
        res = cv.matchTemplate(img_gray,template,cv.TM_CCOEFF_NORMED)
        threshold = 0.7
        loc = np.where( res >= threshold)
        for pt in zip(*loc[::-1]):
            cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
        # cv.imwrite('res.png',img_rgb)
        frame = cv.cvtColor(img_rgb, cv.COLOR_BGR2RGB)
        image = QImage(frame, frame.shape[1], frame.shape[0], frame.strides[0], QImage.Format_RGB888)
        pixmap = QtGui.QPixmap(image)
        pixmap2 = pixmap.scaled(256, 256, QtCore.Qt.KeepAspectRatio)
        self.result.setPixmap(pixmap2)

    def Viola_J(self, temp):

        face_cascade = cv.CascadeClassifier('cascade/haarcascade_frontalface_default.xml')
        eyes_cascade = cv.CascadeClassifier('cascade/haarcascade_eye.xml')
        smile_cascade = cv.CascadeClassifier('cascade/haarcascade_smile.xml')
        glass_cascade = cv.CascadeClassifier('cascade/haarcascade_eye_tree_eyeglasses.xml')
        img_rgb = cv.imread(temp)
        img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
        faces = face_cascade.detectMultiScale(img_gray, 1.3, 5)
        eyes = eyes_cascade.detectMultiScale(img_gray, 1.3, 5)
        smile = smile_cascade.detectMultiScale(img_gray, 1.3, 5)
        glass = glass_cascade.detectMultiScale(img_gray, 1.3, 5)
        for (x, y, w, h) in faces:
            cv.rectangle(img_rgb, (x, y), (x+w, y+h), (255, 0, 0), 2)
        # for (ex, ey, ew, eh) in eyes:
        #     cv.rectangle(img_rgb, (ex, ey), (ex + ew, ey + eh), (255, 0, 0), 2)
        for (ex, ey, ew, eh) in smile:
            cv.rectangle(img_rgb,(ex,ey),(ex+ew,ey+eh), (255,0,0),2)
        for (ex, ey, ew, eh) in glass:
            cv.rectangle(img_rgb,(ex,ey),(ex+ew,ey+eh), (255,0,0),2)
        frame = cv.cvtColor(img_rgb, cv.COLOR_BGR2RGB)
        image = QImage(frame, frame.shape[1], frame.shape[0], frame.strides[0], QImage.Format_RGB888)
        pixmap = QtGui.QPixmap(image)
        pixmap2 = pixmap.scaled(256, 256, QtCore.Qt.KeepAspectRatio)
        self.result.setPixmap(pixmap2)

        ##Линии симметрии

    def sim(self,filename):
        test = []
        img_rgb = cv.imread(filename)
        detector = MTCNN()
        faces = detector.detect_faces(img_rgb)
        data = plt.imread(filename)
        plt.imshow(data)
        for result in faces:
            for key, value in result['keypoints'].items():
                if key == 'left_eye':
                    test.append(value)
                if key == 'right_eye':
                    test.append(value)
        x, y, width, height = result['box']
        rect = Rectangle((x, y), width, height, fill=False, color='red')
        new_array = [n for tup in test for n in tup]
        mid_x = (new_array[0] + new_array[2]) / 2
        mid_y = (new_array[1] + new_array[3]) / 2
        dist_x = math.sqrt((new_array[0] - mid_x)**2+(new_array[1] - mid_y)**2)
        ax = plt.gca()
        # горизонтальная
        l = ax.axline([new_array[0], new_array[1]], [new_array[2], new_array[3]])
        ax.add_patch(rect)
        # ##основная симметричная
        l2 = ax.axline([x + width/2,y + height], [mid_x, mid_y])
        # ##дополнительная симметричная
        # l3 = ax.axvline(new_nose[0] + dist_x)
        # l4 = ax.axvline(new_nose[0] - dist_x)
        l3 = ax.axline([new_array[0], new_array[1]],[x + width/2- dist_x,y + height])
        l4 = ax.axline([new_array[2], new_array[3]], [x + width / 2 + dist_x, y + height])
        ###prochee
        # l2 = ax.axline([new_nose[0], new_nose[1]], [mid_x, mid_y])
        # l2 = mlines.Line2D([new_nose[0], mid_x ], [new_nose[1], mid_y])
        ax.add_line(l)
        ax.add_line(l2)
        ax.add_line(l3)
        ax.add_line(l4)
        plt.show()


from typing import Optional

from flamapy.metamodels.configuration_metamodel.models.configuration import Configuration

from flamapy.metamodels.bdd_metamodel.models import BDDModel
from flamapy.metamodels.bdd_metamodel.operations.interfaces import FeatureInclusionProbability
from flamapy.metamodels.bdd_metamodel.operations import BDDProducts

class BDDFeatureInclusionProbability(FeatureInclusionProbability):
    """The Feature Inclusion Probability (FIP) operation determines the probability
    for a variable to be included in a valid solution.

    This is a brute-force implementation that enumerates all solutions
    for calculating the probabilities.

    Ref.: [Heradio et al. 2019. Supporting the Statistical Analysis of Variability Models. SPLC.
    (https://doi.org/10.1109/ICSE.2019.00091)]
    """

    def __init__(self, partial_configuration: Optional[Configuration] = None) -> None:
        self.bdd_model = None
        self.result: dict[str, float] = {}
        self.partial_configuration = partial_configuration

    def execute(self, model: BDDModel) -> 'BDDFeatureInclusionProbability':
        self.bdd_model = model
        self.result = feature_inclusion_probability(self.bdd_model, self.partial_configuration)
        return self

    def get_result(self) -> dict[str, float]:
        return self.result

    def feature_inclusion_probability(self) -> dict[str, float]:
        return feature_inclusion_probability(self.bdd_model, self.partial_configuration)

def feature_inclusion_probability(bdd_model: BDDModel,
                                  config: Optional[Configuration] = None) -> dict[str, float]:
    products = BDDProducts(config).execute(bdd_model).get_result()
    n_products = len(products)
    if n_products == 0:
        return {feature: 0.0 for feature in bdd_model.variables}

    prob = {}
    for feature in bdd_model.variables:
        prob[feature] = sum(feature in p.elements for p in products) / n_products
    return prob


from django.shortcuts import render, redirect
from django.db.models import Prefetch, Q
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django.views.generic.edit import FormView

from .forms import MailboxForm, TopicForm

from accounts.models import Profile

from .models import Mailbox, Topic
from items.models import Item

from utilities import rules

import logging

class CreateMailboxView(FormView):
    template_name = "create_mailbox.html"
    form_class = MailboxForm

    def form_valid(self, form):
        owner = Profile.objects.get(user_id=self.request.user.pk)
        name = form.cleaned_data["name"]
        form_member_email = form.cleaned_data["member_email"]
        form_member_alias = form.cleaned_data["member_alias"]

        members = []
        if bool(form_member_email):
            member = Profile.objects.get(user__email=form_member_email)
            members.append(member)

        if bool(form_member_alias):
            member = Profile.objects.get(alias=form_member_alias)

        mailbox = owner.mailboxes_owned.create(name=name)
        for member in members:
            mailbox.members.add(member)

        return redirect(reverse("home"))

@login_required
def mailbox_list(request):
    profile = (
        Profile.objects.filter(user_id=request.user.id)
        .prefetch_related(
            Prefetch("mailboxes", to_attr="mailboxes_in"),
            Prefetch("mailboxes_owned", to_attr="my_mailboxes"),
        )
        .first()
    )
    mailboxes_ = Mailbox.objects.filter(
        Q(owner=request.user.id) | Q(members=request.user.id)
    )
    return render(request, "mailbox_list.html", {"mailboxes": mailboxes_})

@login_required
@rules.is_mailbox_member
def mailbox_detail(request, mailbox_id):
    user_topics = Topic.objects.filter(member=request.user.id)
    mailbox = (
        Mailbox.objects.filter(id=mailbox_id)
        .prefetch_related(
            Prefetch("mailbox_topics", queryset=user_topics, to_attr="topics")
        )
        .first()
    )

    topics = list(mailbox.topics)

    return render(
        request, "mailbox_detail.html", {"mailbox": mailbox, "topics": topics}
    )

@login_required
@rules.is_mailbox_member
def create_topic_view(request, mailbox_id):

    members_query = Mailbox.objects.get(id=mailbox_id).members.all()
    if request.method == "POST":
        form = TopicForm(members_query, request.POST)

        if form.is_valid():
            members = form.cleaned_data["members"]
            name = form.cleaned_data["name"]
            email_replies = form.cleaned_data["email_replies"]
            mailbox = Mailbox.objects.get(id=mailbox_id)
            creator = Profile.objects.get(user_id=request.user.id)

            topic = creator.topics.create(
                name=name,
                mailbox=mailbox,
                email_replies=email_replies,
                through_defaults={"creator": creator},
            )

            for member in members:
                topic.member.add(member)

            return redirect(reverse("home"))

    else:
        form = TopicForm(members_query)

    return render(request, "create_topic.html", {"form": form})

@login_required
# @rules.is_mailbox_member
def topic_detail(request, topic_id):
    posts_query = Item.objects.filter(topic=topic_id)
    topic = (
        Topic.objects.filter(id=topic_id)
        .prefetch_related(
            Prefetch("topic_items", queryset=posts_query, to_attr="posts")
        )
        .first()
    )

    posts = list(topic.posts)

    return render(request, "topic_detail.html", {"topic": topic, "posts": posts})


import sys; sys.path.append(".")
from modules.argument_parser_for_blender import ArgumetParserForBlender

import bpy
import pathlib

def collada2stl(input_dir):
    p = pathlib.Path(input_dir)
    collada_files = list(p.glob("**/*.dae"))

    for collada in collada_files:
        bpy.ops.object.select_all(action="DESELECT")
        bpy.ops.object.select_all(action="SELECT")
        bpy.ops.object.delete()

        bpy.ops.wm.collada_import(filepath=collada.as_posix())
        bpy.ops.object.select_all(action="DESELECT")
        bpy.ops.object.select_all(action="SELECT")

        output_name = collada.parent / f"{collada.stem}.stl"

        bpy.ops.export_mesh.stl(filepath=output_name.as_posix())

if __name__ == "__main__":
    parser = ArgumetParserForBlender()
    parser.add_argument("collada_dir", type=str)

    args = parser.parse_args()

    collada_dir = args.collada_dir

    collada2stl(collada_dir)