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

__author__ = 'm_beloborodko@wargaming.net'

import socket
from contextlib import closing
from os import path, makedirs, getenv, listdir
from packaging.version import Version
from pathlib import Path
from functools import reduce
from urllib.parse import urljoin

import yaml
import ruamel.yaml

import wgc_core


class SubConfig(object):

    def __init__(self, config, section_name):
        self.__config = config
        self._section_name = section_name

    def __repr__(self):
        return '<%s: %s>' % (self.__class__.__name__, self.config)

    @property
    def full_config(self):
        return self.__config.full_config

    @property
    def config(self):
        return self.__config.config.get(self._section_name)


class WGCApiConfig(SubConfig):

    @property
    def source_folder(self):
        api_source_folder = urljoin(WGCConfig.test_data.source_folder, 'wgc_api/')
        return api_source_folder

    @property
    def folder(self):
        return self.config['folder']

    @property
    def binary_name(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return 'wgc360_api.exe'
        if WGCConfig.wgc_publisher == 'steam':
            return 'wgcs_api.exe'
        return 'wgc_api.exe'

    @property
    def publication(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return 'WGC360'
        if WGCConfig.wgc_publisher == 'steam':
            return 'WGCSteam'
        return 'WGC'

    @property
    def binary_cs_name(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return 'wgc360_api_cs.exe'
        if WGCConfig.wgc_publisher == 'steam':
            return 'wgcs_api_cs.exe'
        return 'wgc_api_cs.exe'

    @property
    def library_name(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return 'wgc360_api.dll'
        if WGCConfig.wgc_publisher == 'steam':
            return 'wgcs_api.dll'
        return 'wgc_api.dll'

    @property
    def steam_library_name(self):
        return 'wgcs_api.dll'

    @property
    def wargaming_library_name(self):
        return 'wgc_api.dll'

    @property
    def qihoo_library_name(self):
        return 'wgc360_api.dll'

    @property
    def source_dll_path(self):
        if WGCConfig.wgc_arch != 'win32':
            return self.source_x64_dll_path
        return self.source_win32_dll_path

    @property
    def source_game_client_path(self):
        if WGCConfig.wgc_arch != 'win32':
            return self.source_x64_game_client_path
        return self.source_win32_game_client_path

    @property
    def source_shaders_path(self):
        if WGCConfig.wgc_arch != 'win32':
            return urljoin(WGCConfig.test_data.source_folder,
                           f'{WGCConfig.wgc_publisher}/steam_purchase_example/x64/shaders')
        return urljoin(WGCConfig.test_data.source_folder,
                       f'{WGCConfig.wgc_publisher}/steam_purchase_example/win32/shaders')

    @property
    def source_win32_game_client_path(self):
        path_ = urljoin(WGCConfig.test_data.source_folder,
                        f'{WGCConfig.wgc_publisher}/steam_purchase_example/win32/game_client.exe')
        return path_

    @property
    def source_x64_game_client_path(self):
        path_ = urljoin(WGCConfig.test_data.source_folder,
                        f'{WGCConfig.wgc_publisher}/steam_purchase_example/x64/game_client.exe')
        return path_

    @property
    def source_win32_dll_path(self):
        return urljoin(self.source_win32_dir_path, self.library_name)

    @property
    def source_steam_win32_dll_path(self):
        return urljoin(self.source_win32_dir_path, self.steam_library_name)

    @property
    def source_qihoo_win32_dll_path(self):
        return urljoin(self.source_win32_dir_path, self.qihoo_library_name)

    @property
    def source_qihoo_x64_dll_path(self):
        return urljoin(self.source_x64_dir_path, self.qihoo_library_name)

    @property
    def source_steam_x64_dll_path(self):
        return urljoin(self.source_x64_dir_path, self.steam_library_name)

    @property
    def source_x64_dll_path(self):
        return urljoin(self.source_x64_dir_path, self.library_name)

    @property
    def source_win32_dir_path(self):
        root_dir = 'wgc_api'
        path_ = urljoin(WGCConfig.test_data.source_folder, f'{root_dir}/bin/win32/')
        return path_

    @property
    def wgc_api_exe_path(self):
        path_ = urljoin(WGCConfig.test_data.source_folder,
                        f'{WGCConfig.wgc_publisher}/bin/{WGCConfig.wgc_arch}/api/'
                        f'{WGCConfig.application.api.binary_name}')
        return path_

    @property
    def source_x64_dir_path(self):
        root_dir = 'wgc_api'
        path_ = urljoin(WGCConfig.test_data.source_folder, f'{root_dir}/bin/x64/')
        return path_

    @property
    def version(self):
        return self.config.get('version')

    @version.setter
    def version(self, value):
        self.config['version'] = value


class ApplicationConfig(SubConfig):

    @property
    def installation_id(self):
        return self.config['installation-id']

    @installation_id.setter
    def installation_id(self, value):
        self.config['installation-id'] = value

    @property
    def is_final_configuration(self):
        return self.config['final-configuration']

    @is_final_configuration.setter
    def is_final_configuration(self, value):
        self.config['final-configuration'] = value

    @property
    def program_files_folder(self) -> str:
        return self.config['program-files-folder']

    @property
    def base_folder(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return 'c:\\ProgramData\\360 Wargaming\\GameCenter'
        if WGCConfig.wgc_publisher == 'steam':
            return 'c:\\ProgramData\\Wargaming.net\\GameCenter for Steam'
        return 'C:\\ProgramData\\Wargaming.net\\GameCenter'

    @property
    def folder(self):
        return self.config['folder']
    
    @folder.setter
    def folder(self, value):
        self.config['folder'] = value

    @property
    def cat_reports_dir(self):
        return self.config['cat-reports']

    @property
    def publisher_name(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return '360.cn'
        if WGCConfig.wgc_publisher == 'steam':
            return 'Wargaming.net Game Center for Steam'
        return 'Wargaming.net'

    @property
    def registry_key(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return '360 Wargaming Game Center'
        if WGCConfig.wgc_publisher == 'steam':
            return 'Wargaming.net Game Center for Steam'
        return 'Wargaming.net Game Center'

    @property
    def display_name(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return '360 Wargaming Game Center'
        if WGCConfig.wgc_publisher == 'steam':
            return 'Wargaming Game Center'
        return 'Wargaming Game Center'

    @property
    def registry_publisher(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return '360.cn'
        if WGCConfig.wgc_publisher == 'steam':
            return 'Wargaming Game Center'
        return 'Wargaming Game Center'

    @property
    def publisher_name_start_menu(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return '360.cn'
        if WGCConfig.wgc_publisher == 'steam':
            return 'Wargaming.net Game Center for Steam'
        return 'Wargaming.net'

    @property
    def wgc_id(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return 'WGC360.CN.PRODUCTION'
        if WGCConfig.wgc_publisher == 'steam':
            return 'WGCS.NA.PRODUCTION'
        return 'WGC.RU.PRODUCTION'

    @folder.setter
    def folder(self, value):
        self.config['folder'] = value

    @property
    def unicode_folder(self):
        return f'{self.folder}{"long path" * 5}\\Warga g.net\\坦克世界征程'

    @property
    def wgc_path_file(self):
        return 'wgc_path.dat'

    @property
    def wargaming_data_folder(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return path.join(getenv('APPDATA'), '360 Wargaming', 'GameCenter')
        if WGCConfig.wgc_publisher == 'steam':
            return path.join(getenv('APPDATA'), 'Wargaming.net', 'GameCenter for Steam')
        return path.join(getenv('APPDATA'), 'Wargaming.net', 'GameCenter')

    @property
    def user_info_file(self):
        return path.join(self.wargaming_data_folder, 'user_info.xml')

    @property
    def ntf_storage(self):
        return path.join(self.wargaming_data_folder, 'ntf_storage')

    @property
    def start_menu_folder_all_users(self):
        all_users_folder = getenv('ALLUSERSPROFILE')
        return path.join(all_users_folder, 'Microsoft', 'Windows', 'Start Menu')
    
    @property
    def userprofile(self):
        userprofile = getenv('USERPROFILE')
        if not userprofile:
            userprofile = r'C:\Users\wgc_test'
        return userprofile

    @property
    def start_menu_folder(self):
        from wgc_helpers.registry import RegistryHelper
        sub_key = "Software\\Microsoft\\" \
                  "Windows\\CurrentVersion\\Explorer\\User Shell Folders"
        return RegistryHelper.get_key_value(
            RegistryHelper.HKEY_CURRENT_USER,
            sub_key, 'Start Menu')[0].replace('%USERPROFILE%', self.userprofile)

    @property
    def alternative_start_menu_folder(self):
        path_ = path.join(
            getenv('USERPROFILE'), 'AppData', 'Roaming', 'Microsoft', 'Windows')
        path_ = path.join(path_, 'Start Menu', 'Programs', self.publisher_name)
        return path_

    @property
    def desktop_folder(self):
        return path.join(getenv(
            'PUBLIC', getenv('ALLUSERSPROFILE')), 'Desktop')
    
    @property
    def wnc_db(self):
        current_user = getenv('USERPROFILE')
        if '\\hpa.' in current_user:
            current_user = current_user.replace('\\hpa.', '\\')
        return path.join(current_user, 'AppData', 'Local', 'Microsoft',
                         'Windows', 'Notifications', 'wpndatabase.db')

    @property
    def user_desktop_folder(self):
        return path.join(self.userprofile, 'Desktop')

    @property
    def chrome_log_path(self):
        return 'c:\\chrome.json'

    @property
    def public_desktop_folder(self):
        return path.join(getenv('SystemDrive'), 'Users', 'Public', 'Desktop')

    @property
    def crash_dump_folder(self):
        return path.join(self.userprofile, 'Desktop', 'AppData', 'Local', 'CrashDumps')

    @property
    def binary_name(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return 'wgc.exe'
        if WGCConfig.wgc_publisher == 'steam':
            return 'wgc.exe'
        return 'wgc.exe'

    @property
    def watchdog_process(self):
        return self.config['watchdog-process']

    @property
    def renderer_process(self):
        return 'wgc_renderer_host.exe'

    @property
    def lnk_file(self):
        if WGCConfig.wgc_publisher != 'qihoo':
            return 'Game Center.lnk'
        return '360 Wargaming Game Center.lnk'

    @property
    def firewall_rule_name(self):
        return 'Wargaming.net Game Center in'

    @property
    def version_file(self):
        return self.config['version-file']

    @property
    def preferences_file(self):
        return self.config['preferences-file']

    @property
    def wgc_info_file(self):
        return self.config['wgc-info-file']

    @property
    def wgc_info_path(self):
        return path.join(WGCConfig.application.folder, self.wgc_info_file)

    @property
    def service_xml_file(self):
        return self.config['service-xml-file']

    @property
    def prefix(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return 'wgc360'
        if WGCConfig.wgc_publisher == 'steam':
            return 'wgcs'
        return 'wgc'
    
    @property
    def installer_realm(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return 'CN360'
        if WGCConfig.wgc_publisher == 'steam':
            return 'NA'
        return 'EU'

    @property
    def data_prefix(self):
        return 'wgc'

    @property
    def window_handle(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return '360战游平台'
        return 'Wargaming.net Game Center'

    @property
    def install_proposal_name(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return '360战游平台'
        return 'The Wargaming.net Game Center application'

    @property
    def app_name(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return '360战游平台'
        return 'Wargaming.net Game Center'

    @property
    def wgc_window_title_name(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return ['360战游平台', '360 Wargaming Game Center']
        return 'Wargaming.net Game Center'

    @property
    def installer_handle(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return '安装向导 - 360 Wargaming Game Center'
        return 'Wargaming.net Game Center'

    @property
    def full_binary_path(self):
        return path.join(self.folder, self.binary_name)

    @property
    def api(self):
        return WGCApiConfig(self, 'api')

    @property
    def preferences_protocol(self):
        return self.config['preferences-protocol']

    @property
    def wgc_info_protocol(self):
        return Version(str(self.config['wgc-info-protocol']))

    @property
    def user_info_protocol(self):
        return Version(str(self.config['user-info-protocol']))

    @property
    def user_info_import_protocol(self):
        return Version(str(self.config['user-import-info-protocol']))


class GameIdsConfig(SubConfig):

    @property
    def wot(self):
        return self.config['wot']

    @property
    def wowp(self):
        return self.config['wowp']

    @property
    def exc(self):
        return self.config['exc']

    @property
    def wows(self):
        return self.config['wows']


class GamesConfig(SubConfig):

    @property
    def folder(self):
        return self.config['folder']

    @property
    def game_info_file(self):
        return self.config['game-info-file']

    @property
    def game_info_protocol(self):
        return self.config['game-info-protocol']

    @property
    def game_info_prev_protocol(self):
        return self.config['game-info-prev-protocol']

    @property
    def metadata_one_part_file(self):
        return self.config['metadata-one-part-file']

    @property
    def ids(self):
        return GameIdsConfig(self, 'ids')

    @property
    def metadata_last_version(self):
        return str(self.config['metadata-last-version'])

    @metadata_last_version.setter
    def metadata_last_version(self, value):
        self.config['metadata-last-version'] = str(value)


class TestDataConfig(SubConfig):

    @property
    def source_folder(self):
        build = self.full_config['wgc-build']
        build = build + '/' if build else ''

        if self.full_config['share-builds-folder'].startswith('http'):
            result = reduce(
                urljoin, [self.full_config['share-builds-folder'],
                          self.full_config['wgc-branch'].replace(
                              '\\', '/') + '/', build])
        else:
            result = path.join(self.full_config['share-builds-folder'])
        return result

    @property
    def wgc_api_versions_folder(self):
        result = path.join(path.normpath(path.join(
            path.dirname(wgc_core.__file__), '..')), 'wgc_api_versions')
        return result

    @wgc_api_versions_folder.setter
    def wgc_api_versions_folder(self, value):
        self.config['wgc-api-versions-folder'] = value

    @property
    def dummy_games_folder(self):
        return self.config['dummy-games-folder']

    @property
    def torrent_hashes(self):
        from wgc_helpers.os_helper import OSHelper
        data = {}
        torrents = Path(self.dummy_games_folder).glob('**\\*.torrent')
        for torrent in torrents:
            name = path.basename(str(torrent))
            sha256 = OSHelper.get_file_sha256(str(torrent))
            data[name] = sha256
        return data

    @property
    def wgc_plugins_folder(self):
        result = reduce(
            urljoin, [self.full_config['share-artifacts'], 'plugins/'])
        return result

    @property
    def launcher_dummy_games_folder(self):
        result = reduce(
            urljoin, [self.full_config['share-artifacts'],
                      'launcher_dummy_game/'])
        return result

    @property
    def share_dummy_games_folder(self):
        result = f'{self.full_config["share-artifacts"]}wgc_dummy_game/wot/'
        return result

    @property
    def redist_exe_name(self):
        return '1507640920_directx_Jun2010_redist.exe'

    @property
    def share_redist_exe(self):
        result = f'{self.full_config["share-artifacts"]}{self.redist_exe_name}'
        return result


class JiraConfig(SubConfig):

    @property
    def url(self):
        return self.config['url']

    @property
    def username(self):
        return self.config['username']

    @property
    def password(self):
        return self.config['password']

    @property
    def project_id(self):
        return self.config['project_id']


class FBConfig(SubConfig):

    @property
    def username(self):
        return self.config['username']

    @property
    def email(self):
        return self.config['email']

    @property
    def password(self):
        return self.config['password']

    @property
    def spa_id(self):
        return self.config['user-id']


class GoogleConfig(SubConfig):

    @property
    def email(self):
        return self.config['email']

    @property
    def password(self):
        return self.config['password']


class MicrosoftConfig(SubConfig):

    @property
    def email(self):
        return self.config['email']

    @property
    def password(self):
        return self.config['password']


class WGConfig(SubConfig):

    @property
    def email(self):
        return self.config['email']

    @property
    def password(self):
        return self.config['password']

    @property
    def username(self):
        return self.config['username']


class SteamConfig(SubConfig):

    @property
    def email(self):
        return self.config['email']

    @property
    def password(self):
        return self.config['password']

    @property
    def username(self):
        return self.config['username']

    @property
    def steam_id(self):
        return self.config['steam_id']

    @property
    def games_dir(self):
        return self.config['games_dir']

    @property
    def attached_files(self):
        files = ['runprocess_log.txt', 'installscript_log.txt', 'steam_api_log.txt']
        return [path.join('c:\\Program Files (x86)\\Steam\\logs\\', f) for f in files]
    
    
class MocksConfig(SubConfig):
    
    @property
    def share_mocks(self):
        return self.config['share-mocks']

    @property
    def random_ports(self):
        return self.config['random-ports']

    @property
    def http_port(self):
        return self.config['http-port']

    @http_port.setter
    def http_port(self, port):
        self.config['http-port'] = port

    @property
    def https_port(self):
        return self.config['https-port']

    @https_port.setter
    def https_port(self, port):
        self.config['https-port'] = port
        
    @property
    def secure_content_service(self):
        return self.config['secure-content-service']


class WGCAPIExamplesConfig(SubConfig):
    
    @property
    def version_23_4(self):
        url = urljoin(self.full_config['share-builds-folder'], self.config[23.4])
        return url
    
    @property
    def version_24_0(self):
        url = urljoin(self.full_config['share-builds-folder'], self.config[24.0])
        return url
    
    @property
    def version_24_8(self):
        url = urljoin(self.full_config['share-builds-folder'], self.config[24.8])
        return url
    
    @property
    def version_25_0(self):
        url = urljoin(self.full_config['share-builds-folder'], self.config[25.0])
        return url


class SearchEventsConfig(SubConfig):

    @property
    def artifacts_upload(self):
        return self.config['artifacts_upload']

    @artifacts_upload.setter
    def artifacts_upload(self, value):
        self.config['artifacts_upload'] = value

    @property
    def assert_silent(self):
        return self.config['assert_silent']

    @assert_silent.setter
    def assert_silent(self, value):
        self.config['assert_silent'] = value

    @property
    def assert_fatal(self):
        return self.config['assert_fatal']

    @assert_fatal.setter
    def assert_fatal(self, value):
        self.config['assert_fatal'] = value

    @property
    def crash(self):
        return self.config['crash']

    @crash.setter
    def crash(self, value):
        self.config['crash'] = value

    @property
    def assert_track(self):
        return self.config['assert_track']

    @assert_track.setter
    def assert_track(self, value):
        self.config['assert_track'] = value

    @property
    def abnormal_termination(self):
        return self.config['abnormal_termination']

    @abnormal_termination.setter
    def abnormal_termination(self, value):
        self.config['abnormal_termination'] = value


class DbgDiagConfig(SubConfig):

    @property
    def dbg_diag_enabled(self):
        return self.config['dbg-diag-enabled']

    @dbg_diag_enabled.setter
    def dbg_diag_enabled(self, value):
        self.config['dbg-diag-enabled'] = value

    @property
    def pluginhost(self):
        return self.config['pluginhost']

    @pluginhost.setter
    def pluginhost(self, value):
        self.config['pluginhost'] = value

    @property
    def setup_file(self):
        return self.config['setup']

    @setup_file.setter
    def setup_file(self, value):
        self.config['setup'] = value

    @property
    def installer_file(self):
        return self.config['installer']

    @installer_file.setter
    def installer_file(self, value):
        self.config['installer'] = value

    @property
    def wgc_helper(self):
        return self.config['wgc_helper']

    @wgc_helper.setter
    def wgc_helper(self, value):
        self.config['wgc_helper'] = value

    @property
    def wgc_renderer(self):
        return self.config['wgc_renderer']

    @wgc_renderer.setter
    def wgc_renderer(self, value):
        self.config['wgc_renderer'] = value

    @property
    def overlay_test_x64(self):
        return self.config['overlay_test_x64']

    @overlay_test_x64.setter
    def overlay_test_x64(self, value):
        self.config['overlay_test_x64'] = value

    @property
    def overlay_test_x86(self):
        return self.config['overlay_test_x86']

    @overlay_test_x86.setter
    def overlay_test_x86(self, value):
        self.config['overlay_test_x86'] = value

    @property
    def wgc_exe(self):
        return self.config['wgc']

    @wgc_exe.setter
    def wgc_exe(self, value):
        self.config['wgc'] = value


class TestRailConfig(SubConfig):

    @property
    def url(self):
        return self.config['url']

    @property
    def project_id(self):
        return self.config['project-id']

    @property
    def user(self):
        return self.config['user']

    @property
    def password(self):
        return self.config['password']


class StatisticConfig(SubConfig):

    @property
    def required_franz(self):
        return ['installation_id', 'client_event_timestamp', 'event_index',
                'product_version', 'session_id', 'wgc_app_id',
                'wgc_realm', 'pc_id', 'product_mode']

    @property
    def redist_download_start_v1(self):
        data = ['redist_name', 'game_app_id', 'download_speed_limit', 'event_pair_id']
        data.extend(self.required_franz)
        return data

    @property
    def redist_download_finish_v1(self):
        data = ['redist_name', 'game_app_id', 'download_speed_limit', 'event_pair_id',
                'amount_of_data', 'result', 'torrent_hash_fail', 'download_time']
        data.extend(self.required_franz)
        return data

    @property
    def redist_install_start_v1(self):
        data = ['redist_name', 'game_app_id', 'event_pair_id']
        data.extend(self.required_franz)
        return data

    @property
    def redist_install_finish_v1(self):
        data = ['redist_name', 'game_app_id', 'event_pair_id', 'install_time', 'result']
        data.extend(self.required_franz)
        return data

    @property
    def change_ct_state_v1(self):
        data = ['from_ct_state', 'to_ct_state']
        data.extend(self.required_franz)
        return data

    @property
    def wgc_exit_installer_v1(self):
        data = ['inst_exit_method', 'inst_screen_id', 'preset_app_id']
        data.extend(self.required_franz)
        return data

    @property
    def program_post_install_start_v1(self):
        data = ['cli_args', 'file_name', 'event_pair_id', 'game_app_id']
        data.extend(self.required_franz)
        return data

    @property
    def program_post_install_finish_v1(self):
        data = ['install_time', 'file_name', 'event_pair_id', 'game_app_id', 'result']
        data.extend(self.required_franz)
        return data

    @property
    def program_pre_uninstall_start_v1(self):
        data = ['cli_args', 'file_name', 'event_pair_id', 'game_app_id']
        data.extend(self.required_franz)
        return data

    @property
    def program_pre_uninstall_finish_v1(self):
        data = ['uninstall_time', 'file_name', 'event_pair_id', 'game_app_id', 'result']
        data.extend(self.required_franz)
        return data

    @property
    def game_app_type_installed_v1(self):
        data = ['game_language', 'client_type', 'app_type', 'game_app_id']
        data.extend(self.required_franz)
        return data

    @property
    def game_launch_v1(self):
        data = ['game_app_id', 'game_language', 'game_version',
                'cli_args', 'parts', 'game_title_codes']
        data.extend(self.required_franz)
        return data

    @property
    def game_install_start_v1(self):
        data = ['game_app_id', 'game_language', 'installation_path',
                'event_pair_id', 'game_title_codes']
        data.extend(self.required_franz)
        return data

    @property
    def game_install_finish_v4(self):
        data = ['game_app_id', 'game_language', 'parts_from', 'parts_to',
                'event_pair_id', 'game_title_codes']
        data.extend(self.required_franz)
        return data

    @property
    def game_update_finish_v1(self):
        data = ['game_app_id', 'game_language', 'parts_from', 'parts_to',
                'event_pair_id', 'client_type', 'version_from', 'version_to']
        data.extend(self.required_franz)
        return data

    @property
    def game_update_start_v1(self):
        data = ['game_app_id', 'game_language', 'parts_from', 'parts_to',
                'event_pair_id', 'client_type', 'version_from', 'version_to',
                'auto_update_setting_value', 'is_auto_launch']
        data.extend(self.required_franz)
        return data

    @property
    def game_pause_process_v1(self):
        data = ['game_app_id', 'game_language', 'parts', 'event_pair_id']
        data.extend(self.required_franz)
        return data

    @property
    def game_part_install_finish_v1(self):
        data = ['game_app_id', 'game_language', 'part_from', 'part_to', 'event_pair_id', 'client_type',
                'is_reduced_pc_load', 'install_time', 'interrupted']
        data.extend(self.required_franz)
        return data

    @property
    def game_part_download_finish_v1(self):
        data = ['game_app_id', 'game_language', 'part_from', 'part_to', 'event_pair_id', 'client_type',
                'is_reduced_pc_load', 'part_size', 'download_speed_limit', 'download_time', 'interrupted',
                'torrent_hash_fail']
        data.extend(self.required_franz)
        return data

    @property
    def game_part_download_start_v1(self):
        data = ['game_app_id', 'game_language', 'part_from', 'part_to', 'event_pair_id', 'client_type',
                'is_reduced_pc_load', 'download_speed_limit']
        data.extend(self.required_franz)
        return data

    @property
    def game_part_integrity_start_v1(self):
        data = ['game_app_id', 'game_language', 'parts', 'is_auto_launch', 'event_pair_id']
        data.extend(self.required_franz)
        return data

    @property
    def game_part_integrity_finish_v1(self):
        data = ['game_app_id', 'game_language', 'parts', 'event_pair_id']
        data.extend(self.required_franz)
        return data

    @property
    def game_import_start_v2(self):
        data = ['game_app_id', 'game_language', 'is_auto_import', 'event_pair_id']
        data.extend(self.required_franz)
        return data

    @property
    def wgc_deletion_launch_v1(self):
        data = ['delete_games', 'number_of_games']
        data.extend(self.required_franz)
        return data

    @property
    def wgc_update_finish_v1(self):
        data = ['wgc_language', 'event_pair_id', 'parts_from', 'parts_to', 'version_from', 'version_to']
        data.extend(self.required_franz)
        return data

    @property
    def download_process_data_v2(self):
        data = ['amount_of_data_via_cdn', 'amount_of_data_via_p2p', 'amount_of_data_via_staticseeds',
                'total_amount_of_data', 'download_time', 'parts_from', 'parts_to', 'download_process',
                'game_app_id', 'is_completed', 'is_resumed', 'event_pair_id', 'unused_peers_count',
                'detailed_download_data', 'skipped_amount_of_data_via_p2p']
        data.extend(self.required_franz)
        return data

    @property
    def wgc_update_start_v1(self):
        data = ['wgc_language', 'event_pair_id', 'parts_from', 'parts_to', 'version_from', 'version_to']
        data.extend(self.required_franz)
        return data

    @property
    def game_import_finish_v2(self):
        data = ['game_app_id', 'game_language', 'event_pair_id']
        data.extend(self.required_franz)
        return data

    @property
    def wgc_steam_start_v3(self):
        data = ['steam_app_ids', 'steam_account_id']
        data.extend(self.required_franz)
        return data

    @property
    def game_patch_pre_download_start_v1(self):
        data = ['game_app_id', 'game_language', 'patch_file_name', 'event_pair_id', 'pre_download_speed_limit']
        data.extend(self.required_franz)
        return data

    @property
    def game_patch_pre_download_finish_v1(self):
        data = ['game_app_id', 'game_language', 'patch_file_name', 'event_pair_id', 'pre_download_speed_limit',
                'patch_size', 'download_time', 'interrupted', 'result', 'torrent_hash_fail']
        data.extend(self.required_franz)
        return data

    @property
    def game_part_install_start_v1(self):
        data = ['game_app_id', 'game_language', 'part_from', 'part_to', 'event_pair_id', 'client_type',
                'is_reduced_pc_load']
        data.extend(self.required_franz)
        return data

    @property
    def wgc_install_start_v1(self):
        data = ['event_pair_id', 'installation_path']
        data.extend(self.required_franz)
        return data

    @property
    def survey_v2(self):
        data = ['survey_id', 'answer_ids', 'game_app_id']
        data.extend(self.required_franz)
        return data

    @property
    def wgc_install_finish_v1(self):
        data = ['event_pair_id']
        data.extend(self.required_franz)
        return data

    @property
    def wgc_update_req_v1(self):
        data = ['parts']
        data.extend(self.required_franz)
        return data

    @property
    def game_update_req_v1(self):
        data = ['parts']
        data.extend(self.required_franz)
        return data

    @property
    def wgc_start_v1(self):
        data = ['cat_started']
        data.extend(self.required_franz)
        return data

    @property
    def wgc_exit_v1(self):
        data = ['session_duration']
        data.extend(self.required_franz)
        return data


class _WGCConfig(object):

    def __init__(self, wgc_config):
        self.full_config = self.config = wgc_config
        self.application = ApplicationConfig(self, 'application')
        self.games = GamesConfig(self, 'games')
        self.test_data = TestDataConfig(self, 'test-data')
        self.jira = JiraConfig(self, 'jira')
        self.fb = FBConfig(self, 'fb')
        self.google = GoogleConfig(self, 'google')
        self.microsoft = MicrosoftConfig(self, 'microsoft')
        self.wg = WGConfig(self, 'wg')
        self.steam = SteamConfig(self, 'steam')
        self.testrail = TestRailConfig(self, 'testrail')
        self.statistic = StatisticConfig(self, 'statistic')
        self.dbg_diag = DbgDiagConfig(self, 'dbg-diag')
        self.search_events = SearchEventsConfig(self, 'search-events')
        self.wgc_api_examples = WGCAPIExamplesConfig(self, 'wgc-api-examples')
        self.mocks = MocksConfig(self, 'mocks')
        self.cached_ip = self.ip_address
        
    @property
    def relaunch_failed_tests(self):
        return self.config['relaunch-failed-tests']
            
    @property
    def processes_list(self):
        return [
            'wgc.exe',
            'wgc_helper_host.exe',
            'wgc_renderer_host.exe',
            'WargamingErrorMonitor.exe',
            'wgc_api.exe',
            'wgcs_api.exe',
            'overlay_test_x64.exe',
            'overlay_test_x86.exe',
            'wgc360_api.exe',
            'full_screen_app.exe',
            'wgc.exe.update',
            'wgctmp_setup.tmp',
            'wgctmp_setup.exe',
            'qihoowgctmp_setup.tmp',
            'qihoowgctmp_setup.exe',
            'wgc360tmp_setup.tmp',
            'wgc360tmp_setup.exe',
            'pluginhost.exe',
            'pluginhost_l.exe',
            'dummy_game.exe',
            'setup.exe',
            'server32 - windows.exe',
            'rundll32.exe',
            '1507640920_directx_Jun2010_redist.exe',
            'DXSetup.exe',
            'steam.exe',
            'SteamOverlayUI.exe',
            'steamwebhelper.exe',
        ]

    @property
    def terminate_wgc_after_test(self):
        return self.config['terminate-wgc-after-test']

    @terminate_wgc_after_test.setter
    def terminate_wgc_after_test(self, value):
        self.config['terminate-wgc-after-test'] = value
    
    @property
    def terminate_steam_after_test(self):
        return self.config['terminate_steam_after_test']
    
    @terminate_steam_after_test.setter
    def terminate_steam_after_test(self, value):
        self.config['terminate_steam_after_test'] = value
    
    @property
    def remove_game_dir_before_test(self):
        return self.config['remove_game_dir_before_test']
    
    @remove_game_dir_before_test.setter
    def remove_game_dir_before_test(self, value):
        self.config['remove_game_dir_before_test'] = value

    @property
    def check_crash_wgc_after_test(self):
        return self.config['check-crash-wgc-after-test']

    @check_crash_wgc_after_test.setter
    def check_crash_wgc_after_test(self, value):
        self.config['check-crash-wgc-after-test'] = value

    @property
    def test_results_folder(self):
        return path.normpath(
            path.join(str(Path(wgc.__file__).parent.parent),
                      self.full_config['test-results-folder']))

    @property
    def test_cached_folder(self):
        return path.normpath(
            path.join(str(Path(wgc.__file__).parent.parent),
                      self.full_config['test-cached-folder']))

    @property
    def temp_folder(self):
        temp_folder_path = path.join(self.test_results_folder, 'temp')
        if not path.isdir(temp_folder_path):
            makedirs(temp_folder_path)
        return temp_folder_path

    @property
    def cached_folder(self):
        name = f'{WGCConfig.wgc_branch}_{WGCConfig.wgc_build}_{WGCConfig.wgc_arch}_{WGCConfig.wgc_publisher}'
        cache_folder = path.join(
            WGCConfig.test_cached_folder,
            name.encode().translate(bytes.maketrans(b':/\\', b'___')).decode())
        return cache_folder

    def cached_folder_for(self, publisher):
        name = f'{WGCConfig.wgc_branch}_{WGCConfig.wgc_build}_{WGCConfig.wgc_arch}_{publisher}'
        cache_folder = path.join(
            WGCConfig.test_cached_folder,
            name.encode().translate(bytes.maketrans(b':/\\', b'___')).decode())
        return cache_folder

    @property
    def web_seed_url(self):
        if self.full_config['web-seed-url']:
            return self.full_config['web-seed-url']
        art_patch = path.join(self.test_data.dummy_games_folder,
                              'wot', 'patches').replace('\\', '/').rstrip('/') + '/'
        return f'http://{self.ip_address}:{self.mocks.http_port}/download?file={art_patch}'

    @web_seed_url.setter
    def web_seed_url(self, value):
        self.full_config['web-seed-url'] = value

    @property
    def share_artifacts(self):
        return self.full_config['share-artifacts']

    @property
    def share_builds_folder(self):
        return self.full_config['share-builds-folder']

    @property
    def ip_address(self):
        if self.mocks.share_mocks:
            return socket.gethostbyname(socket.gethostname())
        return '127.0.0.1'

    @property
    def public_ip_address(self):
        return socket.gethostbyname(socket.gethostname())

    @property
    def free_ports(self):
        with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
            s.bind(('', 0))
            http_port = s.getsockname()[1]
            with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
                s.bind(('', 0))
                https_port = s.getsockname()[1]
        return http_port, https_port

    def init_free_ports(self):
        if self.mocks.random_ports:
            with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
                s.bind(('', 0))
                http_port = s.getsockname()[1]
                self.mocks.http_port = http_port
                print(f'Set http port = {http_port}')
                with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
                    s.bind(('', 0))
                    https_port = s.getsockname()[1]
                    self.mocks.https_port = https_port
                    print(f'Set https port = {https_port}')

    @property
    def wgni_url(self):
        return f'https://{self.ip_address}:{self.mocks.https_port}'

    @property
    def wgni_client_id(self):
        return 'lQBa5PrkTpSYcEOqdktRnblI1mcv6DEVV5JB4m4f'

    @property
    def wgcps_host(self):
        return f'{self.ip_address}:{self.mocks.https_port}'

    @property
    def wgc_branch(self):
        return self.full_config['wgc-branch'].replace('\\', '/')

    @property
    def wgc_publisher(self):
        return self.full_config['wgc-publisher']

    @property
    def wgc_arch(self):
        return self.full_config['wgc-arch']
    
    @property
    def wgc_configuration(self):
        return self.full_config['wgc-configuration']
    
    @wgc_configuration.setter
    def wgc_configuration(self, value):
        self.full_config['wgc-configuration'] = value

    @property
    def wgc_patch_arch(self):
        return 'win32' if self.wgc_arch == 'win32' else 'win64'

    @wgc_arch.setter
    def wgc_arch(self, value):
        self.full_config['wgc-arch'] = value

    @property
    def wgc_preferences_language(self):
        if self.wgc_publisher == 'qihoo':
            return 'zh_cn'
        return self.full_config['wgc-default-language']

    @wgc_preferences_language.setter
    def wgc_preferences_language(self, value):
        self.full_config['wgc-default-language'] = value

    @wgc_publisher.setter
    def wgc_publisher(self, value):
        self.full_config['wgc-publisher'] = value

    @property
    def wgc_build(self):
        return self.full_config['wgc-build']

    @wgc_build.setter
    def wgc_build(self, value):
        self.full_config['wgc-build'] = value

    @property
    def additional_disk(self):
        return self.full_config['additional-disk']

    @wgc_branch.setter
    def wgc_branch(self, value):
        self.full_config['wgc-branch'] = value

    @property
    def wgc_region(self):
        if WGCConfig.wgc_publisher == 'qihoo':
            return 'cn'
        if WGCConfig.wgc_publisher == 'steam':
            return 'na'
        return 'eu'

    @property
    def local_debug(self):
        return self.full_config['local-debug']

    @property
    def create_full_dump(self):
        return self.full_config['create-full-dump']

    @property
    def working_dir_root(self):
        return path.join(path.dirname(path.dirname(path.dirname(path.abspath(__file__)))))

    @property
    def working_dir_wgc(self):
        return path.join(path.dirname(path.dirname(path.abspath(__file__))))

    @property
    def create_ui_map(self):
        return self.full_config['create-ui-map']

    @create_ui_map.setter
    def create_ui_map(self, value):
        self.full_config['create-ui-map'] = value

    @property
    def save_wgc_logs_on_pass(self):
        return self.full_config['save-wgc-logs-on-pass']

    @save_wgc_logs_on_pass.setter
    def save_wgc_logs_on_pass(self, value):
        self.full_config['save-wgc-logs-on-pass'] = value

    @property
    def TIMEOUT_BEFORE_ACTION(self):
        return self.full_config['timeout-before-action']

    @property
    def TIMEOUT_AFTER_ACTION(self):
        return self.full_config['timeout-after-action']

    @property
    def upload_artifacts_to_share(self):
        return self.full_config['upload-artifacts-to-share']

    @property
    def winevent_enabled(self):
        return self.full_config['winevent-enabled']

    @winevent_enabled.setter
    def winevent_enabled(self, value):
        self.full_config['winevent-enabled'] = value

    @create_full_dump.setter
    def create_full_dump(self, value):
        self.full_config['create-full-dump'] = value

    @property
    def dbg_view_log_file(self):
        return self.full_config['dbg-view-log-file']

    @property
    def dbg_view_enabled(self):
        return self.full_config['dbg-view-enabled']

    @dbg_view_enabled.setter
    def dbg_view_enabled(self, value):
        self.full_config['dbg-view-enabled'] = value

    @property
    def procmon_enabled(self):
        return self.full_config['procmon-enabled']

    @procmon_enabled.setter
    def procmon_enabled(self, value):
        self.full_config['procmon-enabled'] = value

    @property
    def procmon_log_file(self):
        return self.full_config['procmon-log-file']

    @property
    def all_dlc_available(self):
        return self.full_config.get('all-dlc-available', False)

    @all_dlc_available.setter
    def all_dlc_available(self, value):
        self.full_config['all-dlc-available'] = value

    @property
    def capture_video(self):
        return self.full_config['capture-video']

    @capture_video.setter
    def capture_video(self, value):
        self.full_config['capture-video'] = value

    @property
    def enable_screenshots(self):
        return self.full_config['enable-screenshots']

    @enable_screenshots.setter
    def enable_screenshots(self, value):
        self.full_config['enable-screenshots'] = value

    @property
    def detect_black_screen(self):
        return self.full_config['detect-black-screen']

    @detect_black_screen.setter
    def detect_black_screen(self, value):
        self.full_config['detect-black-screen'] = value

    @property
    def sanity_check(self):
        return self.full_config['sanity-check']

    @sanity_check.setter
    def sanity_check(self, value):
        self.full_config['sanity-check'] = value

    @property
    def wgc_patch_folder(self):
        if WGCConfig.wgc_publisher == 'steam':
            prefix = 'wgcs'
        else:
            prefix = 'wgc'
        patch_path = f'{self.share_builds_folder}{self.wgc_branch}/' \
            f'{self.wgc_build}/{self.wgc_publisher}/patch/{prefix}_{self.wgc_build}_{self.wgc_region}/'
        return patch_path

    @property
    def wgc_patches_folder(self):
        patch_path = f'{self.share_builds_folder}{self.wgc_branch}/' \
                     f'{self.wgc_build}/{self.wgc_publisher}/patch/'
        return patch_path

    def patch_folder(self, publisher):
        if WGCConfig.wgc_publisher == 'steam':
            prefix = 'wgcs'
        else:
            prefix = 'wgc'
        patch_path = f'{self.share_builds_folder}{self.wgc_branch}/' \
                     f'{self.wgc_build}/{publisher}/patch/{prefix}_{self.wgc_build}_{self.wgc_region}/'
        return patch_path

    @property
    def cache_patch_folder(self):
        if self.wgc_publisher == 'steam':
            game = 'wgcs'
        else:
            game = 'wgc'
        patch_dir = f'{game}_{self.wgc_build}_{self.wgc_region}'
        cache_patch_folder = path.join(self.cached_folder, patch_dir)
        return cache_patch_folder

    @property
    def wgc_installer_folder(self):
        installer_path = f'{self.share_builds_folder}{self.wgc_branch}/' \
            f'{self.wgc_build}/{self.wgc_publisher}/installer/'
        return installer_path

    @property
    def wgc_offline_installer_folder(self):
        offline_installer_path = f'{self.share_builds_folder}{self.wgc_branch}/' \
            f'{self.wgc_build}/{self.wgc_publisher}/offline_installer/'
        return offline_installer_path

    @property
    def patches_available(self):
        return OSHelper.is_web_path_exist(self.wgc_patch_folder)

    @property
    def installers_available(self):
        return OSHelper.is_web_path_exist(self.wgc_installer_folder)

    @property
    def offline_installers_available(self):
        return OSHelper.is_web_path_exist(self.wgc_offline_installer_folder)

    @property
    def system_temp(self):
        return getenv('TEMP')

    @property
    def wgc_releases_folder(self):
        return f'{self.share_builds_folder}release/'

    @property
    def latest_wgc_api_header_file(self):
        root = path.join(path.normpath(path.join(
            path.dirname(wgc.__file__), '..')), 'wgc_api_versions')
        versions = []
        for release in listdir(root):
            versions.append(Version(release))
        return path.join(root, str(max(versions)), 'wgc_api.h')

    @property
    def wgc_launch_timeout(self):
        return self.full_config.get('wgc-launch-timeout', 100)

    @wgc_launch_timeout.setter
    def wgc_launch_timeout(self, value):
        self.full_config['wgc-launch-timeout'] = value

    @property
    def export_tests(self):
        return self.full_config.get('export_tests', False)

    @property
    def export_requirements(self):
        return self.full_config.get('export_requirements', False)

    @export_requirements.setter
    def export_requirements(self, value):
        self.full_config['export_requirements'] = value

    @property
    def tests_mark(self):
        return self.full_config.get('tests_mark', '')

    @tests_mark.setter
    def tests_mark(self, value):
        self.full_config['tests_mark'] = value

    @export_tests.setter
    def export_tests(self, value):
        self.full_config['export_tests'] = value

    @property
    def testrail_run(self):
        return self.full_config.get('testrail_run', None)

    @testrail_run.setter
    def testrail_run(self, value):
        self.full_config['testrail_run'] = value

    @property
    def wgc_tracking_id(self):
        return '2169874110830640531'

    def override_config(self, config):
        """
        Overrides current WGC config.

        :param dict config: new config.
        """
        self.full_config.update(config)
    
    def update_wgc_build(self, new_value, file_path='default.yaml'):
        """
        Updates the value of the 'wgc-build' key in the specified YAML file.
        """
        if not self.local_debug:
            yaml = ruamel.yaml.YAML()
            yaml.preserve_quotes = True
            yaml.indent(mapping=4, sequence=4, offset=2)
            
            if not path.exists(path.join(path.dirname(__file__), file_path)):
                return
            
            with open(path.join(path.dirname(__file__), file_path), 'r', encoding='utf-8') as file:
                data = yaml.load(file)
    
            data['wgc-build'] = new_value
            
            with open(path.join(path.dirname(__file__), file_path), 'w', encoding='utf-8') as file:
                yaml.dump(data, file)


def _load_default_config():
    default_config_name = 'default.yaml'
    config_path = path.join(path.dirname(__file__), default_config_name)
    with open(config_path, 'r') as default_config:
        config = yaml.safe_load(default_config)

    return _WGCConfig(config)


WGCConfig = _load_default_config()
