# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import copy
import os
from argparse import ArgumentParser, Namespace

import mozlog

here = os.path.abspath(os.path.dirname(__file__))
try:
    from mozbuild.base import MachCommandConditions as conditions
    from mozbuild.base import MozbuildObject

    build_obj = MozbuildObject.from_environment(cwd=here)
except Exception:
    build_obj = None
    conditions = None

from mozperftest.metrics import get_layers as metrics_layers  # noqa
from mozperftest.system import get_layers as system_layers  # noqa
from mozperftest.test import get_layers as test_layers  # noqa
from mozperftest.utils import convert_day  # noqa

FLAVORS = "desktop-browser", "mobile-browser", "doc", "xpcshell", "webpagetest"

class Options:

    general_args = {
        "--flavor": {
            "choices": FLAVORS,
            "metavar": "{{{}}}".format(", ".join(FLAVORS)),
            "default": "desktop-browser",
            "help": "Only run tests of this flavor.",
        },
        "tests": {
            "nargs": "*",
            "metavar": "TEST",
            "default": [],
            "help": "Test to run. Can be a single test file or URL or a directory"
            " of tests (to run recursively). If omitted, the entire suite is run.",
        },
        "--test-iterations": {
            "type": int,
            "default": 1,
            "help": "Number of times the whole test is executed",
        },
        "--output": {
            "type": str,
            "default": "artifacts",
            "help": "Path to where data will be stored, defaults to a top-level "
            "`artifacts` folder.",
        },
        "--hooks": {
            "type": str,
            "default": None,
            "help": "Script containing hooks. Can be a path or a URL.",
        },
        "--verbose": {"action": "store_true", "default": False, "help": "Verbose mode"},
        "--push-to-try": {
            "action": "store_true",
            "default": False,
            "help": "Pushin the test to try",
        },
        "--try-platform": {
            "nargs": "*",
            "type": str,
            "default": "linux",
            "help": "Platform to use on try",
            "choices": ["g5", "pixel2", "linux", "mac", "win"],
        },
        "--on-try": {
            "action": "store_true",
            "default": False,
            "help": "Running the test on try",
        },
        "--test-date": {
            "type": convert_day,
            "default": "today",
            "help": "Used in multi-commit testing, it specifies the day to get test builds from. "
            "Must follow the format `YYYY.MM.DD` or be `today` or `yesterday`.",
        },
    }

    args = copy.deepcopy(general_args)

for layer in system_layers() + test_layers() + metrics_layers():
    if layer.activated:
        # add an option to deactivate it
        option_name = "--no-%s" % layer.name
        option_help = "Deactivates the %s layer" % layer.name
    else:
        option_name = "--%s" % layer.name
        option_help = "Activates the %s layer" % layer.name

    Options.args[option_name] = {
        "action": "store_true",
        "default": False,
        "help": option_help,
    }

    for option, value in layer.arguments.items():
        option = "--%s-%s" % (layer.name, option.replace("_", "-"))
        if option in Options.args:
            raise KeyError("%s option already defined!" % option)
        Options.args[option] = value

class PerftestArgumentParser(ArgumentParser):
    """%(prog)s [options] [test paths]"""

    def __init__(self, app=None, **kwargs):
        ArgumentParser.__init__(
            self, usage=self.__doc__, conflict_handler="resolve", **kwargs
        )
        # XXX see if this list will vary depending on the flavor & app
        self.oldcwd = os.getcwd()
        self.app = app
        if not self.app and build_obj:
            if conditions.is_android(build_obj):
                self.app = "android"
        if not self.app:
            self.app = "generic"
        for name, options in Options.args.items():
            self.add_argument(name, **options)

        mozlog.commandline.add_logging_group(self)
        self.set_by_user = []

    def parse_helper(self, args):
        for arg in args:
            arg_part = arg.partition("--")[-1].partition("-")
            layer_name = f"--{arg_part[0]}"
            layer_exists = arg_part[1] and layer_name in Options.args
            if layer_exists:
                args.append(layer_name)

    def get_user_args(self, args):
        # suppress args that were not provided by the user.
        res = {}
        for key, value in args.items():
            if key not in self.set_by_user:
                continue
            res[key] = value
        return res

    def _parse_known_args(self, arg_strings, namespace):
        # at this point, the namespace is filled with default values
        # defined in the args

        # let's parse what the user really gave us in the CLI
        # in a new namespace
        user_namespace, extras = super()._parse_known_args(arg_strings, Namespace())

        self.set_by_user = list([name for name, value in user_namespace._get_kwargs()])

        # we can now merge both
        for key, value in user_namespace._get_kwargs():
            setattr(namespace, key, value)

        return namespace, extras

    def parse_args(self, args=None, namespace=None):
        self.parse_helper(args)
        return super().parse_args(args, namespace)

    def parse_known_args(self, args=None, namespace=None):
        self.parse_helper(args)
        return super().parse_known_args(args, namespace)

class SideBySideOptions:
    args = [
        [
            ["-t", "--test-name"],
            {
                "type": str,
                "required": True,
                "dest": "test_name",
                "help": "The name of the test task to get videos from.",
            },
        ],
        [
            ["--new-test-name"],
            {
                "type": str,
                "default": None,
                "help": "The name of the test task to get videos from in the new revision.",
            },
        ],
        [
            ["--base-revision"],
            {
                "type": str,
                "required": True,
                "help": "The base revision to compare a new revision to.",
            },
        ],
        [
            ["--new-revision"],
            {
                "type": str,
                "required": True,
                "help": "The base revision to compare a new revision to.",
            },
        ],
        [
            ["--base-branch"],
            {
                "type": str,
                "default": "autoland",
                "help": "Branch to search for the base revision.",
            },
        ],
        [
            ["--new-branch"],
            {
                "type": str,
                "default": "autoland",
                "help": "Branch to search for the new revision.",
            },
        ],
        [
            ["--base-platform"],
            {
                "type": str,
                "required": True,
                "dest": "platform",
                "help": "Platform to return results for.",
            },
        ],
        [
            ["--new-platform"],
            {
                "type": str,
                "default": None,
                "help": "Platform to return results for in the new revision.",
            },
        ],
        [
            ["-o", "--overwrite"],
            {
                "action": "store_true",
                "default": False,
                "help": "If set, the downloaded task group data will be deleted before "
                + "it gets re-downloaded.",
            },
        ],
        [
            ["--cold"],
            {
                "action": "store_true",
                "default": False,
                "help": "If set, we'll only look at cold pageload tests.",
            },
        ],
        [
            ["--warm"],
            {
                "action": "store_true",
                "default": False,
                "help": "If set, we'll only look at warm pageload tests.",
            },
        ],
        [
            ["--most-similar"],
            {
                "action": "store_true",
                "default": False,
                "help": "If set, we'll search for a video pairing that is the most similar.",
            },
        ],
        [
            ["--search-crons"],
            {
                "action": "store_true",
                "default": False,
                "help": "If set, we will search for the tasks within the cron jobs as well. ",
            },
        ],
        [
            ["--skip-download"],
            {
                "action": "store_true",
                "default": False,
                "help": "If set, we won't try to download artifacts again and we'll "
                + "try using what already exists in the output folder.",
            },
        ],
        [
            ["--output"],
            {
                "type": str,
                "default": None,
                "help": "This is where the data will be saved. Defaults to CWD. "
                + "You can include a name for the file here, otherwise it will "
                + "default to side-by-side.mp4.",
            },
        ],
        [
            ["--metric"],
            {
                "type": str,
                "default": "speedindex",
                "help": "Metric to use for side-by-side comparison.",
            },
        ],
        [
            ["--vismetPath"],
            {
                "type": str,
                "default": False,
                "help": "Paths to visualmetrics.py for step chart generation.",
            },
        ],
        [
            ["--original"],
            {
                "action": "store_true",
                "default": False,
                "help": "If set, use the original videos in the side-by-side instead "
                + "of the postprocessed videos.",
            },
        ],
        [
            ["--skip-slow-gif"],
            {
                "action": "store_true",
                "default": False,
                "help": "If set, the slow-motion GIFs won't be produced.",
            },
        ],
    ]

class ChangeDetectorOptions:
    args = [
        # TODO: Move the common tool arguments to a common
        # argument class.
        [
            ["--task-name"],
            {
                "type": str,
                "nargs": "*",
                "default": [],
                "dest": "task_names",
                "help": "The full name of the test task to get data from e.g. "
                "test-android-hw-a51-11-0-aarch64-shippable-qr/opt-"
                "browsertime-tp6m-geckoview-sina-nofis.",
            },
        ],
        [
            ["-t", "--test-name"],
            {
                "type": str,
                "default": None,
                "dest": "test_name",
                "help": "The name of the test task to get data from e.g. "
                "browsertime-tp6m-geckoview-sina-nofis.",
            },
        ],
        [
            ["--platform"],
            {
                "type": str,
                "default": None,
                "help": "Platform to analyze e.g. "
                "test-android-hw-a51-11-0-aarch64-shippable-qr/opt.",
            },
        ],
        [
            ["--new-test-name"],
            {
                "type": str,
                "help": "The name of the test task to get data from in the "
                "base revision e.g. browsertime-tp6m-geckoview-sina-nofis.",
            },
        ],
        [
            ["--new-platform"],
            {
                "type": str,
                "help": "Platform to analyze in base revision e.g. "
                "test-android-hw-a51-11-0-aarch64-shippable-qr/opt.",
            },
        ],
        [
            ["--depth"],
            {
                "type": int,
                "default": None,
                "help": "This sets how the change detector should run. "
                "Default is None, which is a direct comparison between the "
                "revisions. -1 will autocompute the number of revisions to "
                "look at between the base, and new. Any other positive integer "
                "acts as a maximum number to look at.",
            },
        ],
        [
            ["--base-revision"],
            {
                "type": str,
                "required": True,
                "help": "The base revision to compare a new revision to.",
            },
        ],
        [
            ["--new-revision"],
            {
                "type": str,
                "required": True,
                "help": "The new revision to compare a base revision to.",
            },
        ],
        [
            ["--base-branch"],
            {
                "type": str,
                "default": "try",
                "help": "Branch to search for the base revision.",
            },
        ],
        [
            ["--new-branch"],
            {
                "type": str,
                "default": "try",
                "help": "Branch to search for the new revision.",
            },
        ],
        [
            ["--skip-download"],
            {
                "action": "store_true",
                "default": False,
                "help": "If set, we won't try to download artifacts again and we'll "
                + "try using what already exists in the output folder.",
            },
        ],
        [
            ["-o", "--overwrite"],
            {
                "action": "store_true",
                "default": False,
                "help": "If set, the downloaded task group data will be deleted before "
                + "it gets re-downloaded.",
            },
        ],
    ]

class ToolingOptions:
    args = {
        "side-by-side": SideBySideOptions.args,
        "change-detector": ChangeDetectorOptions.args,
    }

class PerftestToolsArgumentParser(ArgumentParser):
    """%(prog)s [options] [test paths]"""

    tool = None

    def __init__(self, *args, **kwargs):
        ArgumentParser.__init__(
            self, usage=self.__doc__, conflict_handler="resolve", **kwargs
        )

        if PerftestToolsArgumentParser.tool is None:
            raise SystemExit("No tool specified, cannot continue parsing")
        else:
            for name, options in ToolingOptions.args[PerftestToolsArgumentParser.tool]:
                self.add_argument(*name, **options)


from .views import *
from django.urls import path

urlpatterns=[
    path('search/',ArticleList.as_view(),name='search'),
    path('type_ahead_wikis/',WikiSearchAPI.as_view(),name='type_ahead_wikis'),
    path('article/',FetchArticle.as_view(), name='article')
]


import asyncio
import os
import youtube_dl

from datetime import datetime
from pyrogram import Client, filters
from pytgcalls import GroupCall
from Python_ARQ import ARQ

from .functions import (
    transcode,
    download_and_transcode_song,
    convert_seconds,
    time_to_seconds,
    generate_cover,
    generate_cover_square
)

from .configs import (
    API_ID as api_id,
    API_HASH as api_hash,
    SUDO_CHAT_ID as sudo_chat_id,
    ARQ_API,
    HEROKU
)

from .misc import (
    HELP_TEXT,
    START_TEXT,
    REPO_TEXT,
    DONATION_TEXT
)

if HEROKU:
    from .configs import SESSION_STRING

if not HEROKU:
    app = Client('ktgvc', api_id=api_id, api_hash=api_hash)
else:
    app = Client(SESSION_STRING, api_id=api_id, api_hash=api_hash)

group_calls = GroupCall(None, path_to_log_file='')
cmd_filter = lambda cmd: filters.command(cmd, prefixes='/')

# Arq Client
arq = ARQ(ARQ_API)

# File raw music
raw_filename = 'input.raw'

queue = []  # This is where the whole song queue is stored
playing = False  # Tells if something is playing or not

@app.on_message(filters.text & cmd_filter('start'))
async def start(_, message):
    await message.reply_text(START_TEXT)

@app.on_message(filters.text & cmd_filter('help'))
async def helps(_, message):
    await message.reply_text(HELP_TEXT)

@app.on_message(filters.text & cmd_filter('repo'))
async def repo(_, message):
    await message.reply_text(REPO_TEXT)

@app.on_message(filters.text & cmd_filter('ping'))
async def ping(_, message):
    start = datetime.now()
    msg = await send('`âš¡`')
    end = datetime.now()
    latency = (end - start).microseconds / 1000
    await msg.edit(f"**Jinda hu sir itni jaldi nhi marunga**\n`{latency} ms me aapke bato ka reply deta hu dekh rhe ho`")

@app.on_message(filters.text & cmd_filter('donation'))
async def donation(_, message):
    await message.reply_text(DONATION_TEXT)

@app.on_message(filters.text & cmd_filter('join'))
async def join(_, message):
    if group_calls.is_connected:
        await message.reply_text('Bot already join hai ab kya tere ghar me aajaye?')
        return
    group_calls.client = app
    await group_calls.start(message.chat.id)
    await message.reply_text('Khusi manao jaoin ho gya XD')

@app.on_message(filters.text & cmd_filter('mute'))
async def mute(_, message):
    group_calls.set_is_mute(is_muted=True)
    await message.reply_text('Succsessfully muted \nab nhi bol payega ye XD')

@app.on_message(filters.text & cmd_filter('unmute'))
async def unmute(_, message):
    group_calls.set_is_mute(is_muted=False)
    await message.reply_text('Succsessfully unmuted \nja bol le wo bhi kya yaad rakhega')

@app.on_message(filters.text & cmd_filter('volume'))
async def volume(_, message):
    if len(message.command) < 2:
        await message.reply_text('You forgot to pass volume (1-200)')

    await group_calls.set_my_volume(volume=int(message.command[1]))
    await message.reply_text(f'Volume changed to {message.command[1]}')

@app.on_message(filters.text & cmd_filter('stop'))
async def stop(_, message):
    global playing
    group_calls.stop_playout()
    queue.clear()
    playing = False
    await message.reply_text('Chal band kar diya gana XD')

@app.on_message(filters.text & cmd_filter('leave'))
async def leave(_, message):
    global playing
    if not group_calls.is_connected:
        await message.reply_text('Abe mai hu kha jo leave karu! Ajib aadmi ho tum bhi')
        return
    await group_calls.stop()
    queue.clear()
    playing = False
    group_calls.input_filename = ''
    await message.reply_text('Bahut gande ho tum mujhe wha se leave karwa diya :~(')

@app.on_message(filters.text & cmd_filter('kill'))
async def killbot(_, message):
    await send("__**Mar diya ðŸ˜ˆ!__**")
    quit()

@app.on_message(filters.text & cmd_filter('play'))
async def queues(_, message):
    if not group_calls.is_connected:
        await message.reply_text('Abe pahle bot ko VC to join karwa!')
        return
    usage = "**Usage:**\n__**/play youtube Song_Name**__"
    if len(message.command) < 3:
        await message.reply_text(usage)
        return
    text = message.text.split(None, 2)[1:]
    service = text[0]
    song_name = text[1]
    requested_by = message.from_user.first_name
    services = ["youtube", "deezer", "saavn"]
    if service not in services:
        await message.reply_text(usage)
        return
    if len(queue) > 0:
        await message.reply_text("__**Added To Queue.__**")
        queue.append({"service": service, "song": song_name,
                      "requested_by": requested_by})
        await play()
        return
    queue.append({"service": service, "song": song_name,
                  "requested_by": requested_by})
    await play()

@app.on_message(filters.text & cmd_filter('skip'))
async def skip(_, message):
    global playing
    if len(queue) == 0:
        await message.reply_text("__**Queue Is Empty. #sedlyf Shi bta teri girlfriend/boyfriend nhi na hai?**__")
        return
    playing = False
    await message.reply_text("__**Hue hue Hta diya wo gana!**__")
    await play()

@app.on_message(filters.text & cmd_filter('queue'))
async def queue_list(_, message):
    if len(queue) != 0:
        i = 1
        text = ""
        for song in queue:
            text += f"**{i}. Platform:** __**{song['service']}**__ | **Song:** __**{song['song']}**__\n"
            i += 1
        await message.reply_text(text)
    else:
        await message.reply_text("__**Queue Is Empty. #sedlyf Shi bta teri girlfriend/boyfriend nhi na hai?**__")

# Queue handler

async def play():
    global queue, playing
    while not playing:
        await asyncio.sleep(2)
        if len(queue) != 0:
            service = queue[0]["service"]
            song = queue[0]["song"]
            requested_by = queue[0]["requested_by"]
            if service == "youtube":
                playing = True
                del queue[0]
                try:
                    await ytplay(requested_by, song)
                except Exception as e:
                    print(str(e))
                    await send(str(e))
                    playing = False
            elif service == "saavn":
                playing = True
                del queue[0]
                try:
                    await jiosaavn(requested_by, song)
                except Exception as e:
                    print(str(e))
                    await send(str(e))
                    playing = False
            elif service == "deezer":
                playing = True
                del queue[0]
                try:
                    await deezer(requested_by, song)
                except Exception as e:
                    print(str(e))
                    await send(str(e))
                    playing = False

# Deezer----------------------------------------------------------------------------------------

async def deezer(requested_by, query):
    global playing
    m = await send(f"__**Searching for {query} on Deezer.**__")
    try:
        songs = await arq.deezer(query, 1)
        title = songs[0].title
        duration = convert_seconds(int(songs[0].duration))
        thumbnail = songs[0].thumbnail
        artist = songs[0].artist
        url = songs[0].url
    except Exception as e:
        await m.edit("__**Found No Song Matching Your Query.**__")
        playing = False
        print(str(e))
        return
    await m.edit("__**Generating Thumbnail.**__")
    await generate_cover_square(requested_by, title, artist, duration, thumbnail)
    await m.edit("__**Downloading And Transcoding.**__")
    await download_and_transcode_song(url)
    await m.delete()
    m = await app.send_photo(
        chat_id=sudo_chat_id,
        photo="final.png",
        caption=f"**Playing** __**[{title}]({url})**__ **Via Deezer.**",
    )
    os.remove("final.png")
    group_calls.input_filename = raw_filename
    await asyncio.sleep(int(songs[0]["duration"]))
    await m.delete()
    playing = False

# Jiosaavn--------------------------------------------------------------------------------------

async def jiosaavn(requested_by, query):
    global playing
    m = await send(f"__**Searching for {query} on JioSaavn.**__")
    try:
        songs = await arq.saavn(query)
        sname = songs[0].song
        slink = songs[0].media_url
        ssingers = songs[0].singers
        sthumb = songs[0].image
        sduration = songs[0].duration
        sduration_converted = convert_seconds(int(sduration))
    except Exception as e:
        await m.edit("__**Found No Song Matching Your Query.**__")
        print(str(e))
        playing = False
        return
    await m.edit("__**Processing Thumbnail.**__")
    await generate_cover_square(
        requested_by, sname, ssingers, sduration_converted, sthumb
    )
    await m.edit("__**Downloading And Transcoding.**__")
    await download_and_transcode_song(slink)
    await m.delete()
    m = await app.send_photo(
        chat_id=sudo_chat_id,
        caption=f"**Playing** __**{sname}**__ **Via Jiosaavn.**",
        photo="final.png",
    )
    os.remove("final.png")
    group_calls.input_filename = raw_filename
    await asyncio.sleep(int(sduration))
    await m.delete()
    playing = False

# Youtube Play-----------------------------------------------------------------------------------

async def ytplay(requested_by, query):
    global playing
    ydl_opts = {"format": "bestaudio"}
    m = await send(f"__**{query} Ye jo tera gana hai usko mai youtube pe dhundh rha hu wait kar**__")
    try:
        results = await arq.youtube(query, 1)
        link = f"https://youtube.com{results[0].url_suffix}"
        title = results[0].title
        thumbnail = results[0].thumbnails[0]
        duration = results[0].duration
        views = results[0].views
        if time_to_seconds(duration) >= 1800:
            await m.edit("__**Abe yaar bas 30min ke andar ke hi gane bajwa mere se Din bhar majduri karwata hai saram nhi aati?**__")
            playing = False
            return
    except Exception as e:
        await m.edit("__**Abe kaun se duniya ka gana diye ho be mila hi nhi #sedlyf mujhe pakka pta hai teri koi girlfriend nhi hogi hai na?**__")
        playing = False
        print(str(e))
        return
    await m.edit("__**Thumbnail bna rha hu wait kar**__")
    await generate_cover(requested_by, title, views, duration, thumbnail)
    await m.edit("__**Youtube devta se gane la rha hu XD**__")
    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        info_dict = ydl.extract_info(link, download=False)
        audio_file = ydl.prepare_filename(info_dict)
        ydl.process_info(info_dict)
    await m.edit("__**Chhu mantar ye jadu chu hai gana bajadu mai**__")
    os.rename(audio_file, "audio.webm")
    transcode("audio.webm")
    await m.delete()
    m = await app.send_photo(
        chat_id=sudo_chat_id,
        caption=f"**Abhi ye baj rha hai** __**[{title}]({link})**__ **Youtube, SATYA aur Shubham devta ki kripa se**",
        photo="final.png",
    )
    os.remove("final.png")
    group_calls.input_filename = raw_filename
    await asyncio.sleep(int(time_to_seconds(duration)))
    playing = False
    await m.delete()

async def send(text):
    m = await app.send_message(sudo_chat_id, text=text, disable_web_page_preview=True)
    return m

print('[INFO] Bot is running...\n')
app.run()


"""
calculate the mAP (mean average precision) for object detection task
Normally we calculate mAP@iou_threshold
"""
import numpy as np
from sklearn.metrics import average_precision_score

from ..vision import iou

def calc_map(iou_threshold, pred, truth):
    """
    Calculate the mAP@iou_threshold, all the coordinates are in the original image space
    Parameters:
        iou_threshold: only greater than this value it can count
        pred: prediction for one image
            [roi1, roi2, roi3, ...]
            roi: {"x1": 1, "x2": 2, "y1": 1, "y2": 2, "class": "cat", "prob": 0.8}
        truth: ground truth for one image
            [bbox1, bbox2, bbox3, ...]
            bbox: {"x1": 1, "x2": 2, "y1": 1, "y2": 2, "class": "cat"}
    Returns:
        mAP@iou_threshold
    """
    result_trues, result_probs = {}, {}

    # we use this flag to identify whether there is a roi ever matched for each bounding box
    for bbox in truth:
        bbox["bbox_matched"] = False

    pred_probs = np.array([item["prob"] for item in pred])
    preds_sorted_by_prob = np.argsort(pred_probs)[::-1]

    for pred_index in preds_sorted_by_prob:
        pred_item = pred[pred_index]
        pred_class = pred_item["class"]
        pred_prob = pred_item["prob"]
        pred_x1, pred_x2, pred_y1, pred_y2 = pred_item["x1"], pred_item["x2"], pred_item["y1"], pred_item["y2"]

        if pred_class not in result_probs:
            result_probs[pred_class] = []
            result_trues[pred_class] = []

        result_probs[pred_class].append(pred_prob)
        # identify whether True Positive or False Positive
        found_match = False

        for bbox in truth:
            truth_class = bbox["class"]
            truth_x1, truth_x2, truth_y1, truth_y2 = bbox["x1"], bbox["x2"], bbox["y1"], bbox["y2"]

            # if pred and truth are not the same
            if truth_class != pred_class:
                continue
            # if the current bounding box is occupied by anthoer roi
            # this makes us not to repeat to count true positives
            if bbox["bbox_matched"] is True:
                continue

            current_iou = iou.iou((pred_x1, pred_y1, pred_x2, pred_y2),
                                  (truth_x1, truth_y1, truth_x2, truth_y2))
            if current_ious >= iou_threshold:
                found_match = True
                bbox["bbox_matched"] = True
                break
            else:
                continue

        result_truth[pred_class].append(int(found_match))

    # we also can't miss False Negative
    for bbox in truth:
        if bbox["bbox_matched"] is False:
            if bbox["class"] not in result_probs:
                result_probs[bbox["class"]] = []
                result_truth[bbox["class"]] = []
            result_probs[bbox["class"]].append(0)
            result_truth[bbox["class"]].append(1)

    # ok now we start to calculate the metrics
    all_aps = []
    for class_item in result_truth:
        ap = average_precision_score(result_truth[class_item], result_probs[class_item])
        all_aps.append(ap)
    return np.mean(all_aps)


import pyDH
import pickle  #для сохренения объекта в файл
Alice = pyDH.DiffieHellman()
Alice_pubkey = Alice.gen_public_key() # Алиса передаст свой открытый ключ Бобу
Alice_privatekey = Alice.get_private_key() # Этот секретный ключ только у Алисы
#сохраняет свои параметры - объект Alice
f = open(r'Alice.pem', 'wb')
pickle.dump(Alice, f)
f.close()
#сохраняет публичный ключ, чтобы его передать Бобу
pub = str(Alice_pubkey)
pubfile = open('public_Alice.pem', 'w+')  # pem- общепринятое расширение для ключа
pubfile.write(pub)
pubfile.close()
#сохраняет секретный ключ
private = str(Alice_privatekey)
privatefile = open('private_Alice.pem', 'w+')   # pem- общепринятое расширение для ключа
privatefile.write(private)
privatefile.close()
print ("Открытый ключ Алисы:",pub)
print ('Закрытый ключ Алисы:',private)


import sqlite3
from typing import Any, Optional, Tuple

from pypika import Order, Query, Table
from pypika import functions as fn

from entities.http_flow import HttpFlow
from entities.http_request import HttpRequest
from repos.base_repo import BaseRepo

class HttpFlowRepo(BaseRepo):
    table: Table

    def __init__(self):
        super().__init__()
        self.table = Table('http_flows')

    def save(self, flow: HttpFlow):
        # Update or insert the HttpFlow
        if flow.id > 0:
            self.generic_update(flow, self.table)
        else:
            self.generic_insert(flow, self.table)

    def delete(self, flow: HttpFlow):
        self.generic_delete(flow, self.table)

    def find(self, id: int) -> Optional[HttpFlow]:
        query = Query.from_(self.table).select('*').where(self.table.id == id)
        results = self.__find_by_query(query.get_sql(), [])

        if len(results) == 0:
            return None
        return results[0]

    def find_by_uuid(self, uuid: str) -> Optional[HttpFlow]:
        query = Query.from_(self.table).select('*').where(self.table.uuid == uuid)
        results = self.__find_by_query(query.get_sql(), [])

        if len(results) == 0:
            return None
        return results[0]

    def find_for_table(self, search_term: str, offset: int, limit: int) -> Tuple[list[HttpFlow], int]:
        # TODO: These needs to apply host filters from Settings
        if len(search_term) > 0:
            return self.__find_by_search(search_term, offset, limit)

        query = Query.from_(self.table).select('*') \
            .where(self.table.type == HttpFlow.TYPE_PROXY) \
            .orderby(self.table.id, order=Order.desc) \
            .offset(offset).limit(limit)

        count_query = Query.from_(self.table).select(fn.Count('*', alias='count')) \
            .where(self.table.type == HttpFlow.TYPE_PROXY)

        http_flows = self.__find_by_query(query.get_sql(), [])
        count = self.__count_query(count_query.get_sql())

        return http_flows, count

    def find_by_ids(self, ids: list[int]) -> list[HttpFlow]:
        query = Query.from_(self.table).select('*').where(self.table.id.isin(ids))
        return self.__find_by_query(query.get_sql(), [])

    # This is used to find all example flows for a set of flows
    def find_by_http_flow_id(self, http_flow_ids: list[int]) -> list[HttpFlow]:
        query = Query.from_(self.table).select('*').where(self.table.http_flow_id.isin(http_flow_ids))
        return self.__find_by_query(query.get_sql(), [])

    # See https://www.sqlite.org/fts5.html
    #
    # Example queries:
    # host:^"www.google.com"
    # host:"www.google.com" AND path:"complete"
    def __find_by_search(self, search_term: str, offset: int, limit: int) -> Tuple[list[HttpFlow], int]:
        # Wrap quotes around search text if none provided
        # User may want to for more advanced search queries like:
        # host:"synack.com" path:"solutions/talent"
        if '"' not in search_term:
            search_term = f'"{search_term}"*'

        query = "SELECT * FROM http_requests_fts WHERE http_requests_fts MATCH ? ORDER BY rank;"
        cursor = self.conn.cursor()
        cursor.execute(query, [search_term])
        rows: list[sqlite3.Row] = cursor.fetchall()

        request_ids = [r['id'] for r in rows]

        # NOTE: FTS5 will include the http_flow id in the search. We may want to exclude the result if the id
        # matches the search term perfectly. This could exclude valid results however.

        query = Query.from_(self.table).select('*') \
            .where(self.table.request_id.isin(request_ids)).where(self.table.type == HttpFlow.TYPE_PROXY) \
            .orderby(self.table.id, order=Order.desc) \
            .offset(offset).limit(limit)

        count_query = Query.from_(self.table).select(fn.Count('*', alias='count')) \
            .where(self.table.request_id.isin(request_ids)).where(self.table.type == HttpFlow.TYPE_PROXY)

        http_flows = self.__find_by_query(query.get_sql(), [])
        count = self.__count_query(count_query.get_sql())

        return http_flows, count

    def __count_query(self, sql_query: str) -> int:
        cursor = self.conn.cursor()
        cursor.execute(sql_query)
        rows: list[sqlite3.Row] = cursor.fetchall()
        return rows[0]['count']

    def __find_by_query(self, sql_query: str, sql_params: list[Any]) -> list[HttpFlow]:
        cursor = self.conn.cursor()
        cursor.execute(sql_query, sql_params)
        rows: list[sqlite3.Row] = cursor.fetchall()

        # Instantiate the flows
        flows: list[HttpFlow] = []
        for row in rows:
            row_values = self.row_to_dict(row)

            # Load Request
            row_values['request'] = HttpRequest.build_blank_placeholder()

            flow = HttpFlow(**row_values)
            flow.id = row['id']
            flow.created_at = row['created_at']
            flows.append(flow)

        return flows


class CRF_cut:
    def __init__(self):
        emit_path = './model/emit_dict.txt'
        trans_path = './model/trans_dict.txt'
        self.prob_trans = self.load_mode(trans_path)
        self.prob_emit = self.load_mode(emit_path)

    def load_mode(self,model_path):
        f = open(model_path,encoding='utf-8')
        a = f.read()
        word_dict = eval(a)
        f.close()
        return word_dict

    def verbiter(self,sent,states,prob_emit,prob_trans):
        V = [{}]
        path = {}

        for state in states:
            V[0][state] = self.prob_emit[state].get(sent[0],0)
            path[state] = state

        for t in range(1,len(sent)):
            V.append({})
            newpath = {}
            for state in states:
                state_path = [(V[t-1][y0] * self.prob_trans[y0].get(state,0) * self.prob_emit[state].get(sent[0],0),y0) for y0 in states if V[t-1][y0] > 0]


import ee
from functools import partial

from hydrafloods import filtering

def fwdet(water_img, dem, band=None, outlier_test=False, force_projection=False):
    """Implementation of the Flood Water Depth Estimation Tool
    Used to calculate water depth given a water map and elevation data
    Original paper: https://doi.org/10.5194/nhess-19-2053-2019
    Earth Engine paper: https://doi.org/10.1109/LGRS.2020.3031190

    args:
        water_img (ee.Image): earth engine image object representing the water extent. Assumes values of 1 equal water.
        dem (ee.Image): earth engine image object representing elevation
        band (str | None): band name to use for algorithm, if set to `None` will use first band in image. default = None
        outlier_test (bool): flag used to find and filter outlier dem values around water boundaries. default = False
        force_projection (bool): flag used to force the output image projection to that of the input dem. default = False

    returns:
        ee.Image: image of water depth in meters
    """

    proj = dem.projection()
    res = proj.nominalScale().multiply(1.5)

    # select band to use for algorithm
    if band is None:
        water_img = water_img.select([0]).selfMask()

    else:
        water_img = water_img.select(ee.String(band)).selfMask()

    if outlier_test:
        filled = filtering.modified_median_zscore(dem)

        expand = water_img.focal_max(
            kernel=ee.Kernel.square(radius=res, units="meters")
        )

        dem_mask = dem.updateMask(water_img.gt(0))

        boundary = dem_mask.add(expand)

        filled = filtering.modified_median_zscore(boundary, filled)

    else:
        filled = dem

    # cumulative cost model

    mod = filled.updateMask(water_img.mask().eq(0))
    source = mod.mask()

    val = 10000
    push = 5000

    cost0 = ee.Image(val).where(source, 0).cumulativeCost(source, push)
    cost1 = ee.Image(val).where(source, 1).cumulativeCost(source, push)
    cost2 = mod.unmask(val).cumulativeCost(source, push)

    costFill = cost2.subtract(cost0).divide(cost1.subtract(cost0))

    costSurface = mod.unmask(0).add(costFill)

    # Kernel for low-pass filter
    boxcar = ee.Kernel.square(radius=3, units="pixels", normalize=True)

    # Floodwater depth calculation and smoothing using a low-pass filter
    depths = (
        costSurface.subtract(filled).convolve(boxcar).updateMask(water_img.mask())
    ).rename("depth")

    # force min values to be 0
    depths = depths.max(ee.Image.constant(0))

    # force the output project to be that of the input dem if force_projection is true
    if force_projection:
        depths = depths.reproject(proj)

    return depths

def fwdet_experimental(
    water_img,
    dem,
    iter=10,
    band=None,
    pixel_edge=0,
    boundary_definition="mean",
    smooth_depths=True,
    force_projection=False,
):
    """Experimantal changes to the implementation of the Flood Water Depth Estimation Tool
    Used to calculate water depth given a water map and elevation data

    Difference from original FwDET implementation include ...

    args:
        water_img (ee.Image): earth engine image object representing the water extent.
        dem (ee.Image): earth engine image object representing elevation
        iter (int|ee.Number, optional): keyword to set number of iterations to fill up every pixel.
            The algorithm will calculate minimum iterations needed and use the max between the two.
            default = 10
        band (str | None,optional): band name to use for algorithm, if set to `None` will use first band in image. default = None
        pixel_edge (int): value that represents the boundary/edge of water. Note: must be client-side int object. default = 0
        boundary_definition (str): method for defining the elevation at water edges. Options are 'mean' or 'nearest'
            Note: 'nearest' is the FwDET original method, 'mean' is an updated method. default=mean
        smooth_depths (bool): flag to control if the resulting water depths should be smoothed or not, uses a 3x3 pixel mean. default=True
        force_projection (bool): flag used to force the output image projection to that of the input dem. default = False

    returns:
        ee.Image: image of water depth in meters
    """
    proj = dem.projection()

    res = water_img.projection().nominalScale().multiply(1.5)

    # select band to use for algorithm
    if band is None:
        water_img = water_img.select([0])

    else:
        water_img = water_img.select(ee.String(band))

    # detect water edge
    watermap_edge = (
        water_img.selfMask().unmask(-999).focal_min(res, "square", "meters").eq(-999)
    )
    watermap_edge = watermap_edge.updateMask(water_img.unmask(0))
    watermap_edge = watermap_edge.selfMask()
    # watermap_edge = watermap_edge.reproject(water_img.projection());

    # detect watermap extent outer edge (not to be used in algorithm)
    if pixel_edge > 0:
        outer_edge = (
            water_img.mask()
            .unmask(0)
            .gte(0)
            .unmask(0, False)
            .gt(0)
            .focal_min(res.multiply(ee.Number(pixel_edge).add(1)), "square", "meters")
            .eq(0)
        )
        outer_edge = outer_edge.updateMask(outer_edge)
        # outer_edge = outer_edge.reproject(water_img.projection())
        # update watermap edge
        watermap_edge = watermap_edge.updateMask(outer_edge.unmask(0).eq(0))
        # watermap_edge = watermap_edge.reproject(water_img.projection());
    else:
        outer_edge = water_img.mask().unmask(0).lt(0)

    DEM = dem  # being super lazy here by setting a variable vs renaming things...

    # get elevation at edge
    # DEM_watered_edge = watermap_edge.multiply(DEM); # simply using boundary cell elevations
    DEM_masked = DEM.updateMask(
        water_img.unmask(0).eq(0).add(watermap_edge.unmask(0)).eq(1)
    )
    DEM_watered_edge = watermap_edge.multiply(
        DEM_masked.focal_mean(res, "square", "meters")
    )
    DEM_watered_edge = DEM_watered_edge  # .reproject(water_img.projection())
    # calculate (approximation of) minimum needed iterations to fill up every pixel
    req_iter = (
        water_img.eq(0)
        .add(watermap_edge)
        .fastDistanceTransform(50, "pixels", "squared_euclidean")
        .sqrt()
        .round()
        .updateMask(water_img)
        .updateMask(outer_edge.unmask(0).eq(0))
        .reproject(water_img.projection())
    )
    req_iter = req_iter.reduceRegion(
        reducer=ee.Reducer.max(),
        geometry=water_img.geometry().bounds(1e3),
        scale=res,
        maxPixels=1e12,
    ).get("distance")
    # set iterations to be used
    iter = ee.Number(req_iter).max(iter)
    # construct list to iterate over (list contents unused, just for iterations)
    iter_list = ee.List.sequence(0, iter)

    # use look up which algorithm to use for defining the boundary elevation values
    boundary_algos = {"mean": _mbce, "nearest": _nbce}
    f_boundary = partial(
        boundary_algos[boundary_definition], mask=water_img, resolution=res
    )

    # apply boundary condition algo, starting with edge values and filling it up further with each iteration
    DEM_watered_fill = ee.Image(iter_list.iterate(f_boundary, DEM_watered_edge))
    DEM_watered = DEM_watered_fill.reproject(water_img.projection())
    # derive water depths
    depths = DEM_watered.subtract(DEM).max(0)

    # smooth water depths
    # Cohen et al. (2018) use a 3x3 pixel mean (but FwDET-GEE seems to use a boxcar kernel?)
    if smooth_depths:
        depths = depths.reduceNeighborhood(
            reducer=ee.Reducer.mean(),
            kernel=ee.Kernel.square(res, units="meters"),
            optimization="boxcar",  # optimization for mean.
        )

    # force the output project to be that of the input dem if force_projection is true
    if force_projection:
        depths = depths.reproject(proj)

    return depths.rename("depth").set("depth_iter", iter)

# Mean Boundary Cell Elevation (MBCE), adapted from NBCE above
# MBCE function
def _mbce(i, img, mask=None, resolution=30):
    """Mean Boundary Cell Elevation (MBCE), unpublished A. Haag (2021)"""
    # obtain mean boundary cell elevation
    new_img = ee.Image(img).focal_mean(resolution, "square", "meters")
    # make sure the original / previous step values are kept intact (comment out for more smoothing)
    # new_img = new_img.where(ee.Image(img).gt(-999), ee.Image(img));
    # make sure it does not expand to other side of floodmap (where there is no water)
    return new_img.updateMask(mask.unmask(0))  # .reproject(floodmap.projection())

# Nearest Boundary Cell Elevation (NBCE) algorithm
def _nbce(i, img, mask=None, resolution=30):
    """Nearest Boundary Cell Elevation (NBCE), Cohen et al. (2018)"""
    # obtain nearest boundary cell elevations using different kernels
    new_img_0 = ee.Image(img).focal_min(resolution, "square", "meters")
    new_img_1 = ee.Image(img).focal_min(resolution, "plus", "meters")
    new_img_2 = ee.Image(img).focal_min(resolution, "cross", "meters")
    # make sure the original / previous step values are kept intact
    new_img_0 = new_img_0.where(ee.Image(img).gt(-999), ee.Image(img))
    new_img_1 = new_img_1.where(ee.Image(img).gt(-999), ee.Image(img))
    new_img_2 = new_img_2.where(ee.Image(img).gt(-999), ee.Image(img))
    # add the NBCE in right order (minimum distance at the end, overwrites diagonal)
    new_img = new_img_0.where(new_img_2.gt(-999), new_img_2)
    new_img = new_img.where(new_img_1.gt(-999), new_img_1)
    # make sure it does not expand to other side of floodmap (where there is no water)
    return new_img.updateMask(mask.unmask(0))

def downscale_wfraction(fraction, dem):
    """Algorithm to downscale water fraction maps using DEM data
    Converts fractional water data to higher resolution binary water data
    Paper: https://doi.org/10.1016/j.rse.2013.03.015

    args:
        fraction (ee.Image): earth engine image object representing the fractional water extent within each pixel.
        dem (ee.Image): earth engine image object representing elevation

    returns:
        ee.Image: downscaled binary image of water
    """

    resolution = fraction.projection().nominalScale()

    water_present = fraction.gt(0.0)

    h_min = dem.updateMask(water_present).focal_min(resolution, "square", "meters")
    h_max = dem.updateMask(water_present).focal_max(resolution, "square", "meters")

    water_high = h_min.add(h_max.subtract(h_min).multiply(fraction))

    return dem.gte(h_min).And(dem.lt(water_high))


import sys
sys.stdin = open('1051_input.txt')
# input = sys.stdin.readline

# 30864KB / 68ms

N, M = map(int, input().split())
arr = [[0] * M for _ in range(N)]
ans = 1
for i in range(N):
    numbers = input()
    for j in range(M):
        arr[i][j] = int(numbers[j])

stan = N if N < M else M
flag = False
for k in range(stan-1, 0, -1):
    for i in range(N-k):
        for j in range(M-k):
            if arr[i][j] == arr[i+k][j] == arr[i+k][j+k] == arr[i][j+k]:
                ans = (k+1)**2
                flag = True
            if flag: break
        if flag: break
    if flag: break

print(ans)


#ASPiRE UoWM 2019
#CanSat in Greece 2019

import os
import string
import time
import datetime
import serial
import board
import busio
import smbus
import adafruit_bmp280
from picamera import PiCamera, Color

cameracapture = True

#Telemetry serial port
Telemetry_SerialConnection = serial.Serial(port = '/dev/ttyUSB0', baudrate = 9600, timeout = 1)

# Create library object using Bus I2C port
i2c = busio.I2C(board.SCL, board.SDA)

bus = smbus.SMBus(1) # or bus = smbus.SMBus(0) for older version boards
#Device_Address = 0x68 # MPU6050 device address

if cameracapture == True:
	camera = PiCamera()
	camera.resolution = (3264, 2448)

bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c)

#GPS Serial port
GPS_port = "/dev/ttyAMA0"
GPS_SerialConnection = serial.Serial(GPS_port, baudrate = 9600, timeout = 1)

def timestamp():
	return time.time()

def read_GPS():
	GPS_SerialConnection.flushInput()
	GPS_Data = GPS_SerialConnection.readline() #Read raw data from GPS chipset (NEO-6M)
	GPS_DataString = str(GPS_Data) #Raw data is binary. We need string to process.

	if 'GGA,' in GPS_DataString:
		GPGGAStringDecoded = GPS_DataString[18:30] + "(Latitude)|" + GPS_DataString[32:44] + "(Longitude)"
		f = open("nmea_strings.txt", "a")
		f.write(GPS_DataString + "\n")
		f.close()
		return "|" + GPGGAStringDecoded

def read_GPS_Latitude():
	GPS_SerialConnection.flushInput()
	GPS_Data = GPS_SerialConnection.readline() #Read raw data from GPS chipset (NEO-6M)
	GPS_DataString = str(GPS_Data) #Raw data is binary. We need string to process.

	if 'GGA,' in GPS_DataString:
		Latitude = "Lat" + GPS_DataString[18:30] + "\n"
		return Latitude

def read_GPS_Longitude():
	GPS_SerialConnection.flushInput()
	GPS_Data = GPS_SerialConnection.readline() #Read raw data from GPS chipset (NEO-6M)
	GPS_DataString = str(GPS_Data) #Raw data is binary. We need string to process.

	if 'GGA,' in GPS_DataString:
		Longitude = "Lon" + GPS_DataString[32:44] + "\n"
		return Longitude

def read_BMP280():
	bmp280.sea_level_pressure = 1013.25
	sensordata = "|Temperature=%0.1f(C)" %bmp280.temperature + "|Pressure=%0.1f(hPa)" %bmp280.pressure + "|Altitude=%0.2f(m)" %bmp280.altitude
	return sensordata

def read_BMP280_Temperature():
	bmp280.sea_level_pressure = 1013.25
	sensordata = "Tmp%0.1f(C)\n" %bmp280.temperature
	return sensordata

def read_BMP280_Pressure():
	bmp280.sea_level_pressure = 1013.25
	sensordata = "Prs%0.1f(hPa)\n" %bmp280.pressure
	return sensordata

def read_BMP280_Altiture():
	bmp280.sea_level_pressure = 1013.25
	sensordata = "Alt%0.2f(m)\n" %bmp280.altitude
	return sensordata

def main():
	try:
		i = 0 #Counter for data lines

		while True:
			#Main Loop Start
			while True:
				gpsDataForFileTransmission = read_GPS()
				if type(gpsDataForFileTransmission) is str:
					break

			#mpu6050DataForFileTransmission = read_MPU6050()
			bmp280DataForFileTransmission = read_BMP280()
			finalData = "|" + str(i) + "|" + str(timestamp()) + gpsDataForFileTransmission + bmp280DataForFileTransmission
			print(finalData)
			i = i + 1
			f = open("measurements.txt", "a")
			f.write(finalData + "\n")
			f.close()

			TEMP = read_BMP280_Temperature()
			Telemetry_SerialConnection.write(TEMP.encode())
			time.sleep(1)
			PRESS = read_BMP280_Pressure()
			Telemetry_SerialConnection.write(PRESS.encode())
			time.sleep(1)
			ALT = read_BMP280_Altiture()
			Telemetry_SerialConnection.write(ALT.encode())
			time.sleep(1)
			while True:
				LAT = read_GPS_Latitude()
				if type(LAT) is str:
					break
			Telemetry_SerialConnection.write(LAT.encode())
			time.sleep(1)
			while True:
				LON = read_GPS_Longitude()
				if type(LON) is str:
					break
			Telemetry_SerialConnection.write(LON.encode())
			time.sleep(1)
			if cameracapture == True:
				camera.start_preview()
				time.sleep(1)
				print("Capturing format: jpeg | picture\n")
				camera.capture('images/image%s.jpg' %str(timestamp()), 'jpeg')
				camera.stop_preview()

			time.sleep(1)
			#Main Loop End
	except KeyboardInterrupt:
		GPS_SerialConnection.close()
		Telemetry_SerialConnection.close()

if __name__ == "__main__":
	main()