"""
@Author: vega13
"""
import platform
import time
import tkinter
import requests
from tkinter import *
from tkinter import filedialog
from tkinter import messagebox
from .request import json_request
from . import overwatch as OW
from . import game
from . import pool

def log(*args):
    print(args)

class Gui(object):
    def __init__(self):

        self.root = tkinter.Tk()
        self.root.title('Overwatch Replay Analyzer v0.1.2')
        self.root.geometry('550x350+400+200')

        self.read_path = None
        self.save_path = None
        self.left_frame = None
        self.right_frame = None
        # path
        self.create_path()
        self.create_time_inputs()
        # player
        self.create_player()
        # run
        self.run_btn = tkinter.Button(self.root, text="Analyze", command=self.run)
        self.run_btn.pack()
        self.create_text()
        # check for update
        self.root.after(500, self.check_update)

    def create_path(self):
        width_msg = 100
        width_path = 400

        path_frame = tkinter.Frame(self.root)
        path_frame.pack(fill=X)
        # read
        read_frame = tkinter.Frame(path_frame)
        read_frame.pack(fill=X)
        read_msg = tkinter.Message(read_frame, width=width_msg, text='Video path:')
        read_msg.pack(side=LEFT)
        read_path = tkinter.Message(read_frame, width=width_path, text='file1')
        read_path.pack(side=LEFT)
        read_btn = tkinter.Button(read_frame, text="Choose file", command=self.click_read)
        read_btn.pack(side=RIGHT)
        # save
        save_frame = tkinter.Frame(path_frame)
        save_frame.pack(fill=X)
        save_msg = tkinter.Message(save_frame, width=width_msg, text='Save to:')
        save_msg.pack(side=LEFT)
        save_path = tkinter.Message(save_frame, width=width_path, text='file2')
        save_path.pack(side=LEFT)
        save_btn = tkinter.Button(save_frame, text="Choose file", command=self.click_save)
        save_btn.pack(side=RIGHT)

        self.read_path = read_path
        self.save_path = save_path

    def create_player(self):
        player = tkinter.Frame(self.root)
        player.pack(fill=X, expand=1)
        # left
        left_frame = tkinter.Frame(player)
        left_frame.pack(side=LEFT)
        left_team_name = tkinter.Entry(left_frame, bg='pink', fg='black')
        left_team_name.insert(0, 'Team A')
        left_team_name.pack()
        for i in range(1, 7):
            e = tkinter.Entry(left_frame, bg='red', fg='white')
            name = 'Player' + str(i)
            e.insert(0, name)
            e.pack()
        # right
        right_frame = tkinter.Frame(player)
        right_frame.pack(side=RIGHT)
        right_team_name = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        right_team_name.insert(0, 'Team B')
        right_team_name.pack()
        for i in range(7, 13):
            e = tkinter.Entry(right_frame, bg='blue', fg='white')
            name = 'Player' + str(i)
            e.insert(0, name)
            e.pack()

        self.left_frame = left_frame
        self.right_frame = right_frame

    def create_time_inputs(self):
        time_inputs_frame = tkinter.Frame(self.root)
        time_inputs_frame.pack(fill=X, expand=1)

        left_frame = tkinter.Frame(time_inputs_frame)
        left_frame.pack(side=LEFT)

        label_start_time = tkinter.Label(left_frame, text="Start time in seconds (0 = start from beginning):")
        label_end_time = tkinter.Label(left_frame, text="End time in seconds (0 = analyze till the end):")
        label_fps = tkinter.Label(left_frame, text='FPS of analyzer:')
        label_game_type = tkinter.Label(left_frame, text='Game type (0 = OWL, 1 = Custom game):')
        label_game_version = tkinter.Label(left_frame, text='OWL stage number(0=preseason, 1, 2, 3, 4):')
        label_start_time.pack()
        label_end_time.pack()
        label_fps.pack()
        label_game_type.pack()
        label_game_version.pack()

        right_frame = tkinter.Frame(time_inputs_frame)
        right_frame.pack(side=RIGHT)

        start_time = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        start_time.insert(0, '0')
        start_time.pack()
        end_time = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        end_time.insert(0, '0')
        end_time.pack()
        fps = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        fps.insert(0, '2')
        fps.pack()
        game_type = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        game_type.insert(0, '0')
        game_type.pack()
        game_version = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        game_version.insert(0, '0')
        game_version.pack()

        self.time_inputs_frame = right_frame
    def create_text(self):
        self.notice_window = tkinter.Toplevel(self.root)
        self.notice_window.title('Notice')
        self.notice_window.geometry('400x400+300+100')
        self.notice =

        self.notice.insert(tkinter.INSERT,
            """Overwatch Replay Analyzer, a data extractor of Overwatch game replays

You can contact the author or report issues by: http://example.com""")
        self.notice.pack()

    def click_save(self):
        filename = filedialog.askdirectory(initialdir='~/')
        self.save_path.config(text=filename)

    def click_read(self):
        filename = filedialog.askopenfilename(initialdir='~/OW/OverwatchDataAnalysis/videos')
        self.read_path.config(text=filename)

    def check_update(self):
        import configparser
        config = configparser.ConfigParser()
        config.read('ora/etc/config.ini')

        if platform.system() == "Windows":
            osname = "win"
        else:
            osname = "osx"

        current_version = config['version']['client_version']
        r = requests.get(
                config['api']['url'] + config['api']['check_update']
                + osname + '/' + current_version
            ).json()

        if not r['is_latest']:
            tkinter.messagebox.showinfo('Update Available',
                'There is a newer version of ORA available, please download at \n{}'.format(r['url']))

    def info(self):
        valid = True
        info = {
            "name_team_left": "left",
            "name_team_right": "right",
            "name_players_team_left": [],
            "name_players_team_right": [],
            "video_path": "/",
            "output_path": "/",
            "start_time": 0,
            "end_time": 0,
            "fps": 0,
            "game_type": 0,
            "game_version": 1
        }
        frame_left = self.left_frame.pack_slaves()
        frame_right = self.right_frame.pack_slaves()
        time_inputs_frame = self.time_inputs_frame.pack_slaves()
        info['name_team_left'] = frame_left[0].get()
        info['name_team_right'] = frame_right[0].get()
        try:
            info['start_time'] = int(time_inputs_frame[0].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid video start time!')
            valid = False
            return [info, valid]

        try:
            info['end_time'] = int(time_inputs_frame[1].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid video end time!')
            valid = False
            return [info, valid]

        try:
            info['fps'] = int(time_inputs_frame[2].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid analysis fps!')
            valid = False
            return [info, valid]

        if not (info['end_time'] >= 0 and info['start_time'] >= 0 \
            and info['end_time'] >= info['start_time']):
            tkinter.messagebox.showinfo('Error', 'Invalid video end time!')
            valid = False
            return [info, valid]

        if not (info['fps'] > 0):
            tkinter.messagebox.showinfo('Error', 'Invalid analysis fps!')
            valid = False
            return [info, valid]

        try:
            info['game_type'] = int(time_inputs_frame[3].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid game type!')
            valid = False
            return [info, valid]

        try:
            info['game_version'] = int(time_inputs_frame[4].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid owl stage number!')
            valid = False
            return [info, valid]

        if not (info['game_type'] == 0 or info['game_type'] == 1):
            tkinter.messagebox.showinfo('Error', 'Invalid game type!')
            valid = False
            return [info, valid]

        if not (info['game_version'] >= 0 or info['game_version'] < 5):
            tkinter.messagebox.showinfo('Error', 'Invalid OWL stage number!')
            valid = False
            return [info, valid]

        team_left = []
        team_right = []
        for i in range(1, 7):
            player_left = frame_left[i].get()
            team_left.append(player_left)
            player_right = frame_right[i].get()
            team_right.append(player_right)

        info['name_players_team_left'] = team_left
        info['name_players_team_right'] = team_right
        info['video_path'] = self.read_path['text']
        info['output_path'] = self.save_path['text']

        return [info, valid]

    def show(self):
        self.root.mainloop()

    def show_finish_msg(self):
        tkinter.messagebox.showinfo('An alert box', 'Output file successfully saved!')

    def show_progress(self, progress):
        self.notice.insert(Tkinter.INSERT, str(progress))

    def run(self):
        info, valid = self.info()
        game_type = OW.GAMETYPE_OWL if info['game_type'] == 0 else OW.GAMETYPE_CUSTOM
        self.game_instance = game.Game(game_type)
        if valid is True:
            self.game_instance.set_game_info(info)
            pool.initPool()

            try:
                self.game_instance.analyze(info['start_time'], info['end_time'], is_test=False)
            except Exception as err:
                print(err)
            else:
                self.game_instance.output()
                self.show_finish_msg()

            pool.PROCESS_POOL.close()
            pool.PROCESS_POOL.join()

gui_instance = Gui()


for i in range(7, 13):
            e = tkinter.Entry(right_frame, bg='blue', fg='white')
            name = 'Player' + str(i)
            e.insert(0, name)
            e.pack()

"""
@Author: vega13
"""
import platform
import time
import tkinter
import requests
from tkinter import *
from tkinter import filedialog
from tkinter import messagebox
from .request import json_request
from . import overwatch as OW
from . import game
from . import pool

def log(*args):
    print(args)

class Gui(object):
    def __init__(self):

        self.root = tkinter.Tk()
        self.root.title('Overwatch Replay Analyzer v0.1.2')
        self.root.geometry('550x350+400+200')

        self.read_path = None
        self.save_path = None
        self.left_frame = None
        self.right_frame = None
        # path
        self.create_path()
        self.create_time_inputs()
        # player
        self.create_player()
        # run
        self.run_btn = tkinter.Button(self.root, text="Analyze", command=self.run)
        self.run_btn.pack()
        self.create_text()
        # check for update
        self.root.after(500, self.check_update)

    def create_path(self):
        width_msg = 100
        width_path = 400

        path_frame = tkinter.Frame(self.root)
        path_frame.pack(fill=X)
        # read
        read_frame = tkinter.Frame(path_frame)
        read_frame.pack(fill=X)
        read_msg = tkinter.Message(read_frame, width=width_msg, text='Video path:')
        read_msg.pack(side=LEFT)
        read_path = tkinter.Message(read_frame, width=width_path, text='file1')
        read_path.pack(side=LEFT)
        read_btn = tkinter.Button(read_frame, text="Choose file", command=self.click_read)
        read_btn.pack(side=RIGHT)
        # save
        save_frame = tkinter.Frame(path_frame)
        save_frame.pack(fill=X)
        save_msg = tkinter.Message(save_frame, width=width_msg, text='Save to:')
        save_msg.pack(side=LEFT)
        save_path = tkinter.Message(save_frame, width=width_path, text='file2')
        save_path.pack(side=LEFT)
        save_btn = tkinter.Button(save_frame, text="Choose file", command=self.click_save)
        save_btn.pack(side=RIGHT)

        self.read_path = read_path
        self.save_path = save_path

    def create_player(self):
        player = tkinter.Frame(self.root)
        player.pack(fill=X, expand=1)
        # left
        left_frame = tkinter.Frame(player)
        left_frame.pack(side=LEFT)
        left_team_name = tkinter.Entry(left_frame, bg='pink', fg='black')
        left_team_name.insert(0, 'Team A')
        left_team_name.pack()
        for i in range(1, 7):
            e = tkinter.Entry(left_frame, bg='red', fg='white')
            name = 'Player' + str(i)
            e.insert(0, name)
            e.pack()
        # right
        right_frame = tkinter.Frame(player)
        right_frame.pack(side=RIGHT)
        right_team_name = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        right_team_name.insert(0, 'Team B')
        right_team_name.pack()

        self.left_frame = left_frame
        self.right_frame = right_frame

    def create_time_inputs(self):
        time_inputs_frame = tkinter.Frame(self.root)
        time_inputs_frame.pack(fill=X, expand=1)

        left_frame = tkinter.Frame(time_inputs_frame)
        left_frame.pack(side=LEFT)

        label_start_time = tkinter.Label(left_frame, text="Start time in seconds (0 = start from beginning):")
        label_end_time = tkinter.Label(left_frame, text="End time in seconds (0 = analyze till the end):")
        label_fps = tkinter.Label(left_frame, text='FPS of analyzer:')
        label_game_type = tkinter.Label(left_frame, text='Game type (0 = OWL, 1 = Custom game):')
        label_game_version = tkinter.Label(left_frame, text='OWL stage number(0=preseason, 1, 2, 3, 4):')
        label_start_time.pack()
        label_end_time.pack()
        label_fps.pack()
        label_game_type.pack()
        label_game_version.pack()

        right_frame = tkinter.Frame(time_inputs_frame)
        right_frame.pack(side=RIGHT)

        start_time = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        start_time.insert(0, '0')
        start_time.pack()
        end_time = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        end_time.insert(0, '0')
        end_time.pack()
        fps = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        fps.insert(0, '2')
        fps.pack()
        game_type = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        game_type.insert(0, '0')
        game_type.pack()
        game_version = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        game_version.insert(0, '0')
        game_version.pack()

        self.time_inputs_frame = right_frame
    def create_text(self):
        self.notice_window = tkinter.Toplevel(self.root)
        self.notice_window.title('Notice')
        self.notice_window.geometry('400x400+300+100')
        self.notice = tkinter.Text(self.notice_window)
        self.notice.insert(tkinter.INSERT,
            """Overwatch Replay Analyzer, a data extractor of Overwatch game replays

You can contact the author or report issues by: http://example.com""")
        self.notice.pack()

    def click_save(self):
        filename = filedialog.askdirectory(initialdir='~/')
        self.save_path.config(text=filename)

    def click_read(self):
        filename = filedialog.askopenfilename(initialdir='~/OW/OverwatchDataAnalysis/videos')
        self.read_path.config(text=filename)

    def check_update(self):
        import configparser
        config = configparser.ConfigParser()
        config.read('ora/etc/config.ini')

        if platform.system() == "Windows":
            osname = "win"
        else:
            osname = "osx"

        current_version = config['version']['client_version']
        r = requests.get(
                config['api']['url'] + config['api']['check_update']
                + osname + '/' + current_version
            ).json()

        if not r['is_latest']:
            tkinter.messagebox.showinfo('Update Available',
                'There is a newer version of ORA available, please download at \n{}'.format(r['url']))

    def info(self):
        valid = True
        info = {
            "name_team_left": "left",
            "name_team_right": "right",
            "name_players_team_left": [],
            "name_players_team_right": [],
            "video_path": "/",
            "output_path": "/",
            "start_time": 0,
            "end_time": 0,
            "fps": 0,
            "game_type": 0,
            "game_version": 1
        }
        frame_left = self.left_frame.pack_slaves()
        frame_right = self.right_frame.pack_slaves()
        time_inputs_frame = self.time_inputs_frame.pack_slaves()
        info['name_team_left'] = frame_left[0].get()
        info['name_team_right'] = frame_right[0].get()
        try:
            info['start_time'] = int(time_inputs_frame[0].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid video start time!')
            valid = False
            return [info, valid]

        try:
            info['end_time'] = int(time_inputs_frame[1].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid video end time!')
            valid = False
            return [info, valid]

        try:
            info['fps'] = int(time_inputs_frame[2].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid analysis fps!')
            valid = False
            return [info, valid]

        if not (info['end_time'] >= 0 and info['start_time'] >= 0 \
            and info['end_time'] >= info['start_time']):
            tkinter.messagebox.showinfo('Error', 'Invalid video end time!')
            valid = False
            return [info, valid]

        if not (info['fps'] > 0):
            tkinter.messagebox.showinfo('Error', 'Invalid analysis fps!')
            valid = False
            return [info, valid]

        try:
            info['game_type'] = int(time_inputs_frame[3].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid game type!')
            valid = False
            return [info, valid]

        try:
            info['game_version'] = int(time_inputs_frame[4].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid owl stage number!')
            valid = False
            return [info, valid]

        if not (info['game_type'] == 0 or info['game_type'] == 1):
            tkinter.messagebox.showinfo('Error', 'Invalid game type!')
            valid = False
            return [info, valid]

        if not (info['game_version'] >= 0 or info['game_version'] < 5):
            tkinter.messagebox.showinfo('Error', 'Invalid OWL stage number!')
            valid = False
            return [info, valid]

        team_left = []
        team_right = []
        for i in range(1, 7):
            player_left = frame_left[i].get()
            team_left.append(player_left)
            player_right = frame_right[i].get()
            team_right.append(player_right)

        info['name_players_team_left'] = team_left
        info['name_players_team_right'] = team_right
        info['video_path'] = self.read_path['text']
        info['output_path'] = self.save_path['text']

        return [info, valid]

    def show(self):
        self.root.mainloop()

    def show_finish_msg(self):
        tkinter.messagebox.showinfo('An alert box', 'Output file successfully saved!')

    def show_progress(self, progress):
        self.notice.insert(Tkinter.INSERT, str(progress))

    def run(self):
        info, valid = self.info()
        game_type = OW.GAMETYPE_OWL if info['game_type'] == 0 else OW.GAMETYPE_CUSTOM
        self.game_instance = game.Game(game_type)
        if valid is True:
            self.game_instance.set_game_info(info)
            pool.initPool()

            try:
                self.game_instance.analyze(info['start_time'], info['end_time'], is_test=False)
            except Exception as err:
                print(err)
            else:
                self.game_instance.output()
                self.show_finish_msg()

            pool.PROCESS_POOL.close()
            pool.PROCESS_POOL.join()

gui_instance = Gui()


try:
            info['game_type'] = int(time_inputs_frame[3].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid game type!')
            valid = False
            return [info, valid]

"""
@Author: vega13
"""
import platform
import time
import tkinter
import requests
from tkinter import *
from tkinter import filedialog
from tkinter import messagebox
from .request import json_request
from . import overwatch as OW
from . import game
from . import pool

def log(*args):
    print(args)

class Gui(object):
    def __init__(self):

        self.root = tkinter.Tk()
        self.root.title('Overwatch Replay Analyzer v0.1.2')
        self.root.geometry('550x350+400+200')

        self.read_path = None
        self.save_path = None
        self.left_frame = None
        self.right_frame = None
        # path
        self.create_path()
        self.create_time_inputs()
        # player
        self.create_player()
        # run
        self.run_btn = tkinter.Button(self.root, text="Analyze", command=self.run)
        self.run_btn.pack()
        self.create_text()
        # check for update
        self.root.after(500, self.check_update)

    def create_path(self):
        width_msg = 100
        width_path = 400

        path_frame = tkinter.Frame(self.root)
        path_frame.pack(fill=X)
        # read
        read_frame = tkinter.Frame(path_frame)
        read_frame.pack(fill=X)
        read_msg = tkinter.Message(read_frame, width=width_msg, text='Video path:')
        read_msg.pack(side=LEFT)
        read_path = tkinter.Message(read_frame, width=width_path, text='file1')
        read_path.pack(side=LEFT)
        read_btn = tkinter.Button(read_frame, text="Choose file", command=self.click_read)
        read_btn.pack(side=RIGHT)
        # save
        save_frame = tkinter.Frame(path_frame)
        save_frame.pack(fill=X)
        save_msg = tkinter.Message(save_frame, width=width_msg, text='Save to:')
        save_msg.pack(side=LEFT)
        save_path = tkinter.Message(save_frame, width=width_path, text='file2')
        save_path.pack(side=LEFT)
        save_btn = tkinter.Button(save_frame, text="Choose file", command=self.click_save)
        save_btn.pack(side=RIGHT)

        self.read_path = read_path
        self.save_path = save_path

    def create_player(self):
        player = tkinter.Frame(self.root)
        player.pack(fill=X, expand=1)
        # left
        left_frame = tkinter.Frame(player)
        left_frame.pack(side=LEFT)
        left_team_name = tkinter.Entry(left_frame, bg='pink', fg='black')
        left_team_name.insert(0, 'Team A')
        left_team_name.pack()
        for i in range(1, 7):
            e = tkinter.Entry(left_frame, bg='red', fg='white')
            name = 'Player' + str(i)
            e.insert(0, name)
            e.pack()
        # right
        right_frame = tkinter.Frame(player)
        right_frame.pack(side=RIGHT)
        right_team_name = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        right_team_name.insert(0, 'Team B')
        right_team_name.pack()
        for i in range(7, 13):
            e = tkinter.Entry(right_frame, bg='blue', fg='white')
            name = 'Player' + str(i)
            e.insert(0, name)
            e.pack()

        self.left_frame = left_frame
        self.right_frame = right_frame

    def create_time_inputs(self):
        time_inputs_frame = tkinter.Frame(self.root)
        time_inputs_frame.pack(fill=X, expand=1)

        left_frame = tkinter.Frame(time_inputs_frame)
        left_frame.pack(side=LEFT)

        label_start_time = tkinter.Label(left_frame, text="Start time in seconds (0 = start from beginning):")
        label_end_time = tkinter.Label(left_frame, text="End time in seconds (0 = analyze till the end):")
        label_fps = tkinter.Label(left_frame, text='FPS of analyzer:')
        label_game_type = tkinter.Label(left_frame, text='Game type (0 = OWL, 1 = Custom game):')
        label_game_version = tkinter.Label(left_frame, text='OWL stage number(0=preseason, 1, 2, 3, 4):')
        label_start_time.pack()
        label_end_time.pack()
        label_fps.pack()
        label_game_type.pack()
        label_game_version.pack()

        right_frame = tkinter.Frame(time_inputs_frame)
        right_frame.pack(side=RIGHT)

        start_time = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        start_time.insert(0, '0')
        start_time.pack()
        end_time = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        end_time.insert(0, '0')
        end_time.pack()
        fps = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        fps.insert(0, '2')
        fps.pack()
        game_type = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        game_type.insert(0, '0')
        game_type.pack()
        game_version = tkinter.Entry(right_frame, bg='lightBlue', fg='black')
        game_version.insert(0, '0')
        game_version.pack()

        self.time_inputs_frame = right_frame
    def create_text(self):
        self.notice_window = tkinter.Toplevel(self.root)
        self.notice_window.title('Notice')
        self.notice_window.geometry('400x400+300+100')
        self.notice = tkinter.Text(self.notice_window)
        self.notice.insert(tkinter.INSERT,
            """Overwatch Replay Analyzer, a data extractor of Overwatch game replays

You can contact the author or report issues by: http://example.com""")
        self.notice.pack()

    def click_save(self):
        filename = filedialog.askdirectory(initialdir='~/')
        self.save_path.config(text=filename)

    def click_read(self):
        filename = filedialog.askopenfilename(initialdir='~/OW/OverwatchDataAnalysis/videos')
        self.read_path.config(text=filename)

    def check_update(self):
        import configparser
        config = configparser.ConfigParser()
        config.read('ora/etc/config.ini')

        if platform.system() == "Windows":
            osname = "win"
        else:
            osname = "osx"

        current_version = config['version']['client_version']
        r = requests.get(
                config['api']['url'] + config['api']['check_update']
                + osname + '/' + current_version
            ).json()

        if not r['is_latest']:
            tkinter.messagebox.showinfo('Update Available',
                'There is a newer version of ORA available, please download at \n{}'.format(r['url']))

    def info(self):
        valid = True
        info = {
            "name_team_left": "left",
            "name_team_right": "right",
            "name_players_team_left": [],
            "name_players_team_right": [],
            "video_path": "/",
            "output_path": "/",
            "start_time": 0,
            "end_time": 0,
            "fps": 0,
            "game_type": 0,
            "game_version": 1
        }
        frame_left = self.left_frame.pack_slaves()
        frame_right = self.right_frame.pack_slaves()
        time_inputs_frame = self.time_inputs_frame.pack_slaves()
        info['name_team_left'] = frame_left[0].get()
        info['name_team_right'] = frame_right[0].get()
        try:
            info['start_time'] = int(time_inputs_frame[0].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid video start time!')
            valid = False
            return [info, valid]

        try:
            info['end_time'] = int(time_inputs_frame[1].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid video end time!')
            valid = False
            return [info, valid]

        try:
            info['fps'] = int(time_inputs_frame[2].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid analysis fps!')
            valid = False
            return [info, valid]

        if not (info['end_time'] >= 0 and info['start_time'] >= 0 \
            and info['end_time'] >= info['start_time']):
            tkinter.messagebox.showinfo('Error', 'Invalid video end time!')
            valid = False
            return [info, valid]

        if not (info['fps'] > 0):
            tkinter.messagebox.showinfo('Error', 'Invalid analysis fps!')
            valid = False
            return [info, valid]

        try:
            info['game_version'] = int(time_inputs_frame[4].get())
        except ValueError:
            tkinter.messagebox.showinfo('Error', 'Invalid owl stage number!')
            valid = False
            return [info, valid]

        if not (info['game_type'] == 0 or info['game_type'] == 1):
            tkinter.messagebox.showinfo('Error', 'Invalid game type!')
            valid = False
            return [info, valid]

        if not (info['game_version'] >= 0 or info['game_version'] < 5):
            tkinter.messagebox.showinfo('Error', 'Invalid OWL stage number!')
            valid = False
            return [info, valid]

        team_left = []
        team_right = []
        for i in range(1, 7):
            player_left = frame_left[i].get()
            team_left.append(player_left)
            player_right = frame_right[i].get()
            team_right.append(player_right)

        info['name_players_team_left'] = team_left
        info['name_players_team_right'] = team_right
        info['video_path'] = self.read_path['text']
        info['output_path'] = self.save_path['text']

        return [info, valid]

    def show(self):
        self.root.mainloop()

    def show_finish_msg(self):
        tkinter.messagebox.showinfo('An alert box', 'Output file successfully saved!')

    def show_progress(self, progress):
        self.notice.insert(Tkinter.INSERT, str(progress))

    def run(self):
        info, valid = self.info()
        game_type = OW.GAMETYPE_OWL if info['game_type'] == 0 else OW.GAMETYPE_CUSTOM
        self.game_instance = game.Game(game_type)
        if valid is True:
            self.game_instance.set_game_info(info)
            pool.initPool()

            try:
                self.game_instance.analyze(info['start_time'], info['end_time'], is_test=False)
            except Exception as err:
                print(err)
            else:
                self.game_instance.output()
                self.show_finish_msg()

            pool.PROCESS_POOL.close()
            pool.PROCESS_POOL.join()

gui_instance = Gui()


def create_metric_alert_dimension(dimension_name, value_list, operator=None):

# pylint: disable=too-many-locals

from azure.cli.command_modules.monitor.util import get_operator_map, get_aggregation_map
from knack.log import get_logger

logger = get_logger(__name__)

_metric_alert_dimension_prefix = '_where_'

def create_metric_alert(client, resource_group_name, rule_name, scopes, condition, disabled=False, description=None,
                        tags=None, actions=None, severity=2, window_size='5m', evaluation_frequency='1m',
                        auto_mitigate=None, target_resource_type=None, target_resource_region=None):
    from azure.mgmt.monitor.models import (MetricAlertResource,
                                           MetricAlertSingleResourceMultipleMetricCriteria,
                                           MetricAlertMultipleResourceMultipleMetricCriteria,
                                           DynamicMetricCriteria)
    from azure.cli.core import CLIError
    # generate names for the conditions
    for i, cond in enumerate(condition):
        cond.name = 'cond{}'.format(i)
    criteria = None
    resource_type, scope_type = _parse_resource_and_scope_type(scopes)
    if scope_type in ['resource_group', 'subscription']:
        if target_resource_type is None or target_resource_region is None:
            raise CLIError('--target-resource-type and --target-resource-region must be provided.')
        criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
    else:
        if len(scopes) == 1:
            is_dynamic_threshold_criterion = False
            for v in condition:
                if isinstance(v, DynamicMetricCriteria):
                    is_dynamic_threshold_criterion = True
            if not is_dynamic_threshold_criterion:
                criteria = MetricAlertSingleResourceMultipleMetricCriteria(all_of=condition)
            else:
                criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
        else:
            criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
            target_resource_type = resource_type
            target_resource_region = target_resource_region if target_resource_region else 'global'

    kwargs = {
        'description': description,
        'severity': severity,
        'enabled': not disabled,
        'scopes': scopes,
        'evaluation_frequency': evaluation_frequency,
        'window_size': window_size,
        'criteria': criteria,
        'target_resource_type': target_resource_type,
        'target_resource_region': target_resource_region,
        'actions': actions,
        'tags': tags,
        'location': 'global',
        'auto_mitigate': auto_mitigate
    }
    return client.create_or_update(resource_group_name, rule_name, MetricAlertResource(**kwargs))

def update_metric_alert(instance, scopes=None, description=None, enabled=None, tags=None,
                        severity=None, window_size=None, evaluation_frequency=None, auto_mitigate=None,
                        add_actions=None, remove_actions=None, add_conditions=None, remove_conditions=None):
    if scopes is not None:
        instance.scopes = scopes
    if description is not None:
        instance.description = description
    if enabled is not None:
        instance.enabled = enabled
    if tags is not None:
        instance.tags = tags
    if severity is not None:
        instance.severity = severity
    if window_size is not None:
        instance.window_size = window_size
    if evaluation_frequency is not None:
        instance.evaluation_frequency = evaluation_frequency
    if auto_mitigate is not None:
        instance.auto_mitigate = auto_mitigate

    # process action removals
    if remove_actions is not None:
        instance.actions = [x for x in instance.actions if x.action_group_id.lower() not in remove_actions]

    # process action additions
    if add_actions is not None:
        add_action_ids = {x.action_group_id.lower() for x in add_actions}
        instance.actions = [x for x in instance.actions if x.action_group_id.lower() not in add_action_ids]
        instance.actions.extend(add_actions)

    # process condition removals
    if remove_conditions is not None:
        instance.criteria.all_of = [x for x in instance.criteria.all_of if x.name not in remove_conditions]

    def _get_next_name():
        i = 0
        while True:
            possible_name = 'cond{}'.format(i)
            match = next((x for x in instance.criteria.all_of if x.name == possible_name), None)
            if match:
                i = i + 1
                continue
            return possible_name

    # process condition additions
    if add_conditions is not None:
        for condition in add_conditions:
            condition.name = _get_next_name()
            instance.criteria.all_of.append(condition)

    return instance

values = ' or '.join(value_list)
    return '{} {} {} {}'.format(_metric_alert_dimension_prefix, dimension_name, operator, values)

def create_metric_alert_condition(condition_type, aggregation, metric_name, operator, metric_namespace='',
                                  dimension_list=None, threshold=None, alert_sensitivity=None,
                                  number_of_evaluation_periods=None, min_failing_periods_to_alert=None,
                                  ignore_data_before=None, skip_metric_validation=None):
    if metric_namespace:
        metric_namespace += '.'
    condition = "{} {}'{}' {} ".format(aggregation, metric_namespace, metric_name, operator)
    if condition_type == 'static':
        condition += '{} '.format(threshold)
    elif condition_type == 'dynamic':
        dynamics = 'dynamic {} {} of {} '.format(
            alert_sensitivity, min_failing_periods_to_alert, number_of_evaluation_periods)
        if ignore_data_before:
            dynamics += 'since {} '.format(ignore_data_before)
        condition += dynamics
    else:
        raise NotImplementedError()

    if dimension_list:
        dimensions = ' '.join([t for t in dimension_list if t.strip()])
        if dimensions.startswith(_metric_alert_dimension_prefix):
            dimensions = [t for t in dimensions.split(_metric_alert_dimension_prefix) if t]
            dimensions = 'where' + 'and'.join(dimensions)
        condition += dimensions

    if skip_metric_validation:
        condition += ' with skipmetricvalidation'

    return condition.strip()

def list_metric_alerts(client, resource_group_name=None):
    if resource_group_name:
        return client.list_by_resource_group(resource_group_name)
    return client.list_by_subscription()

def create_metric_rule(client, resource_group_name, rule_name, target, condition, description=None, disabled=False,
                       location=None, tags=None, email_service_owners=False, actions=None):
    from azure.mgmt.monitor.models import AlertRuleResource, RuleEmailAction
    condition.data_source.resource_uri = target
    custom_emails, webhooks, _ = _parse_actions(actions)
    actions = [
        RuleEmailAction(send_to_service_owners=email_service_owners, custom_emails=custom_emails)
    ] + (webhooks or [])
    rule = AlertRuleResource(
        location=location, name_properties_name=rule_name, is_enabled=not disabled,
        condition=condition, tags=tags, description=description, actions=actions)
    return client.create_or_update(resource_group_name, rule_name, rule)

def update_metric_rule(instance, target=None, condition=None, description=None, enabled=None, metric=None,
                       operator=None, threshold=None, aggregation=None, period=None, tags=None,
                       email_service_owners=None, add_actions=None, remove_actions=None):
    # Update general properties
    if description is not None:
        instance.description = description
    if enabled is not None:
        instance.is_enabled = enabled
    if tags is not None:
        instance.tags = tags

    # Update conditions
    if condition is not None:
        target = target or instance.condition.data_source.resource_uri
        instance.condition = condition

    if metric is not None:
        instance.condition.data_source.metric_name = metric
    if operator is not None:
        instance.condition.operator = get_operator_map()[operator]
    if threshold is not None:
        instance.condition.threshold = threshold
    if aggregation is not None:
        instance.condition.time_aggregation = get_aggregation_map()[aggregation]
    if period is not None:
        instance.condition.window_size = period

    if target is not None:
        instance.condition.data_source.resource_uri = target

    # Update actions
    emails, webhooks, curr_email_service_owners = _parse_actions(instance.actions)

    # process removals
    if remove_actions is not None:
        removed_emails, removed_webhooks = _parse_action_removals(remove_actions)
        emails = [x for x in emails if x not in removed_emails]
        webhooks = [x for x in webhooks if x.service_uri not in removed_webhooks]

    # process additions
    if add_actions is not None:
        added_emails, added_webhooks, _ = _parse_actions(add_actions)
        emails = list(set(emails) | set(added_emails))
        webhooks = webhooks + added_webhooks

    # Replace the existing actions array. This potentially restructures rules that were created
    # via other methods (Portal, ARM template). However, the functionality of these rules should
    # be the same.
    from azure.mgmt.monitor.models import RuleEmailAction
    if email_service_owners is None:
        email_service_owners = curr_email_service_owners
    actions = [RuleEmailAction(send_to_service_owners=email_service_owners, custom_emails=emails)] + webhooks
    instance.actions = actions

    return instance

def _parse_actions(actions):
    """ Actions come in as a combined list. This method separates the webhook actions into a
        separate collection and combines any number of email actions into a single email collection
        and a single value for `email_service_owners`. If any email action contains a True value
        for `send_to_service_owners` then it is assumed the entire value should be True. """
    from azure.mgmt.monitor.models import RuleEmailAction, RuleWebhookAction
    actions = actions or []
    email_service_owners = None
    webhooks = [x for x in actions if isinstance(x, RuleWebhookAction)]
    custom_emails = set()
    for action in actions:
        if isinstance(action, RuleEmailAction):
            if action.send_to_service_owners:
                email_service_owners = True
            custom_emails = custom_emails | set(action.custom_emails)
    return list(custom_emails), webhooks, email_service_owners

def _parse_action_removals(actions):
    """ Separates the combined list of keys to remove into webhooks and emails. """
    flattened = list({x for sublist in actions for x in sublist})
    emails = []
    webhooks = []
    for item in flattened:
        if item.startswith('http://') or item.startswith('https://'):
            webhooks.append(item)
        else:
            emails.append(item)
    return emails, webhooks

def _parse_resource_and_scope_type(scopes):
    from azure.mgmt.core.tools import parse_resource_id
    from azure.cli.core.azclierror import InvalidArgumentValueError

    if not scopes:
        raise InvalidArgumentValueError('scopes cannot be null.')

    namespace = ''
    resource_type = ''
    scope_type = None

    def validate_scope(item_namespace, item_resource_type, item_scope_type):
        if namespace != item_namespace or resource_type != item_resource_type or scope_type != item_scope_type:
            raise InvalidArgumentValueError('Multiple scopes should be the same resource type.')

    def store_scope(item_namespace, item_resource_type, item_scope_type):
        nonlocal namespace
        nonlocal resource_type
        nonlocal scope_type
        namespace = item_namespace
        resource_type = item_resource_type
        scope_type = item_scope_type

    def parse_one_scope_with_action(scope, operation_on_scope):
        result = parse_resource_id(scope)
        if 'namespace' in result and 'resource_type' in result:
            resource_types = [result['type']]
            child_idx = 1
            while 'child_type_{}'.format(child_idx) in result:
                resource_types.append(result['child_type_{}'.format(child_idx)])
                child_idx += 1
            operation_on_scope(result['namespace'], '/'.join(resource_types), 'resource')
        elif 'resource_group' in result:  # It's a resource group.
            operation_on_scope('', '', 'resource_group')
        elif 'subscription' in result:  # It's a subscription.
            operation_on_scope('', '', 'subscription')
        else:
            raise InvalidArgumentValueError('Scope must be a valid resource id.')

    # Store the resource type and scope type from first scope
    parse_one_scope_with_action(scopes[0], operation_on_scope=store_scope)
    # Validate the following scopes
    for item in scopes:
        parse_one_scope_with_action(item, operation_on_scope=validate_scope)

    return namespace + '/' + resource_type, scope_type


i = 0
        while True:
            possible_name = 'cond{}'.format(i)
            match = next((x for x in instance.criteria.all_of if x.name == possible_name), None)
            if match:
                i = i + 1
                continue
            return possible_name

# pylint: disable=too-many-locals

from azure.cli.command_modules.monitor.util import get_operator_map, get_aggregation_map
from knack.log import get_logger

logger = get_logger(__name__)

_metric_alert_dimension_prefix = '_where_'

def create_metric_alert(client, resource_group_name, rule_name, scopes, condition, disabled=False, description=None,
                        tags=None, actions=None, severity=2, window_size='5m', evaluation_frequency='1m',
                        auto_mitigate=None, target_resource_type=None, target_resource_region=None):
    from azure.mgmt.monitor.models import (MetricAlertResource,
                                           MetricAlertSingleResourceMultipleMetricCriteria,
                                           MetricAlertMultipleResourceMultipleMetricCriteria,
                                           DynamicMetricCriteria)
    from azure.cli.core import CLIError
    # generate names for the conditions
    for i, cond in enumerate(condition):
        cond.name = 'cond{}'.format(i)
    criteria = None
    resource_type, scope_type = _parse_resource_and_scope_type(scopes)
    if scope_type in ['resource_group', 'subscription']:
        if target_resource_type is None or target_resource_region is None:
            raise CLIError('--target-resource-type and --target-resource-region must be provided.')
        criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
    else:
        if len(scopes) == 1:
            is_dynamic_threshold_criterion = False
            for v in condition:
                if isinstance(v, DynamicMetricCriteria):
                    is_dynamic_threshold_criterion = True
            if not is_dynamic_threshold_criterion:
                criteria = MetricAlertSingleResourceMultipleMetricCriteria(all_of=condition)
            else:
                criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
        else:
            criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
            target_resource_type = resource_type
            target_resource_region = target_resource_region if target_resource_region else 'global'

    kwargs = {
        'description': description,
        'severity': severity,
        'enabled': not disabled,
        'scopes': scopes,
        'evaluation_frequency': evaluation_frequency,
        'window_size': window_size,
        'criteria': criteria,
        'target_resource_type': target_resource_type,
        'target_resource_region': target_resource_region,
        'actions': actions,
        'tags': tags,
        'location': 'global',
        'auto_mitigate': auto_mitigate
    }
    return client.create_or_update(resource_group_name, rule_name, MetricAlertResource(**kwargs))

def update_metric_alert(instance, scopes=None, description=None, enabled=None, tags=None,
                        severity=None, window_size=None, evaluation_frequency=None, auto_mitigate=None,
                        add_actions=None, remove_actions=None, add_conditions=None, remove_conditions=None):
    if scopes is not None:
        instance.scopes = scopes
    if description is not None:
        instance.description = description
    if enabled is not None:
        instance.enabled = enabled
    if tags is not None:
        instance.tags = tags
    if severity is not None:
        instance.severity = severity
    if window_size is not None:
        instance.window_size = window_size
    if evaluation_frequency is not None:
        instance.evaluation_frequency = evaluation_frequency
    if auto_mitigate is not None:
        instance.auto_mitigate = auto_mitigate

    # process action removals
    if remove_actions is not None:
        instance.actions = [x for x in instance.actions if x.action_group_id.lower() not in remove_actions]

    # process action additions
    if add_actions is not None:
        add_action_ids = {x.action_group_id.lower() for x in add_actions}
        instance.actions = [x for x in instance.actions if x.action_group_id.lower() not in add_action_ids]
        instance.actions.extend(add_actions)

    # process condition removals
    if remove_conditions is not None:
        instance.criteria.all_of = [x for x in instance.criteria.all_of if x.name not in remove_conditions]

    def _get_next_name():

    # process condition additions
    if add_conditions is not None:
        for condition in add_conditions:
            condition.name = _get_next_name()
            instance.criteria.all_of.append(condition)

    return instance

def create_metric_alert_dimension(dimension_name, value_list, operator=None):
    values = ' or '.join(value_list)
    return '{} {} {} {}'.format(_metric_alert_dimension_prefix, dimension_name, operator, values)

def create_metric_alert_condition(condition_type, aggregation, metric_name, operator, metric_namespace='',
                                  dimension_list=None, threshold=None, alert_sensitivity=None,
                                  number_of_evaluation_periods=None, min_failing_periods_to_alert=None,
                                  ignore_data_before=None, skip_metric_validation=None):
    if metric_namespace:
        metric_namespace += '.'
    condition = "{} {}'{}' {} ".format(aggregation, metric_namespace, metric_name, operator)
    if condition_type == 'static':
        condition += '{} '.format(threshold)
    elif condition_type == 'dynamic':
        dynamics = 'dynamic {} {} of {} '.format(
            alert_sensitivity, min_failing_periods_to_alert, number_of_evaluation_periods)
        if ignore_data_before:
            dynamics += 'since {} '.format(ignore_data_before)
        condition += dynamics
    else:
        raise NotImplementedError()

    if dimension_list:
        dimensions = ' '.join([t for t in dimension_list if t.strip()])
        if dimensions.startswith(_metric_alert_dimension_prefix):
            dimensions = [t for t in dimensions.split(_metric_alert_dimension_prefix) if t]
            dimensions = 'where' + 'and'.join(dimensions)
        condition += dimensions

    if skip_metric_validation:
        condition += ' with skipmetricvalidation'

    return condition.strip()

def list_metric_alerts(client, resource_group_name=None):
    if resource_group_name:
        return client.list_by_resource_group(resource_group_name)
    return client.list_by_subscription()

def create_metric_rule(client, resource_group_name, rule_name, target, condition, description=None, disabled=False,
                       location=None, tags=None, email_service_owners=False, actions=None):
    from azure.mgmt.monitor.models import AlertRuleResource, RuleEmailAction
    condition.data_source.resource_uri = target
    custom_emails, webhooks, _ = _parse_actions(actions)
    actions = [
        RuleEmailAction(send_to_service_owners=email_service_owners, custom_emails=custom_emails)
    ] + (webhooks or [])
    rule = AlertRuleResource(
        location=location, name_properties_name=rule_name, is_enabled=not disabled,
        condition=condition, tags=tags, description=description, actions=actions)
    return client.create_or_update(resource_group_name, rule_name, rule)

def update_metric_rule(instance, target=None, condition=None, description=None, enabled=None, metric=None,
                       operator=None, threshold=None, aggregation=None, period=None, tags=None,
                       email_service_owners=None, add_actions=None, remove_actions=None):
    # Update general properties
    if description is not None:
        instance.description = description
    if enabled is not None:
        instance.is_enabled = enabled
    if tags is not None:
        instance.tags = tags

    # Update conditions
    if condition is not None:
        target = target or instance.condition.data_source.resource_uri
        instance.condition = condition

    if metric is not None:
        instance.condition.data_source.metric_name = metric
    if operator is not None:
        instance.condition.operator = get_operator_map()[operator]
    if threshold is not None:
        instance.condition.threshold = threshold
    if aggregation is not None:
        instance.condition.time_aggregation = get_aggregation_map()[aggregation]
    if period is not None:
        instance.condition.window_size = period

    if target is not None:
        instance.condition.data_source.resource_uri = target

    # Update actions
    emails, webhooks, curr_email_service_owners = _parse_actions(instance.actions)

    # process removals
    if remove_actions is not None:
        removed_emails, removed_webhooks = _parse_action_removals(remove_actions)
        emails = [x for x in emails if x not in removed_emails]
        webhooks = [x for x in webhooks if x.service_uri not in removed_webhooks]

    # process additions
    if add_actions is not None:
        added_emails, added_webhooks, _ = _parse_actions(add_actions)
        emails = list(set(emails) | set(added_emails))
        webhooks = webhooks + added_webhooks

    # Replace the existing actions array. This potentially restructures rules that were created
    # via other methods (Portal, ARM template). However, the functionality of these rules should
    # be the same.
    from azure.mgmt.monitor.models import RuleEmailAction
    if email_service_owners is None:
        email_service_owners = curr_email_service_owners
    actions = [RuleEmailAction(send_to_service_owners=email_service_owners, custom_emails=emails)] + webhooks
    instance.actions = actions

    return instance

def _parse_actions(actions):
    """ Actions come in as a combined list. This method separates the webhook actions into a
        separate collection and combines any number of email actions into a single email collection
        and a single value for `email_service_owners`. If any email action contains a True value
        for `send_to_service_owners` then it is assumed the entire value should be True. """
    from azure.mgmt.monitor.models import RuleEmailAction, RuleWebhookAction
    actions = actions or []
    email_service_owners = None
    webhooks = [x for x in actions if isinstance(x, RuleWebhookAction)]
    custom_emails = set()
    for action in actions:
        if isinstance(action, RuleEmailAction):
            if action.send_to_service_owners:
                email_service_owners = True
            custom_emails = custom_emails | set(action.custom_emails)
    return list(custom_emails), webhooks, email_service_owners

def _parse_action_removals(actions):
    """ Separates the combined list of keys to remove into webhooks and emails. """
    flattened = list({x for sublist in actions for x in sublist})
    emails = []
    webhooks = []
    for item in flattened:
        if item.startswith('http://') or item.startswith('https://'):
            webhooks.append(item)
        else:
            emails.append(item)
    return emails, webhooks

def _parse_resource_and_scope_type(scopes):
    from azure.mgmt.core.tools import parse_resource_id
    from azure.cli.core.azclierror import InvalidArgumentValueError

    if not scopes:
        raise InvalidArgumentValueError('scopes cannot be null.')

    namespace = ''
    resource_type = ''
    scope_type = None

    def validate_scope(item_namespace, item_resource_type, item_scope_type):
        if namespace != item_namespace or resource_type != item_resource_type or scope_type != item_scope_type:
            raise InvalidArgumentValueError('Multiple scopes should be the same resource type.')

    def store_scope(item_namespace, item_resource_type, item_scope_type):
        nonlocal namespace
        nonlocal resource_type
        nonlocal scope_type
        namespace = item_namespace
        resource_type = item_resource_type
        scope_type = item_scope_type

    def parse_one_scope_with_action(scope, operation_on_scope):
        result = parse_resource_id(scope)
        if 'namespace' in result and 'resource_type' in result:
            resource_types = [result['type']]
            child_idx = 1
            while 'child_type_{}'.format(child_idx) in result:
                resource_types.append(result['child_type_{}'.format(child_idx)])
                child_idx += 1
            operation_on_scope(result['namespace'], '/'.join(resource_types), 'resource')
        elif 'resource_group' in result:  # It's a resource group.
            operation_on_scope('', '', 'resource_group')
        elif 'subscription' in result:  # It's a subscription.
            operation_on_scope('', '', 'subscription')
        else:
            raise InvalidArgumentValueError('Scope must be a valid resource id.')

    # Store the resource type and scope type from first scope
    parse_one_scope_with_action(scopes[0], operation_on_scope=store_scope)
    # Validate the following scopes
    for item in scopes:
        parse_one_scope_with_action(item, operation_on_scope=validate_scope)

    return namespace + '/' + resource_type, scope_type


# pylint: disable=too-many-locals

from azure.cli.command_modules.monitor.util import get_operator_map, get_aggregation_map
from knack.log import get_logger

logger = get_logger(__name__)

_metric_alert_dimension_prefix = '_where_'

def create_metric_alert(client, resource_group_name, rule_name, scopes, condition, disabled=False, description=None,
                        tags=None, actions=None, severity=2, window_size='5m', evaluation_frequency='1m',
                        auto_mitigate=None, target_resource_type=None, target_resource_region=None):
    from azure.mgmt.monitor.models import (MetricAlertResource,
                                           MetricAlertSingleResourceMultipleMetricCriteria,
                                           MetricAlertMultipleResourceMultipleMetricCriteria,
                                           DynamicMetricCriteria)
    from azure.cli.core import CLIError
    # generate names for the conditions
    for i, cond in enumerate(condition):
        cond.name = 'cond{}'.format(i)
    criteria = None
    resource_type, scope_type = _parse_resource_and_scope_type(scopes)
    if scope_type in ['resource_group', 'subscription']:
        if target_resource_type is None or target_resource_region is None:
            raise CLIError('--target-resource-type and --target-resource-region must be provided.')
        criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
    else:
        if len(scopes) == 1:
            is_dynamic_threshold_criterion = False
            for v in condition:
                if isinstance(v, DynamicMetricCriteria):
                    is_dynamic_threshold_criterion = True
            if not is_dynamic_threshold_criterion:
                criteria = MetricAlertSingleResourceMultipleMetricCriteria(all_of=condition)
            else:
                criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
        else:
            criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
            target_resource_type = resource_type
            target_resource_region = target_resource_region if target_resource_region else 'global'

    kwargs = {
        'description': description,
        'severity': severity,
        'enabled': not disabled,
        'scopes': scopes,
        'evaluation_frequency': evaluation_frequency,
        'window_size': window_size,
        'criteria': criteria,
        'target_resource_type': target_resource_type,
        'target_resource_region': target_resource_region,
        'actions': actions,
        'tags': tags,
        'location': 'global',
        'auto_mitigate': auto_mitigate
    }
    return client.create_or_update(resource_group_name, rule_name, MetricAlertResource(**kwargs))

def update_metric_alert(instance, scopes=None, description=None, enabled=None, tags=None,
                        severity=None, window_size=None, evaluation_frequency=None, auto_mitigate=None,
                        add_actions=None, remove_actions=None, add_conditions=None, remove_conditions=None):
    if scopes is not None:
        instance.scopes = scopes
    if description is not None:
        instance.description = description
    if enabled is not None:
        instance.enabled = enabled
    if tags is not None:
        instance.tags = tags
    if severity is not None:
        instance.severity = severity
    if window_size is not None:
        instance.window_size = window_size
    if evaluation_frequency is not None:
        instance.evaluation_frequency = evaluation_frequency
    if auto_mitigate is not None:
        instance.auto_mitigate = auto_mitigate

    # process action removals
    if remove_actions is not None:
        instance.actions = [x for x in instance.actions if x.action_group_id.lower() not in remove_actions]

    # process action additions
    if add_actions is not None:
        add_action_ids = {x.action_group_id.lower() for x in add_actions}
        instance.actions = [x for x in instance.actions if x.action_group_id.lower() not in add_action_ids]
        instance.actions.extend(add_actions)

    # process condition removals
    if remove_conditions is not None:
        instance.criteria.all_of = [x for x in instance.criteria.all_of if x.name not in remove_conditions]

    def _get_next_name():
        i = 0
        while True:
            possible_name = 'cond{}'.format(i)
            match = next((x for x in instance.criteria.all_of if x.name == possible_name), None)
            if match:
                i = i + 1
                continue
            return possible_name

    # process condition additions
    if add_conditions is not None:
        for condition in add_conditions:
            condition.name = _get_next_name()
            instance.criteria.all_of.append(condition)

    return instance

def create_metric_alert_dimension(dimension_name, value_list, operator=None):
    values = ' or '.join(value_list)
    return '{} {} {} {}'.format(_metric_alert_dimension_prefix, dimension_name, operator, values)

def create_metric_alert_condition(condition_type, aggregation, metric_name, operator, metric_namespace='',
                                  dimension_list=None, threshold=None, alert_sensitivity=None,
                                  number_of_evaluation_periods=None, min_failing_periods_to_alert=None,
                                  ignore_data_before=None, skip_metric_validation=None):
    if metric_namespace:
        metric_namespace += '.'
    condition = "{} {}'{}' {} ".format(aggregation, metric_namespace, metric_name, operator)
    if condition_type == 'static':
        condition += '{} '.format(threshold)
    elif condition_type == 'dynamic':
        dynamics = 'dynamic {} {} of {} '.format(
            alert_sensitivity, min_failing_periods_to_alert, number_of_evaluation_periods)
        if ignore_data_before:
            dynamics += 'since {} '.format(ignore_data_before)
        condition += dynamics
    else:
        raise NotImplementedError()

    if dimension_list:
        dimensions = ' '.join([t for t in dimension_list if t.strip()])
        if dimensions.startswith(_metric_alert_dimension_prefix):
            dimensions = [t for t in dimensions.split(_metric_alert_dimension_prefix) if t]
            dimensions = 'where' + 'and'.join(dimensions)
        condition += dimensions

    if skip_metric_validation:
        condition += ' with skipmetricvalidation'

    return condition.strip()

def list_metric_alerts(client, resource_group_name=None):
    if resource_group_name:
        return client.list_by_resource_group(resource_group_name)
    return client.list_by_subscription()

def create_metric_rule(client, resource_group_name, rule_name, target, condition, description=None, disabled=False,
                       location=None, tags=None, email_service_owners=False, actions=None):
    from azure.mgmt.monitor.models import AlertRuleResource, RuleEmailAction
    condition.data_source.resource_uri = target
    custom_emails, webhooks, _ = _parse_actions(actions)
    actions = [
        RuleEmailAction(send_to_service_owners=email_service_owners, custom_emails=custom_emails)
    ] + (webhooks or [])
    rule = AlertRuleResource(
        location=location, name_properties_name=rule_name, is_enabled=not disabled,
        condition=condition, tags=tags, description=description, actions=actions)
    return client.create_or_update(resource_group_name, rule_name, rule)

def update_metric_rule(instance, target=None, condition=None, description=None, enabled=None, metric=None,
                       operator=None, threshold=None, aggregation=None, period=None, tags=None,
                       email_service_owners=None, add_actions=None, remove_actions=None):
    # Update general properties
    if description is not None:
        instance.description = description
    if enabled is not None:
        instance.is_enabled = enabled
    if tags is not None:
        instance.tags = tags

    # Update conditions
    if condition is not None:
        target = target or instance.condition.data_source.resource_uri
        instance.condition = condition

    if metric is not None:
        instance.condition.data_source.metric_name = metric
    if operator is not None:
        instance.condition.operator = get_operator_map()[operator]
    if threshold is not None:
        instance.condition.threshold = threshold
    if aggregation is not None:
        instance.condition.time_aggregation = get_aggregation_map()[aggregation]
    if period is not None:
        instance.condition.window_size = period

    if target is not None:
        instance.condition.data_source.resource_uri = target

    # Update actions
    emails, webhooks, curr_email_service_owners = _parse_actions(instance.actions)

    # process removals
    if remove_actions is not None:
        removed_emails, removed_webhooks = _parse_action_removals(remove_actions)
        emails = [x for x in emails if x not in removed_emails]
        webhooks = [x for x in webhooks if x.service_uri not in removed_webhooks]

    # process additions
    if add_actions is not None:
        added_emails, added_webhooks, _ = _parse_actions(add_actions)
        emails = list(set(emails) | set(added_emails))
        webhooks = webhooks + added_webhooks

    # Replace the existing actions array. This potentially restructures rules that were created
    # via other methods (Portal, ARM template). However, the functionality of these rules should
    # be the same.
    from azure.mgmt.monitor.models import RuleEmailAction
    if email_service_owners is None:
        email_service_owners = curr_email_service_owners
    actions = [RuleEmailAction(send_to_service_owners=email_service_owners, custom_emails=emails)] + webhooks
    instance.actions = actions

    return instance

def _parse_actions(actions):
    """ Actions come in as a combined list. This method separates the webhook actions into a
        separate collection and combines any number of email actions into a single email collection
        and a single value for `email_service_owners`. If any email action contains a True value
        for `send_to_service_owners` then it is assumed the entire value should be True. """
    from azure.mgmt.monitor.models import RuleEmailAction, RuleWebhookAction
    actions = actions or []
    email_service_owners = None
    webhooks = [x for x in actions if isinstance(x, RuleWebhookAction)]
    custom_emails = set()
    for action in actions:
        if isinstance(action, RuleEmailAction):
            if action.send_to_service_owners:
                email_service_owners = True
            custom_emails = custom_emails | set(action.custom_emails)
    return list(custom_emails), webhooks, email_service_owners

def _parse_action_removals(actions):
    """ Separates the combined list of keys to remove into webhooks and emails. """
    flattened = list({x for sublist in actions for x in sublist})
    emails = []
    webhooks = []
    for item in flattened:
        if item.startswith('http://') or item.startswith('https://'):
            webhooks.append(item)
        else:
            emails.append(item)
    return emails, webhooks

def _parse_resource_and_scope_type(scopes):
    from azure.mgmt.core.tools import parse_resource_id
    from azure.cli.core.azclierror import InvalidArgumentValueError

    if not scopes:
        raise InvalidArgumentValueError('scopes cannot be null.')

    namespace = ''
    resource_type

    scope_type = None

    def validate_scope(item_namespace, item_resource_type, item_scope_type):
        if namespace != item_namespace or resource_type != item_resource_type or scope_type != item_scope_type:
            raise InvalidArgumentValueError('Multiple scopes should be the same resource type.')

    def store_scope(item_namespace, item_resource_type, item_scope_type):
        nonlocal namespace
        nonlocal resource_type
        nonlocal scope_type
        namespace = item_namespace
        resource_type = item_resource_type
        scope_type = item_scope_type

    def parse_one_scope_with_action(scope, operation_on_scope):
        result = parse_resource_id(scope)
        if 'namespace' in result and 'resource_type' in result:
            resource_types = [result['type']]
            child_idx = 1
            while 'child_type_{}'.format(child_idx) in result:
                resource_types.append(result['child_type_{}'.format(child_idx)])
                child_idx += 1
            operation_on_scope(result['namespace'], '/'.join(resource_types), 'resource')
        elif 'resource_group' in result:  # It's a resource group.
            operation_on_scope('', '', 'resource_group')
        elif 'subscription' in result:  # It's a subscription.
            operation_on_scope('', '', 'subscription')
        else:
            raise InvalidArgumentValueError('Scope must be a valid resource id.')

    # Store the resource type and scope type from first scope
    parse_one_scope_with_action(scopes[0], operation_on_scope=store_scope)
    # Validate the following scopes
    for item in scopes:
        parse_one_scope_with_action(item, operation_on_scope=validate_scope)

    return namespace + '/' + resource_type, scope_type


    flattened = list({x for sublist in actions for x in sublist})
    emails = []
    webhooks = []
    for item in flattened:
        if item.startswith('http://') or item.startswith('https://'):
            webhooks.append(item)
        else:
            emails.append(item)
    return emails, webhooks

# pylint: disable=too-many-locals

from azure.cli.command_modules.monitor.util import get_operator_map, get_aggregation_map
from knack.log import get_logger

logger = get_logger(__name__)

_metric_alert_dimension_prefix = '_where_'

def create_metric_alert(client, resource_group_name, rule_name, scopes, condition, disabled=False, description=None,
                        tags=None, actions=None, severity=2, window_size='5m', evaluation_frequency='1m',
                        auto_mitigate=None, target_resource_type=None, target_resource_region=None):
    from azure.mgmt.monitor.models import (MetricAlertResource,
                                           MetricAlertSingleResourceMultipleMetricCriteria,
                                           MetricAlertMultipleResourceMultipleMetricCriteria,
                                           DynamicMetricCriteria)
    from azure.cli.core import CLIError
    # generate names for the conditions
    for i, cond in enumerate(condition):
        cond.name = 'cond{}'.format(i)
    criteria = None
    resource_type, scope_type = _parse_resource_and_scope_type(scopes)
    if scope_type in ['resource_group', 'subscription']:
        if target_resource_type is None or target_resource_region is None:
            raise CLIError('--target-resource-type and --target-resource-region must be provided.')
        criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
    else:
        if len(scopes) == 1:
            is_dynamic_threshold_criterion = False
            for v in condition:
                if isinstance(v, DynamicMetricCriteria):
                    is_dynamic_threshold_criterion = True
            if not is_dynamic_threshold_criterion:
                criteria = MetricAlertSingleResourceMultipleMetricCriteria(all_of=condition)
            else:
                criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
        else:
            criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
            target_resource_type = resource_type
            target_resource_region = target_resource_region if target_resource_region else 'global'

    kwargs = {
        'description': description,
        'severity': severity,
        'enabled': not disabled,
        'scopes': scopes,
        'evaluation_frequency': evaluation_frequency,
        'window_size': window_size,
        'criteria': criteria,
        'target_resource_type': target_resource_type,
        'target_resource_region': target_resource_region,
        'actions': actions,
        'tags': tags,
        'location': 'global',
        'auto_mitigate': auto_mitigate
    }
    return client.create_or_update(resource_group_name, rule_name, MetricAlertResource(**kwargs))

def update_metric_alert(instance, scopes=None, description=None, enabled=None, tags=None,
                        severity=None, window_size=None, evaluation_frequency=None, auto_mitigate=None,
                        add_actions=None, remove_actions=None, add_conditions=None, remove_conditions=None):
    if scopes is not None:
        instance.scopes = scopes
    if description is not None:
        instance.description = description
    if enabled is not None:
        instance.enabled = enabled
    if tags is not None:
        instance.tags = tags
    if severity is not None:
        instance.severity = severity
    if window_size is not None:
        instance.window_size = window_size
    if evaluation_frequency is not None:
        instance.evaluation_frequency = evaluation_frequency
    if auto_mitigate is not None:
        instance.auto_mitigate = auto_mitigate

    # process action removals
    if remove_actions is not None:
        instance.actions = [x for x in instance.actions if x.action_group_id.lower() not in remove_actions]

    # process action additions
    if add_actions is not None:
        add_action_ids = {x.action_group_id.lower() for x in add_actions}
        instance.actions = [x for x in instance.actions if x.action_group_id.lower() not in add_action_ids]
        instance.actions.extend(add_actions)

    # process condition removals
    if remove_conditions is not None:
        instance.criteria.all_of = [x for x in instance.criteria.all_of if x.name not in remove_conditions]

    def _get_next_name():
        i = 0
        while True:
            possible_name = 'cond{}'.format(i)
            match = next((x for x in instance.criteria.all_of if x.name == possible_name), None)
            if match:
                i = i + 1
                continue
            return possible_name

    # process condition additions
    if add_conditions is not None:
        for condition in add_conditions:
            condition.name = _get_next_name()
            instance.criteria.all_of.append(condition)

    return instance

def create_metric_alert_dimension(dimension_name, value_list, operator=None):
    values = ' or '.join(value_list)
    return '{} {} {} {}'.format(_metric_alert_dimension_prefix, dimension_name, operator, values)

def create_metric_alert_condition(condition_type, aggregation, metric_name, operator, metric_namespace='',
                                  dimension_list=None, threshold=None, alert_sensitivity=None,
                                  number_of_evaluation_periods=None, min_failing_periods_to_alert=None,
                                  ignore_data_before=None, skip_metric_validation=None):
    if metric_namespace:
        metric_namespace += '.'
    condition = "{} {}'{}' {} ".format(aggregation, metric_namespace, metric_name, operator)
    if condition_type == 'static':
        condition += '{} '.format(threshold)
    elif condition_type == 'dynamic':
        dynamics = 'dynamic {} {} of {} '.format(
            alert_sensitivity, min_failing_periods_to_alert, number_of_evaluation_periods)
        if ignore_data_before:
            dynamics += 'since {} '.format(ignore_data_before)
        condition += dynamics
    else:
        raise NotImplementedError()

    if dimension_list:
        dimensions = ' '.join([t for t in dimension_list if t.strip()])
        if dimensions.startswith(_metric_alert_dimension_prefix):
            dimensions = [t for t in dimensions.split(_metric_alert_dimension_prefix) if t]
            dimensions = 'where' + 'and'.join(dimensions)
        condition += dimensions

    if skip_metric_validation:
        condition += ' with skipmetricvalidation'

    return condition.strip()

def list_metric_alerts(client, resource_group_name=None):
    if resource_group_name:
        return client.list_by_resource_group(resource_group_name)
    return client.list_by_subscription()

def create_metric_rule(client, resource_group_name, rule_name, target, condition, description=None, disabled=False,
                       location=None, tags=None, email_service_owners=False, actions=None):
    from azure.mgmt.monitor.models import AlertRuleResource, RuleEmailAction
    condition.data_source.resource_uri = target
    custom_emails, webhooks, _ = _parse_actions(actions)
    actions = [
        RuleEmailAction(send_to_service_owners=email_service_owners, custom_emails=custom_emails)
    ] + (webhooks or [])
    rule = AlertRuleResource(
        location=location, name_properties_name=rule_name, is_enabled=not disabled,
        condition=condition, tags=tags, description=description, actions=actions)
    return client.create_or_update(resource_group_name, rule_name, rule)

def update_metric_rule(instance, target=None, condition=None, description=None, enabled=None, metric=None,
                       operator=None, threshold=None, aggregation=None, period=None, tags=None,
                       email_service_owners=None, add_actions=None, remove_actions=None):
    # Update general properties
    if description is not None:
        instance.description = description
    if enabled is not None:
        instance.is_enabled = enabled
    if tags is not None:
        instance.tags = tags

    # Update conditions
    if condition is not None:
        target = target or instance.condition.data_source.resource_uri
        instance.condition = condition

    if metric is not None:
        instance.condition.data_source.metric_name = metric
    if operator is not None:
        instance.condition.operator = get_operator_map()[operator]
    if threshold is not None:
        instance.condition.threshold = threshold
    if aggregation is not None:
        instance.condition.time_aggregation = get_aggregation_map()[aggregation]
    if period is not None:
        instance.condition.window_size = period

    if target is not None:
        instance.condition.data_source.resource_uri = target

    # Update actions
    emails, webhooks, curr_email_service_owners = _parse_actions(instance.actions)

    # process removals
    if remove_actions is not None:
        removed_emails, removed_webhooks = _parse_action_removals(remove_actions)
        emails = [x for x in emails if x not in removed_emails]
        webhooks = [x for x in webhooks if x.service_uri not in removed_webhooks]

    # process additions
    if add_actions is not None:
        added_emails, added_webhooks, _ = _parse_actions(add_actions)
        emails = list(set(emails) | set(added_emails))
        webhooks = webhooks + added_webhooks

    # Replace the existing actions array. This potentially restructures rules that were created
    # via other methods (Portal, ARM template). However, the functionality of these rules should
    # be the same.
    from azure.mgmt.monitor.models import RuleEmailAction
    if email_service_owners is None:
        email_service_owners = curr_email_service_owners
    actions = [RuleEmailAction(send_to_service_owners=email_service_owners, custom_emails=emails)] + webhooks
    instance.actions = actions

    return instance

def _parse_actions(actions):
    """ Actions come in as a combined list. This method separates the webhook actions into a
        separate collection and combines any number of email actions into a single email collection
        and a single value for `email_service_owners`. If any email action contains a True value
        for `send_to_service_owners` then it is assumed the entire value should be True. """
    from azure.mgmt.monitor.models import RuleEmailAction, RuleWebhookAction
    actions = actions or []
    email_service_owners = None
    webhooks = [x for x in actions if isinstance(x, RuleWebhookAction)]
    custom_emails = set()
    for action in actions:
        if isinstance(action, RuleEmailAction):
            if action.send_to_service_owners:
                email_service_owners = True
            custom_emails = custom_emails | set(action.custom_emails)
    return list(custom_emails), webhooks, email_service_owners

def _parse_action_removals(actions):
    """ Separates the combined list of keys to remove into webhooks and emails. """

def _parse_resource_and_scope_type(scopes):
    from azure.mgmt.core.tools import parse_resource_id
    from azure.cli.core.azclierror import InvalidArgumentValueError

    if not scopes:
        raise InvalidArgumentValueError('scopes cannot be null.')

    namespace = ''
    resource_type = ''
    scope_type = None

    def validate_scope(item_namespace, item_resource_type, item_scope_type):
        if namespace != item_namespace or resource_type != item_resource_type or scope_type != item_scope_type:
            raise InvalidArgumentValueError('Multiple scopes should be the same resource type.')

    def store_scope(item_namespace, item_resource_type, item_scope_type):
        nonlocal namespace
        nonlocal resource_type
        nonlocal scope_type
        namespace = item_namespace
        resource_type = item_resource_type
        scope_type = item_scope_type

    def parse_one_scope_with_action(scope, operation_on_scope):
        result = parse_resource_id(scope)
        if 'namespace' in result and 'resource_type' in result:
            resource_types = [result['type']]
            child_idx = 1
            while 'child_type_{}'.format(child_idx) in result:
                resource_types.append(result['child_type_{}'.format(child_idx)])
                child_idx += 1
            operation_on_scope(result['namespace'], '/'.join(resource_types), 'resource')
        elif 'resource_group' in result:  # It's a resource group.
            operation_on_scope('', '', 'resource_group')
        elif 'subscription' in result:  # It's a subscription.
            operation_on_scope('', '', 'subscription')
        else:
            raise InvalidArgumentValueError('Scope must be a valid resource id.')

    # Store the resource type and scope type from first scope
    parse_one_scope_with_action(scopes[0], operation_on_scope=store_scope)
    # Validate the following scopes
    for item in scopes:
        parse_one_scope_with_action(item, operation_on_scope=validate_scope)

    return namespace + '/' + resource_type, scope_type


for item in flattened:
        if item.startswith('http://') or item.startswith('https://'):
            webhooks.append(item)
        else:
            emails.append(item)

# pylint: disable=too-many-locals

from azure.cli.command_modules.monitor.util import get_operator_map, get_aggregation_map
from knack.log import get_logger

logger = get_logger(__name__)

_metric_alert_dimension_prefix = '_where_'

def create_metric_alert(client, resource_group_name, rule_name, scopes, condition, disabled=False, description=None,
                        tags=None, actions=None, severity=2, window_size='5m', evaluation_frequency='1m',
                        auto_mitigate=None, target_resource_type=None, target_resource_region=None):
    from azure.mgmt.monitor.models import (MetricAlertResource,
                                           MetricAlertSingleResourceMultipleMetricCriteria,
                                           MetricAlertMultipleResourceMultipleMetricCriteria,
                                           DynamicMetricCriteria)
    from azure.cli.core import CLIError
    # generate names for the conditions
    for i, cond in enumerate(condition):
        cond.name = 'cond{}'.format(i)
    criteria = None
    resource_type, scope_type = _parse_resource_and_scope_type(scopes)
    if scope_type in ['resource_group', 'subscription']:
        if target_resource_type is None or target_resource_region is None:
            raise CLIError('--target-resource-type and --target-resource-region must be provided.')
        criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
    else:
        if len(scopes) == 1:
            is_dynamic_threshold_criterion = False
            for v in condition:
                if isinstance(v, DynamicMetricCriteria):
                    is_dynamic_threshold_criterion = True
            if not is_dynamic_threshold_criterion:
                criteria = MetricAlertSingleResourceMultipleMetricCriteria(all_of=condition)
            else:
                criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
        else:
            criteria = MetricAlertMultipleResourceMultipleMetricCriteria(all_of=condition)
            target_resource_type = resource_type
            target_resource_region = target_resource_region if target_resource_region else 'global'

    kwargs = {
        'description': description,
        'severity': severity,
        'enabled': not disabled,
        'scopes': scopes,
        'evaluation_frequency': evaluation_frequency,
        'window_size': window_size,
        'criteria': criteria,
        'target_resource_type': target_resource_type,
        'target_resource_region': target_resource_region,
        'actions': actions,
        'tags': tags,
        'location': 'global',
        'auto_mitigate': auto_mitigate
    }
    return client.create_or_update(resource_group_name, rule_name, MetricAlertResource(**kwargs))

def update_metric_alert(instance, scopes=None, description=None, enabled=None, tags=None,
                        severity=None, window_size=None, evaluation_frequency=None, auto_mitigate=None,
                        add_actions=None, remove_actions=None, add_conditions=None, remove_conditions=None):
    if scopes is not None:
        instance.scopes = scopes
    if description is not None:
        instance.description = description
    if enabled is not None:
        instance.enabled = enabled
    if tags is not None:
        instance.tags = tags
    if severity is not None:
        instance.severity = severity
    if window_size is not None:
        instance.window_size = window_size
    if evaluation_frequency is not None:
        instance.evaluation_frequency = evaluation_frequency
    if auto_mitigate is not None:
        instance.auto_mitigate = auto_mitigate

    # process action removals
    if remove_actions is not None:
        instance.actions = [x for x in instance.actions if x.action_group_id.lower() not in remove_actions]

    # process action additions
    if add_actions is not None:
        add_action_ids = {x.action_group_id.lower() for x in add_actions}
        instance.actions = [x for x in instance.actions if x.action_group_id.lower() not in add_action_ids]
        instance.actions.extend(add_actions)

    # process condition removals
    if remove_conditions is not None:
        instance.criteria.all_of = [x for x in instance.criteria.all_of if x.name not in remove_conditions]

    def _get_next_name():
        i = 0
        while True:
            possible_name = 'cond{}'.format(i)
            match = next((x for x in instance.criteria.all_of if x.name == possible_name), None)
            if match:
                i = i + 1
                continue
            return possible_name

    # process condition additions
    if add_conditions is not None:
        for condition in add_conditions:
            condition.name = _get_next_name()
            instance.criteria.all_of.append(condition)

    return instance

def create_metric_alert_dimension(dimension_name, value_list, operator=None):
    values = ' or '.join(value_list)
    return '{} {} {} {}'.format(_metric_alert_dimension_prefix, dimension_name, operator, values)

def create_metric_alert_condition(condition_type, aggregation, metric_name, operator, metric_namespace='',
                                  dimension_list=None, threshold=None, alert_sensitivity=None,
                                  number_of_evaluation_periods=None, min_failing_periods_to_alert=None,
                                  ignore_data_before=None, skip_metric_validation=None):
    if metric_namespace:
        metric_namespace += '.'
    condition = "{} {}'{}' {} ".format(aggregation, metric_namespace, metric_name, operator)
    if condition_type == 'static':
        condition += '{} '.format(threshold)
    elif condition_type == 'dynamic':
        dynamics = 'dynamic {} {} of {} '.format(
            alert_sensitivity, min_failing_periods_to_alert, number_of_evaluation_periods)
        if ignore_data_before:
            dynamics += 'since {} '.format(ignore_data_before)
        condition += dynamics
    else:
        raise NotImplementedError()

    if dimension_list:
        dimensions = ' '.join([t for t in dimension_list if t.strip()])
        if dimensions.startswith(_metric_alert_dimension_prefix):
            dimensions = [t for t in dimensions.split(_metric_alert_dimension_prefix) if t]
            dimensions = 'where' + 'and'.join(dimensions)
        condition += dimensions

    if skip_metric_validation:
        condition += ' with skipmetricvalidation'

    return condition.strip()

def list_metric_alerts(client, resource_group_name=None):
    if resource_group_name:
        return client.list_by_resource_group(resource_group_name)
    return client.list_by_subscription()

def create_metric_rule(client, resource_group_name, rule_name, target, condition, description=None, disabled=False,
                       location=None, tags=None, email_service_owners=False, actions=None):
    from azure.mgmt.monitor.models import AlertRuleResource, RuleEmailAction
    condition.data_source.resource_uri = target
    custom_emails, webhooks, _ = _parse_actions(actions)
    actions = [
        RuleEmailAction(send_to_service_owners=email_service_owners, custom_emails=custom_emails)
    ] + (webhooks or [])
    rule = AlertRuleResource(
        location=location, name_properties_name=rule_name, is_enabled=not disabled,
        condition=condition, tags=tags, description=description, actions=actions)
    return client.create_or_update(resource_group_name, rule_name, rule)

def update_metric_rule(instance, target=None, condition=None, description=None, enabled=None, metric=None,
                       operator=None, threshold=None, aggregation=None, period=None, tags=None,
                       email_service_owners=None, add_actions=None, remove_actions=None):
    # Update general properties
    if description is not None:
        instance.description = description
    if enabled is not None:
        instance.is_enabled = enabled
    if tags is not None:
        instance.tags = tags

    # Update conditions
    if condition is not None:
        target = target or instance.condition.data_source.resource_uri
        instance.condition = condition

    if metric is not None:
        instance.condition.data_source.metric_name = metric
    if operator is not None:
        instance.condition.operator = get_operator_map()[operator]
    if threshold is not None:
        instance.condition.threshold = threshold
    if aggregation is not None:
        instance.condition.time_aggregation = get_aggregation_map()[aggregation]
    if period is not None:
        instance.condition.window_size = period

    if target is not None:
        instance.condition.data_source.resource_uri = target

    # Update actions
    emails, webhooks, curr_email_service_owners = _parse_actions(instance.actions)

    # process removals
    if remove_actions is not None:
        removed_emails, removed_webhooks = _parse_action_removals(remove_actions)
        emails = [x for x in emails if x not in removed_emails]
        webhooks = [x for x in webhooks if x.service_uri not in removed_webhooks]

    # process additions
    if add_actions is not None:
        added_emails, added_webhooks, _ = _parse_actions(add_actions)
        emails = list(set(emails) | set(added_emails))
        webhooks = webhooks + added_webhooks

    # Replace the existing actions array. This potentially restructures rules that were created
    # via other methods (Portal, ARM template). However, the functionality of these rules should
    # be the same.
    from azure.mgmt.monitor.models import RuleEmailAction
    if email_service_owners is None:
        email_service_owners = curr_email_service_owners
    actions = [RuleEmailAction(send_to_service_owners=email_service_owners, custom_emails=emails)] + webhooks
    instance.actions = actions

    return instance

def _parse_actions(actions):
    """ Actions come in as a combined list. This method separates the webhook actions into a
        separate collection and combines any number of email actions into a single email collection
        and a single value for `email_service_owners`. If any email action contains a True value
        for `send_to_service_owners` then it is assumed the entire value should be True. """
    from azure.mgmt.monitor.models import RuleEmailAction, RuleWebhookAction
    actions = actions or []
    email_service_owners = None
    webhooks = [x for x in actions if isinstance(x, RuleWebhookAction)]
    custom_emails = set()
    for action in actions:
        if isinstance(action, RuleEmailAction):
            if action.send_to_service_owners:
                email_service_owners = True
            custom_emails = custom_emails | set(action.custom_emails)
    return list(custom_emails), webhooks, email_service_owners

def _parse_action_removals(actions):
    """ Separates the combined list of keys to remove into webhooks and emails. """
    flattened = list({x for sublist in actions for x in sublist})
    emails = []
    webhooks = []

    return emails, webhooks

def _parse_resource_and_scope_type(scopes):
    from azure.mgmt.core.tools import parse_resource_id
    from azure.cli.core.azclierror import InvalidArgumentValueError

    if not scopes:
        raise InvalidArgumentValueError('scopes cannot be null.')

    namespace = ''
    resource_type = ''
    scope_type = None

    def validate_scope(item_namespace, item_resource_type, item_scope_type):
        if namespace != item_namespace or resource_type != item_resource_type or scope_type != item_scope_type:
            raise InvalidArgumentValueError('Multiple scopes should be the same resource type.')

    def store_scope(item_namespace, item_resource_type, item_scope_type):
        nonlocal namespace
        nonlocal resource_type
        nonlocal scope_type
        namespace = item_namespace
        resource_type = item_resource_type
        scope_type = item_scope_type

    def parse_one_scope_with_action(scope, operation_on_scope):
        result = parse_resource_id(scope)
        if 'namespace' in result and 'resource_type' in result:
            resource_types = [result['type']]
            child_idx = 1
            while 'child_type_{}'.format(child_idx) in result:
                resource_types.append(result['child_type_{}'.format(child_idx)])
                child_idx += 1
            operation_on_scope(result['namespace'], '/'.join(resource_types), 'resource')
        elif 'resource_group' in result:  # It's a resource group.
            operation_on_scope('', '', 'resource_group')
        elif 'subscription' in result:  # It's a subscription.
            operation_on_scope('', '', 'subscription')
        else:
            raise InvalidArgumentValueError('Scope must be a valid resource id.')

    # Store the resource type and scope type from first scope
    parse_one_scope_with_action(scopes[0], operation_on_scope=store_scope)
    # Validate the following scopes
    for item in scopes:
        parse_one_scope_with_action(item, operation_on_scope=validate_scope)

    return namespace + '/' + resource_type, scope_type