Beispiel #1
0
 def __init__(self, layoutID, questID):
     settings = ViewSettings(layoutID)
     settings.model = ChallengeRewardViewModel()
     self.__questID = questID
     self.__viewLifecycleWatcher = ViewLifecycleWatcher()
     self._tooltips = {}
     super(ChallengeRewardView, self).__init__(settings)
 def __init__(self, _=None):
     super(MainView, self).__init__()
     self.__viewLifecycleWatcher = ViewLifecycleWatcher()
     self.fadeAnchorsOut = False
     self.__locatedOnEmbelem = False
     self.itemIsPicked = False
     self.__propertiesSheet = None
     self._seasonSoundAnimantion = None
     self._isPropertySheetShown = False
     self.__ctx = None
     self.__renderEnv = None
     return
 def __init__(self, _=None):
     super(MainView, self).__init__()
     self.__viewLifecycleWatcher = ViewLifecycleWatcher()
     self.fadeAnchorsOut = False
     self._currentSeason = SeasonType.SUMMER
     self._tabIndex = C11nTabs.PAINT
     self._lastTab = C11nTabs.PAINT
     self._originalStyle = None
     self._modifiedStyle = None
     self._isCurrentStyleInstalled = False
     self._originalOutfits = {}
     self._modifiedOutfits = {}
     self._currentOutfit = None
     self._mode = C11nMode.CUSTOM
     self._isDeferredRenderer = True
     self.__anchorPositionCallbackID = None
     self._state = {}
     self._needFullRebuild = False
     self.__locatedOnEmbelem = False
     self.itemIsPicked = False
     self._vehicleCustomizationAnchorsUpdater = None
     return
Beispiel #4
0
 def __init__(self, _=None):
     super(CamoSelectorMainView, self).__init__()
     self.__viewLifecycleWatcher = ViewLifecycleWatcher()
     self.fadeAnchorsOut = False
     self._currentSeason = SeasonType.SUMMER
     self._tabIndex = C11nTabs.CAMO_SHOP
     self._lastTab = C11nTabs.CAMO_SHOP
     self._originalOutfits = {}
     self._modifiedOutfits = {}
     self._currentOutfit = None
     self._setupOutfit = None
     self._mode = C11nMode.INSTALL
     self._currentSettings = {'custom': {}, 'remap': {}}
     self._randMode = RandMode.RANDOM
     self._ally = True
     self._enemy = True
     self._settingSeason = SeasonType.UNDEFINED
     self._isDeferredRenderer = True
     self.__anchorPositionCallbackID = None
     self._state = {}
     self._needFullRebuild = False
     self.__locatedOnEmbelem = False
     self.itemIsPicked = False
     self._vehicleCustomizationAnchorsUpdater = None
Beispiel #5
0
 def __init__(self, ctx=None):
     super(LobbyView, self).__init__(ctx)
     self.__currIgrType = constants.IGR_TYPE.NONE
     self.__viewLifecycleWatcher = ViewLifecycleWatcher()
     self._entityEnqueueCancelCallback = None
     return
Beispiel #6
0
class LobbyView(LobbyPageMeta, IWaitingWidget):
    class COMPONENTS(object):
        HEADER = 'lobbyHeader'

    itemsCache = dependency.descriptor(IItemsCache)
    igrCtrl = dependency.descriptor(IIGRController)
    lobbyContext = dependency.descriptor(ILobbyContext)

    def __init__(self, ctx=None):
        super(LobbyView, self).__init__(ctx)
        self.__currIgrType = constants.IGR_TYPE.NONE
        self.__viewLifecycleWatcher = ViewLifecycleWatcher()
        self._entityEnqueueCancelCallback = None
        return

    @proto_getter(PROTO_TYPE.BW_CHAT2)
    def bwProto(self):
        return None

    def showWaiting(self, message, _=False):
        self.as_showWaitingS(backport.text(message))

    def hideWaiting(self):
        self.as_hideWaitingS()

    def moveSpace(self, dx, dy, dz):
        self.fireEvent(
            CameraRelatedEvents(CameraRelatedEvents.LOBBY_VIEW_MOUSE_MOVE,
                                ctx={
                                    'dx': dx,
                                    'dy': dy,
                                    'dz': dz
                                }))
        self.fireEvent(
            events.LobbySimpleEvent(events.LobbySimpleEvent.NOTIFY_SPACE_MOVED,
                                    ctx={
                                        'dx': dx,
                                        'dy': dy,
                                        'dz': dz
                                    }))

    def notifyCursorOver3dScene(self, isOver3dScene):
        self.fireEvent(
            events.LobbySimpleEvent(
                events.LobbySimpleEvent.NOTIFY_CURSOR_OVER_3DSCENE,
                ctx={'isOver3dScene': isOver3dScene}))

    def notifyCursorDragging(self, isDragging):
        self.fireEvent(
            events.LobbySimpleEvent(
                events.LobbySimpleEvent.NOTIFY_CURSOR_DRAGGING,
                ctx={'isDragging': isDragging}))

    @uniprof.regionDecorator(label='account.show_gui', scope='enter')
    def _populate(self):
        View._populate(self)
        self.__currIgrType = self.igrCtrl.getRoomType()
        g_prbLoader.setEnabled(True)
        self.addListener(events.LobbySimpleEvent.SHOW_HELPLAYOUT,
                         self.__showHelpLayout, EVENT_BUS_SCOPE.LOBBY)
        self.addListener(events.LobbySimpleEvent.CLOSE_HELPLAYOUT,
                         self.__closeHelpLayout, EVENT_BUS_SCOPE.LOBBY)
        self.addListener(events.GameEvent.SCREEN_SHOT_MADE,
                         self.__handleScreenShotMade, EVENT_BUS_SCOPE.GLOBAL)
        self.addListener(events.GameEvent.HIDE_LOBBY_SUB_CONTAINER_ITEMS,
                         self.__hideSubContainerItems, EVENT_BUS_SCOPE.GLOBAL)
        self.addListener(events.GameEvent.REVEAL_LOBBY_SUB_CONTAINER_ITEMS,
                         self.__revealSubContainerItems,
                         EVENT_BUS_SCOPE.GLOBAL)
        self.addListener(events.LobbySimpleEvent.TURN_LOBBY_DRAGGING_ON,
                         self.__turnLobbyDraggingOn, EVENT_BUS_SCOPE.LOBBY)
        self.addListener(events.LobbySimpleEvent.TURN_LOBBY_DRAGGING_OFF,
                         self.__turnLobbyDraggingOff, EVENT_BUS_SCOPE.LOBBY)
        g_playerEvents.onEntityCheckOutEnqueued += self._onEntityCheckoutEnqueued
        g_playerEvents.onAccountBecomeNonPlayer += self._onAccountBecomeNonPlayer
        viewLifecycleHandler = _LobbySubViewsLifecycleHandler()
        self.__viewLifecycleWatcher.start(self.app.containerManager,
                                          [viewLifecycleHandler])
        self.igrCtrl.onIgrTypeChanged += self.__onIgrTypeChanged
        battlesCount = self.itemsCache.items.getAccountDossier().getTotalStats(
        ).getBattlesCount()
        epicBattlesCount = self.itemsCache.items.getAccountDossier(
        ).getEpicBattleStats().getBattlesCount()
        self.lobbyContext.updateBattlesCount(battlesCount, epicBattlesCount)
        self.fireEvent(
            events.GUICommonEvent(events.GUICommonEvent.LOBBY_VIEW_LOADED))
        self.bwProto.voipController.invalidateMicrophoneMute()

    def _invalidate(self, *args, **kwargs):
        g_prbLoader.setEnabled(True)
        super(LobbyView, self)._invalidate(*args, **kwargs)

    @uniprof.regionDecorator(label='account.show_gui', scope='exit')
    def _dispose(self):
        self.igrCtrl.onIgrTypeChanged -= self.__onIgrTypeChanged
        self.__viewLifecycleWatcher.stop()
        g_playerEvents.onEntityCheckOutEnqueued -= self._onEntityCheckoutEnqueued
        g_playerEvents.onAccountBecomeNonPlayer -= self._onAccountBecomeNonPlayer
        if self._entityEnqueueCancelCallback:
            self._entityEnqueueCancelCallback = None
            g_eventBus.removeListener(ViewEventType.LOAD_VIEW,
                                      self._onEntityCheckoutCanceled,
                                      EVENT_BUS_SCOPE.LOBBY)
        self.removeListener(events.LobbySimpleEvent.SHOW_HELPLAYOUT,
                            self.__showHelpLayout, EVENT_BUS_SCOPE.LOBBY)
        self.removeListener(events.LobbySimpleEvent.CLOSE_HELPLAYOUT,
                            self.__closeHelpLayout, EVENT_BUS_SCOPE.LOBBY)
        self.removeListener(events.GameEvent.SCREEN_SHOT_MADE,
                            self.__handleScreenShotMade,
                            EVENT_BUS_SCOPE.GLOBAL)
        self.removeListener(events.GameEvent.HIDE_LOBBY_SUB_CONTAINER_ITEMS,
                            self.__hideSubContainerItems,
                            EVENT_BUS_SCOPE.GLOBAL)
        self.removeListener(events.GameEvent.REVEAL_LOBBY_SUB_CONTAINER_ITEMS,
                            self.__revealSubContainerItems,
                            EVENT_BUS_SCOPE.GLOBAL)
        self.removeListener(events.LobbySimpleEvent.TURN_LOBBY_DRAGGING_ON,
                            self.__turnLobbyDraggingOn, EVENT_BUS_SCOPE.LOBBY)
        self.removeListener(events.LobbySimpleEvent.TURN_LOBBY_DRAGGING_OFF,
                            self.__turnLobbyDraggingOff, EVENT_BUS_SCOPE.LOBBY)
        View._dispose(self)
        return

    def _onEntityCheckoutEnqueued(self, cancelCallback):
        g_eventBus.addListener(ViewEventType.LOAD_VIEW,
                               self._onEntityCheckoutCanceled,
                               EVENT_BUS_SCOPE.LOBBY)
        g_eventBus.handleEvent(
            LoadViewEvent(
                SFViewLoadParams(VIEW_ALIAS.BOOTCAMP_QUEUE_DIALOG_SHOW)),
            EVENT_BUS_SCOPE.LOBBY)
        self._entityEnqueueCancelCallback = cancelCallback

    def _onEntityCheckoutCanceled(self, event):
        if event.alias == VIEW_ALIAS.BOOTCAMP_QUEUE_DIALOG_CANCEL:
            g_eventBus.removeListener(ViewEventType.LOAD_VIEW,
                                      self._onEntityCheckoutCanceled,
                                      EVENT_BUS_SCOPE.LOBBY)
            if self._entityEnqueueCancelCallback:
                self._entityEnqueueCancelCallback()
            self._entityEnqueueCancelCallback = None
        return

    def _onAccountBecomeNonPlayer(self):
        self._entityEnqueueCancelCallback = None
        g_eventBus.removeListener(ViewEventType.LOAD_VIEW,
                                  self._onEntityCheckoutCanceled,
                                  EVENT_BUS_SCOPE.LOBBY)
        return

    def __showHelpLayout(self, _):
        self.as_showHelpLayoutS()

    def __closeHelpLayout(self, _):
        self.as_closeHelpLayoutS()

    def __handleScreenShotMade(self, event):
        if 'path' not in event.ctx:
            return
        SystemMessages.pushMessage(
            i18n.makeString('#menu:screenshot/save') %
            {'path': event.ctx['path']}, SystemMessages.SM_TYPE.Information)

    def __hideSubContainerItems(self, _):
        self.as_setSubContainerItemsVisibilityS(False)

    def __revealSubContainerItems(self, _):
        self.as_setSubContainerItemsVisibilityS(True)

    def __onIgrTypeChanged(self, roomType, xpFactor):
        icon = gui.makeHtmlString('html_templates:igr/iconSmall', 'premium')
        if roomType == constants.IGR_TYPE.PREMIUM:
            SystemMessages.pushMessage(i18n.makeString(
                SYSTEM_MESSAGES.IGR_CUSTOMIZATION_BEGIN, igrIcon=icon),
                                       type=SystemMessages.SM_TYPE.Information)
        elif roomType in [
                constants.IGR_TYPE.BASE, constants.IGR_TYPE.NONE
        ] and self.__currIgrType == constants.IGR_TYPE.PREMIUM:
            SystemMessages.pushMessage(i18n.makeString(
                SYSTEM_MESSAGES.IGR_CUSTOMIZATION_END, igrIcon=icon),
                                       type=SystemMessages.SM_TYPE.Information)
        self.__currIgrType = roomType

    def __turnLobbyDraggingOn(self, _):
        self.as_switchLobbyDraggingS(True)

    def __turnLobbyDraggingOff(self, _):
        self.as_switchLobbyDraggingS(False)
Beispiel #7
0
class LobbyView(LobbyPageMeta):
    class COMPONENTS:
        HEADER = 'lobbyHeader'

    itemsCache = dependency.descriptor(IItemsCache)
    igrCtrl = dependency.descriptor(IIGRController)
    lobbyContext = dependency.descriptor(ILobbyContext)

    def __init__(self, ctx=None):
        super(LobbyView, self).__init__(ctx)
        self.__currIgrType = constants.IGR_TYPE.NONE
        self.__viewLifecycleWatcher = ViewLifecycleWatcher()
        self._entityEnqueueCancelCallback = None
        return

    @proto_getter(PROTO_TYPE.BW_CHAT2)
    def bwProto(self):
        return None

    def _populate(self):
        View._populate(self)
        self.__currIgrType = self.igrCtrl.getRoomType()
        g_prbLoader.setEnabled(True)
        self.addListener(events.LobbySimpleEvent.SHOW_HELPLAYOUT,
                         self.__showHelpLayout, EVENT_BUS_SCOPE.LOBBY)
        self.addListener(events.LobbySimpleEvent.CLOSE_HELPLAYOUT,
                         self.__closeHelpLayout, EVENT_BUS_SCOPE.LOBBY)
        self.addListener(events.GameEvent.SCREEN_SHOT_MADE,
                         self.__handleScreenShotMade, EVENT_BUS_SCOPE.GLOBAL)
        g_playerEvents.onVehicleBecomeElite += self._onVehicleBecomeElite
        g_playerEvents.onEntityCheckOutEnqueued += self._onEntityCheckoutEnqueued
        g_playerEvents.onAccountBecomeNonPlayer += self._onAccountBecomeNonPlayer
        viewLifecycleHandler = _LobbySubViewsLifecycleHandler()
        self.__viewLifecycleWatcher.start(self.app.containerManager,
                                          [viewLifecycleHandler])
        self.igrCtrl.onIgrTypeChanged += self.__onIgrTypeChanged
        battlesCount = self.itemsCache.items.getAccountDossier().getTotalStats(
        ).getBattlesCount()
        self.lobbyContext.updateBattlesCount(battlesCount)
        self.fireEvent(
            events.GUICommonEvent(events.GUICommonEvent.LOBBY_VIEW_LOADED))
        self.bwProto.voipController.invalidateMicrophoneMute()

    def _invalidate(self, *args, **kwargs):
        g_prbLoader.setEnabled(True)
        super(LobbyView, self)._invalidate(*args, **kwargs)

    def _dispose(self):
        self.igrCtrl.onIgrTypeChanged -= self.__onIgrTypeChanged
        self.__viewLifecycleWatcher.stop()
        g_playerEvents.onVehicleBecomeElite -= self._onVehicleBecomeElite
        g_playerEvents.onEntityCheckOutEnqueued -= self._onEntityCheckoutEnqueued
        g_playerEvents.onAccountBecomeNonPlayer -= self._onAccountBecomeNonPlayer
        if self._entityEnqueueCancelCallback:
            self._entityEnqueueCancelCallback = None
            g_eventBus.removeListener(BootcampEvent.QUEUE_DIALOG_CANCEL,
                                      self._onEntityCheckoutCanceled,
                                      EVENT_BUS_SCOPE.LOBBY)
        self.removeListener(events.LobbySimpleEvent.SHOW_HELPLAYOUT,
                            self.__showHelpLayout, EVENT_BUS_SCOPE.LOBBY)
        self.removeListener(events.LobbySimpleEvent.CLOSE_HELPLAYOUT,
                            self.__closeHelpLayout, EVENT_BUS_SCOPE.LOBBY)
        self.removeListener(events.GameEvent.SCREEN_SHOT_MADE,
                            self.__handleScreenShotMade,
                            EVENT_BUS_SCOPE.GLOBAL)
        View._dispose(self)
        return

    def __showHelpLayout(self, _):
        self.as_showHelpLayoutS()

    def __closeHelpLayout(self, _):
        self.as_closeHelpLayoutS()

    def __handleScreenShotMade(self, event):
        if 'path' not in event.ctx:
            return
        SystemMessages.pushMessage(
            i18n.makeString('#menu:screenshot/save') %
            {'path': event.ctx['path']}, SystemMessages.SM_TYPE.Information)

    def _onVehicleBecomeElite(self, vehTypeCompDescr):
        if not isPopupsWindowsOpenDisabled():
            self.fireEvent(
                events.LoadViewEvent(
                    VIEW_ALIAS.ELITE_WINDOW,
                    getViewName(VIEW_ALIAS.ELITE_WINDOW, vehTypeCompDescr),
                    {'vehTypeCompDescr': vehTypeCompDescr}),
                EVENT_BUS_SCOPE.LOBBY)

    def _onEntityCheckoutEnqueued(self, cancelCallback):
        g_eventBus.addListener(BootcampEvent.QUEUE_DIALOG_CANCEL,
                               self._onEntityCheckoutCanceled,
                               EVENT_BUS_SCOPE.LOBBY)
        g_eventBus.handleEvent(BootcampEvent(BootcampEvent.QUEUE_DIALOG_SHOW),
                               EVENT_BUS_SCOPE.LOBBY)
        self._entityEnqueueCancelCallback = cancelCallback

    def _onEntityCheckoutCanceled(self, _):
        g_eventBus.removeListener(BootcampEvent.QUEUE_DIALOG_CANCEL,
                                  self._onEntityCheckoutCanceled,
                                  EVENT_BUS_SCOPE.LOBBY)
        if self._entityEnqueueCancelCallback:
            self._entityEnqueueCancelCallback()
        self._entityEnqueueCancelCallback = None
        return

    def _onAccountBecomeNonPlayer(self):
        self._entityEnqueueCancelCallback = None
        g_eventBus.removeListener(BootcampEvent.QUEUE_DIALOG_CANCEL,
                                  self._onEntityCheckoutCanceled,
                                  EVENT_BUS_SCOPE.LOBBY)
        return

    def moveSpace(self, dx, dy, dz):
        dx, dy, dz = int(dx), int(dy), int(dz)
        if g_hangarSpace.space:
            g_hangarSpace.space.handleMouseEvent(dx, dy, dz)
        self.fireEvent(
            events.LobbySimpleEvent(events.LobbySimpleEvent.NOTIFY_SPACE_MOVED,
                                    ctx={
                                        'dx': dx,
                                        'dy': dy,
                                        'dz': dz
                                    }))

    def notifyCursorOver3dScene(self, isOver3dScene):
        self.fireEvent(
            events.LobbySimpleEvent(
                events.LobbySimpleEvent.NOTIFY_CURSOR_OVER_3DSCENE,
                ctx={'isOver3dScene': isOver3dScene}))

    def notifyCursorDragging(self, isDragging):
        self.fireEvent(
            events.LobbySimpleEvent(
                events.LobbySimpleEvent.NOTIFY_CURSOR_DRAGGING,
                ctx={'isDragging': isDragging}))

    def __onIgrTypeChanged(self, roomType, xpFactor):
        icon = gui.makeHtmlString('html_templates:igr/iconSmall', 'premium')
        if roomType == constants.IGR_TYPE.PREMIUM:
            SystemMessages.pushMessage(i18n.makeString(
                SYSTEM_MESSAGES.IGR_CUSTOMIZATION_BEGIN, igrIcon=icon),
                                       type=SystemMessages.SM_TYPE.Information)
        elif roomType in [
                constants.IGR_TYPE.BASE, constants.IGR_TYPE.NONE
        ] and self.__currIgrType == constants.IGR_TYPE.PREMIUM:
            SystemMessages.pushMessage(i18n.makeString(
                SYSTEM_MESSAGES.IGR_CUSTOMIZATION_END, igrIcon=icon),
                                       type=SystemMessages.SM_TYPE.Information)
        self.__currIgrType = roomType
Beispiel #8
0
class CamoSelectorMainView(CustomizationMainViewMeta):
    _COMMON_SOUND_SPACE = C11N_SOUND_SPACE
    lobbyContext = dependency.descriptor(ILobbyContext)
    itemsCache = dependency.descriptor(IItemsCache)
    service = dependency.descriptor(ICustomizationService)
    settingsCore = dependency.descriptor(ISettingsCore)

    def __init__(self, _=None):
        super(CamoSelectorMainView, self).__init__()
        self.__viewLifecycleWatcher = ViewLifecycleWatcher()
        self.fadeAnchorsOut = False
        self._currentSeason = SeasonType.SUMMER
        self._tabIndex = C11nTabs.CAMO_SHOP
        self._lastTab = C11nTabs.CAMO_SHOP
        self._originalOutfits = {}
        self._modifiedOutfits = {}
        self._currentOutfit = None
        self._setupOutfit = None
        self._mode = C11nMode.INSTALL
        self._currentSettings = {'custom': {}, 'remap': {}}
        self._randMode = RandMode.RANDOM
        self._ally = True
        self._enemy = True
        self._settingSeason = SeasonType.UNDEFINED
        self._isDeferredRenderer = True
        self.__anchorPositionCallbackID = None
        self._state = {}
        self._needFullRebuild = False
        self.__locatedOnEmbelem = False
        self.itemIsPicked = False
        self._vehicleCustomizationAnchorsUpdater = None

    def showBuyWindow(self):
        self.__releaseItemSound()
        self.soundManager.playInstantSound(SOUNDS.SELECT)
        purchaseItems = self.getPurchaseItems()
        self.buyAndExit(purchaseItems)

    def onSelectItem(self, index):
        self._carouselDP.selectItemIdx(index)
        self.soundManager.playInstantSound(SOUNDS.SELECT)

    def onPickItem(self):
        if self._mode == C11nMode.SETUP:
            self.__onRegionHighlighted(GUI_ITEM_TYPE.CAMOUFLAGE, 1, 0, True, False)
        elif not self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.PICK)
            self.itemIsPicked = True

    def onReleaseItem(self):
        self.__releaseItemSound()

    def changeSeason(self, seasonIdx):
        self._currentSeason = SEASON_IDX_TO_TYPE[seasonIdx]
        seasonName = SEASON_TYPE_TO_NAME.get(self._currentSeason)
        self.soundManager.playInstantSound(SOUNDS.SEASON_SELECT.format(seasonName))
        self.refreshOutfit()
        self.refreshCarousel(rebuild=True)
        self.as_refreshAnchorPropertySheetS()
        self.__setAnchorsInitData(self._tabIndex, self._tabIndex in C11nTabs.REGIONS, True)

    def changeCamoTeamMode(self, idx):
        self._ally = bool(idx & TeamMode.ALLY)
        self._enemy = bool(idx & TeamMode.ENEMY)
        self._updateCurrentSettings()
        self.__setBuyingPanelData()

    def changeCamoRandMode(self, idx):
        self._randMode = idx
        self._updateCurrentSettings()
        self.__setBuyingPanelData()

    def _updateCurrentSettings(self):
        item = self._currentOutfit.getContainer(1).slotFor(GUI_ITEM_TYPE.CAMOUFLAGE).getItem(0)
        itemName, itemKey = (item.descriptor.userKey, 'custom') if item.priceGroup == 'custom' else (item.id, 'remap')
        settings = self._currentSettings[itemKey].setdefault(itemName, {})
        settings['useForAlly'] = self._ally
        settings['useForEnemy'] = self._enemy
        settings['random_mode'] = self._randMode

    def _cleanSettings(self, allSettings, checkSeasons=True):
        camouflages = items.vehicles.g_cache.customization20().camouflages
        for itemsKey in allSettings:
            itemSettings = allSettings[itemsKey]
            for camoID in itemSettings.keys():
                origSetting = g_config.camouflages[itemsKey].get(camoID, {})
                camoSetting = itemSettings[camoID]
                if 'season' in camoSetting:
                    if checkSeasons and itemsKey == 'remap' and not self.itemsCache.items.getItemByCD(
                            camouflages[camoID].compactDescr).isHidden:
                        print g_config.ID + ': in-shop camouflage season changing is disabled (id:',\
                            camoID + ', season setting was', (camoSetting['season'] or 'empty') + ')'
                        del camoSetting['season']
                        if 'season' in origSetting:
                            del origSetting['season']
                    elif 'season' not in origSetting:
                        itemSeasons = SeasonType.UNDEFINED
                        for season in SEASONS_CONSTANTS.SEASONS:
                            if season in camoSetting['season']:
                                itemSeasons |= getattr(SeasonType, season.upper())
                        camoSeason = camouflages[camoID].season
                        if itemSeasons == (camoSeason if camoSeason < 8 else camoSeason - 8):
                            del camoSetting['season']
                    elif origSetting['season'] == camoSetting['season']:
                        del camoSetting['season']
                for team in ('useForAlly', 'useForEnemy'):
                    if team in camoSetting:
                        if origSetting.get(team, True) == camoSetting[team]:
                            del camoSetting[team]
                if 'random_mode' in camoSetting:
                    if camoSetting['random_mode'] == origSetting.get('random_mode', RandMode.RANDOM):
                        del camoSetting['random_mode']
                if not camoSetting:
                    del itemSettings[camoID]
        return allSettings

    def refreshCarousel(self, rebuild=False):
        if rebuild:
            self._carouselDP.buildList(self._tabIndex, self._currentSeason, refresh=False)
            self.as_setCarouselDataS(self._buildCustomizationCarouselDataVO())
        self._carouselDP.refresh()

    def refreshHotFilters(self):
        self.as_setCarouselFiltersDataS(
            {'hotFilters': [self._carouselDP.getOwnedFilter(), self._carouselDP.getAppliedFilter()]})

    def refreshOutfit(self):
        if self._mode == C11nMode.INSTALL:
            self._currentOutfit = self._modifiedOutfits[self._currentSeason]
        else:
            self._currentOutfit = self._setupOutfit
        self.service.tryOnOutfit(self._currentOutfit)
        g_tankActiveCamouflage[g_currentVehicle.item.intCD] = self._currentSeason

    def showGroupFromTab(self, tabIndex):
        self.soundManager.playInstantSound(SOUNDS.TAB_SWITCH)
        self._tabIndex = tabIndex
        doRegions = self._tabIndex in C11nTabs.REGIONS
        self.service.stopHighlighter()
        if self._mode == C11nMode.INSTALL and doRegions:
            self.service.startHighlighter(chooseMode(tabToItem(self._tabIndex), g_currentVehicle.item))
        self.__setAnchorsInitData(self._tabIndex, doRegions)
        if self.__locatedOnEmbelem and g_hangarSpace.spaceInited:
            space = g_hangarSpace.space
            space.clearSelectedEmblemInfo()
            space.locateCameraToPreview()
        self.__updateAnchorPositions()
        if self._tabIndex == C11nTabs.EFFECT:
            slotIdVO = CustomizationSlotIdVO(Area.MISC, GUI_ITEM_TYPE.MODIFICATION, 0)._asdict()
        else:
            slotIdVO = None
        self.as_updateSelectedRegionsS(slotIdVO)
        self.refreshCarousel(rebuild=True)
        if self._mode == C11nMode.SETUP:
            self.__onRegionHighlighted(GUI_ITEM_TYPE.CAMOUFLAGE, 1, 0, True, False)

    def installCustomizationElement(self, intCD, areaId, slotId, regionId, seasonIdx):
        if self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.APPLY)
        item = self.itemsCache.items.getItemByCD(intCD)
        if self._mode == C11nMode.INSTALL:
            season = SEASON_IDX_TO_TYPE.get(seasonIdx, self._currentSeason)
            outfit = self._modifiedOutfits[season]
            outfit.getContainer(areaId).slotFor(slotId).set(item, idx=regionId)
        else:
            outfit = self._setupOutfit
            for areaId in xrange(1, 4):
                outfit.getContainer(areaId).slotFor(slotId).set(item, idx=regionId)
            itemName, itemKey = (item.descriptor.userKey, 'custom') if item.priceGroup == 'custom' else (item.id, 'remap')
            itemSettings = self._currentSettings[itemKey].setdefault(itemName, {})
            itemOrigSettings = g_config.camouflages[itemKey].get(itemName, {})
            itemSeasonsStr = itemSettings.get('season', itemOrigSettings.get('season', None))
            if itemSeasonsStr is not None:
                itemSeasons = SeasonType.UNDEFINED
                for season in SEASONS_CONSTANTS.SEASONS:
                    if season in itemSeasonsStr:
                        itemSeasons |= getattr(SeasonType, season.upper())
            else:
                itemSeasons = item.season
                itemSeasonsStr = itemSettings['season'] = ','.join(
                    x for x in SEASONS_CONSTANTS.SEASONS if getattr(SeasonType, x.upper()) & itemSeasons)
            if seasonIdx not in SEASON_IDX_TO_TYPE:  # item is selected from carousel, not changed from property sheet
                self._settingSeason = itemSeasons
                self._randMode = itemSettings.get('random_mode', itemOrigSettings.get('random_mode', RandMode.RANDOM))
                self._ally = itemSettings.get('useForAlly', itemOrigSettings.get('useForAlly', True))
                self._enemy = itemSettings.get('useForEnemy', itemOrigSettings.get('useForEnemy', True))
            else:
                self._settingSeason |= SEASON_IDX_TO_TYPE[seasonIdx]
                newSeasons = set(x for x in SEASONS_CONSTANTS.SEASONS if x in itemSeasonsStr)
                newSeasons.add(SEASON_TYPE_TO_NAME[SEASON_IDX_TO_TYPE[seasonIdx]])
                itemSettings['season'] = ','.join(x for x in SEASONS_CONSTANTS.SEASONS if x in newSeasons)
        outfit.invalidate()
        self.refreshOutfit()
        self.__setBuyingPanelData()
        self.__setHeaderInitData()
        self.refreshCarousel(rebuild=self._carouselDP.getAppliedFilter() or self._carouselDP.getOwnedFilter())

    def clearCustomizationItem(self, areaId, slotId, regionId, seasonIdx):
        self.soundManager.playInstantSound(SOUNDS.REMOVE)
        if self._mode == C11nMode.INSTALL:
            season = SEASON_IDX_TO_TYPE.get(seasonIdx, self._currentSeason)
            outfit = self._modifiedOutfits[season]
            outfit.getContainer(areaId).slotFor(slotId).remove(idx=regionId)
        else:
            item = self._setupOutfit.getContainer(areaId).slotFor(slotId).getItem(regionId)
            itemName, itemKey = (item.descriptor.userKey, 'custom') if item.priceGroup == 'custom' else (item.id, 'remap')
            itemSettings = self._currentSettings[itemKey].setdefault(itemName, {})
            itemOrigSettings = g_config.camouflages[itemKey].get(itemName, {})
            itemSeasonsStr = itemSettings.get('season', itemOrigSettings.get('season', None))
            if seasonIdx not in SEASON_IDX_TO_TYPE:
                print 'WARNING', seasonIdx
            else:
                newSeasons = set()
                if itemSeasonsStr is not None:
                    [newSeasons.add(x) for x in SEASONS_CONSTANTS.SEASONS if x in itemSeasonsStr]
                newSeasons.discard(SEASON_TYPE_TO_NAME[SEASON_IDX_TO_TYPE[seasonIdx]])
                itemSettings['season'] = ','.join(x for x in SEASONS_CONSTANTS.SEASONS if x in newSeasons)
                self._settingSeason = SeasonType.UNDEFINED
                for season in newSeasons:
                    self._settingSeason |= getattr(SeasonType, season.upper())
        self.refreshOutfit()
        self.__setAnchorsInitData(self._tabIndex, self._tabIndex in C11nTabs.REGIONS, True)
        self.__setBuyingPanelData()
        self.__setHeaderInitData()
        self.refreshCarousel(rebuild=self._carouselDP.getAppliedFilter() or self._carouselDP.getOwnedFilter())

    def switchToCustom(self, updateUI=True):
        self.service.startHighlighter(chooseMode(tabToItem(self._tabIndex), g_currentVehicle.item))
        self.switchMode(C11nMode.INSTALL)

    def switchToStyle(self):
        if self._tabIndex not in C11nTabs.CAMO:
            self._tabIndex = C11nTabs.CAMO_SHOP
        self.switchMode(C11nMode.SETUP)
        self.__onRegionHighlighted(GUI_ITEM_TYPE.CAMOUFLAGE, 1, 0, True, False)
        self.service.stopHighlighter()

    def switchMode(self, mode):
        self.soundManager.playInstantSound(SOUNDS.TAB_SWITCH)
        self._mode = mode
        self.refreshOutfit()
        self.__setFooterInitData()
        self._carouselDP.selectItem()
        self.__setBuyingPanelData()
        self.__setHeaderInitData()

    def fadeOutAnchors(self, isFadeOut):
        self.fadeAnchorsOut = isFadeOut

    def closeWindow(self):
        purchaseItems = self.getPurchaseItems()
        cart = getTotalPurchaseInfo(purchaseItems)
        if cart.numTotal or any(self._cleanSettings(self._currentSettings).itervalues()):
            DialogsInterface.showDialog(I18nConfirmDialogMeta('customization/close'), self.__onConfirmCloseWindow)
        else:
            self.__onConfirmCloseWindow(proceed=True)

    def itemContextMenuDisplayed(self):
        cmHandler = self.app.contextMenuManager.getCurrentHandler()
        if isinstance(cmHandler, CustomizationItemCMHandler):
            cmHandler.onSelected += self._itemCtxMenuSelected

    def resetFilter(self):
        self.clearFilter()
        self.refreshFilterData()
        self.refreshHotFilters()
        self.refreshCarousel(rebuild=True)

    def clearFilter(self):
        self._carouselDP.clearFilter()

    def refreshFilterData(self):
        self.as_setFilterDataS(self._carouselDP.getFilterData())

    def getHistoricalPopoverData(self):
        self.soundManager.playInstantSound(SOUNDS.SELECT)
        nonHistoricItems = []
        for outfit in self._modifiedOutfits.itervalues():
            nonHistoricItems.extend((item for item in outfit.items() if not item.isHistorical()))

        return {'items': [item.intCD for item in sorted(nonHistoricItems, key=comparisonKey)]}

    def removeItems(self, *intCDs):
        self.soundManager.playInstantSound(SOUNDS.REMOVE)
        for outfit in self._modifiedOutfits.itervalues():
            for slot in outfit.slots():
                for idx in range(slot.capacity()):
                    item = slot.getItem(idx)
                    if item and item.intCD in intCDs:
                        slot.remove(idx)

        self.refreshOutfit()
        self.__setHeaderInitData()
        self.__setBuyingPanelData()
        self.as_refreshAnchorPropertySheetS()
        self.__setAnchorsInitData(self._tabIndex, self._tabIndex in C11nTabs.REGIONS, True)
        self.refreshCarousel(rebuild=self._carouselDP.getAppliedFilter() or self._carouselDP.getOwnedFilter())

    def updatePropertySheetButtons(self, areaId, slotId, regionId):
        self.service.onPropertySheetShow(areaId, slotId, regionId)

    def onLobbyClick(self):
        if self._tabIndex in (C11nTabs.EMBLEM, C11nTabs.INSCRIPTION):
            self.as_hideAnchorPropertySheetS()
            if self.__locatedOnEmbelem and g_hangarSpace.spaceInited:
                space = g_hangarSpace.space
                space.clearSelectedEmblemInfo()
                space.locateCameraToPreview()
                self.__updateAnchorPositions()
                self.__locatedOnEmbelem = False

    def setEnableMultiselectRegions(self, isEnabled):
        pass

    def onChangeSize(self):
        self.__updateAnchorPositions()

    def onSelectAnchor(self, areaID, regionID):
        if g_hangarSpace.spaceInited:
            self.soundManager.playInstantSound(SOUNDS.CHOOSE)
            if self._tabIndex == C11nTabs.EMBLEM:
                emblemType = 'player'
                zoom = 0.06
            else:
                emblemType = 'inscription'
                zoom = 0.1
            self.__updateAnchorPositions()
            self.__locatedOnEmbelem = g_hangarSpace.space.locateCameraOnEmblem(areaID < 2, emblemType, regionID, zoom)
            self.as_cameraAutoRotateChangedS(True)
            BigWorld.callback(5, self.__cameraRotationFinished)

    def __cameraRotationFinished(self):
        self.as_cameraAutoRotateChangedS(False)

    def getOutfitsInfo(self):
        outfitsInfo = {}
        for season in SEASONS_ORDER:
            outfitsInfo[season] = OutfitInfo(self._originalOutfits[season], self._modifiedOutfits[season])

        return outfitsInfo

    def getPurchaseItems(self):
        return getCustomPurchaseItems(self.getOutfitsInfo())

    def getItemInventoryCount(self, item):
        return getItemInventoryCount(item, self.getOutfitsInfo())

    def getCurrentOutfit(self):
        return self._currentOutfit

    def getModifiedOutfit(self, season):
        return self._modifiedOutfits.get(season)

    def getMode(self):
        return self._mode

    def getCurrentSeason(self):
        return self._currentSeason

    def getCurrentTab(self):
        return self._tabIndex

    def getRandMode(self):
        return self._randMode

    def getIsAlly(self):
        return self._ally

    def getIsEnemy(self):
        return self._enemy

    def getTeamMode(self):
        return 0 | (self._ally and TeamMode.ALLY) | (self._enemy and TeamMode.ENEMY)

    def getSettingSeason(self):
        return self._settingSeason

    def getAppliedItems(self, isOriginal=True):
        outfits = self._originalOutfits if isOriginal else self._modifiedOutfits
        seasons = SeasonType.COMMON_SEASONS if isOriginal else (self._currentSeason,)
        appliedItems = set()
        for seasonType in seasons:
            outfit = outfits[seasonType]
            appliedItems.update((i.intCD for i in outfit.items()))
        return appliedItems

    def isItemInOutfit(self, item):
        return any((outfit.has(item) for outfit in self._originalOutfits.itervalues())) or any(
            (outfit.has(item) for outfit in self._modifiedOutfits.itervalues()))

    def buyAndExit(self, purchaseItems):
        self._currentSettings = self._cleanSettings(self._currentSettings)
        for itemsKey in self._currentSettings:
            for camoName in self._currentSettings[itemsKey]:
                g_config.camouflages[itemsKey].setdefault(camoName, {}).update(self._currentSettings[itemsKey][camoName])
        if self._currentSettings['remap']:
            newSettings = {'disable': g_config.disable,
                           'remap': g_config.camouflages['remap']}
            loadJson(g_config.ID, 'settings', newSettings, g_config.configPath, True)
        if self._currentSettings['custom']:
            for confFolderName in g_config.configFolders:
                configFolder = g_config.configFolders[confFolderName]
                loadJson(g_config.ID, 'settings', {key: g_config.camouflages['custom'][key] for key in configFolder},
                         g_config.configPath + 'camouflages/' + confFolderName + '/', True, False)
        if any(self._currentSettings.itervalues()):
            from ..processors import collectCamouflageData
            collectCamouflageData()
        self.itemsCache.onSyncCompleted -= self.__onCacheResync
        boughtOutfits = {season: self.service.getCustomOutfit(season) for season in SeasonType.COMMON_SEASONS}
        cart = getTotalPurchaseInfo(purchaseItems)
        nationName, vehicleName = g_currentVehicle.item.descriptor.name.split(':')
        vehConfig = g_config.outfitCache.get(nationName, {}).get(vehicleName, {})
        for pItem in (x for x in purchaseItems if x.selected):
            seasonName = SEASON_TYPE_TO_NAME[pItem.group]
            if pItem.slot == GUI_ITEM_TYPE.CAMOUFLAGE:
                bItem, bComp = boughtOutfits[pItem.group].getContainer(pItem.areaID).slotFor(pItem.slot)._items.get(
                    pItem.regionID, (None, None))
                component = self._modifiedOutfits[pItem.group].getContainer(pItem.areaID).slotFor(pItem.slot).getComponent(
                    pItem.regionID)
                if pItem.isDismantling and (not bItem or not bComp) or not pItem.isDismantling and pItem.item == bItem and \
                        component.palette == bComp.palette and component.patternSize == bComp.patternSize:
                    vehConfig.get(seasonName, {}).get('camo', {}).pop(TankPartIndexes.getName(pItem.areaID), [])
                else:
                    g_config.outfitCache.setdefault(nationName, {}).setdefault(vehicleName, {}).setdefault(
                        seasonName, {}).setdefault('camo', {})[TankPartIndexes.getName(pItem.areaID)] = (
                        [pItem.item.id, component.palette, component.patternSize] if not pItem.isDismantling else [])
                g_config.hangarCamoCache.get(nationName, {}).get(vehicleName, {}).get(seasonName, {}).pop(
                    TankPartIndexes.getName(pItem.areaID), {})
            else:
                typeName = GUI_ITEM_TYPE_NAMES[pItem.slot]
                bItem = boughtOutfits[pItem.group].getContainer(pItem.areaID).slotFor(pItem.slot).getItem(pItem.regionID)
                if pItem.isDismantling and not bItem or not pItem.isDismantling and pItem.item == bItem:
                    vehConfig.get(seasonName, {}).get(typeName, {}).get(
                        TankPartIndexes.getName(pItem.areaID) if pItem.areaID < 4 else 'misc',
                        {}).pop(str(pItem.regionID), None)
                else:
                    g_config.outfitCache.setdefault(nationName, {}).setdefault(vehicleName, {}).setdefault(
                        seasonName, {}).setdefault(typeName, {}).setdefault(
                        TankPartIndexes.getName(pItem.areaID) if pItem.areaID < 4 else 'misc', {})[
                        str(pItem.regionID)] = (pItem.item.id if not pItem.isDismantling else None)
        for nationName in g_config.outfitCache.keys():
            for vehicleName in g_config.outfitCache[nationName].keys():
                for season in g_config.outfitCache[nationName][vehicleName].keys():
                    for itemType in g_config.outfitCache[nationName][vehicleName][season].keys():
                        if itemType == 'camo':
                            if g_currentVehicle.item.turret.isGunCarriage:
                                g_config.outfitCache[nationName][vehicleName][season][itemType].pop('turret', None)
                        else:
                            for areaName in g_config.outfitCache[nationName][vehicleName][season][itemType].keys():
                                if not g_config.outfitCache[nationName][vehicleName][season][itemType][areaName]:
                                    del g_config.outfitCache[nationName][vehicleName][season][itemType][areaName]
                        if not g_config.outfitCache[nationName][vehicleName][season][itemType]:
                            del g_config.outfitCache[nationName][vehicleName][season][itemType]
                    if not g_config.outfitCache[nationName][vehicleName][season]:
                        del g_config.outfitCache[nationName][vehicleName][season]
                if not g_config.outfitCache[nationName][vehicleName]:
                    del g_config.outfitCache[nationName][vehicleName]
            if not g_config.outfitCache[nationName]:
                del g_config.outfitCache[nationName]
        loadJson(g_config.ID, 'outfitCache', g_config.outfitCache, g_config.configPath, True)
        if cart.totalPrice != ITEM_PRICE_EMPTY:
            msgCtx = {'money': formatPrice(cart.totalPrice.price),
                      'count': cart.numSelected}
            SystemMessages.pushMessage(g_config.i18n['UI_flashCol_applied_money'] % msgCtx,
                                       type=CURRENCY_TO_SM_TYPE.get(cart.totalPrice.getCurrency(byWeight=True),
                                                                    SM_TYPE.PurchaseForGold))
        else:
            SystemMessages.pushI18nMessage(MESSENGER.SERVICECHANNELMESSAGES_SYSMSG_CONVERTER_CUSTOMIZATIONS,
                                           type=SM_TYPE.Information)
        self.__onCacheResync()
        self.itemsCache.onSyncCompleted += self.__onCacheResync

    # noinspection PyUnusedLocal
    @process('sellItem')
    def sellItem(self, intCD, shouldSell):
        assert False

    def onAnchorsShown(self, anchors):
        if self._vehicleCustomizationAnchorsUpdater is not None:
            self._vehicleCustomizationAnchorsUpdater.setAnchors(anchors, self._tabIndex in C11nTabs.REGIONS)

    def _populate(self):
        super(CamoSelectorMainView, self)._populate()
        self.soundManager.playInstantSound(SOUNDS.ENTER)
        self.__viewLifecycleWatcher.start(self.app.containerManager, [_C11nWindowsLifecycleHandler()])
        self._isDeferredRenderer = self.settingsCore.getSetting(GRAPHICS.RENDER_PIPELINE) == 0
        g_clientUpdateManager.addMoneyCallback(self.__setBuyingPanelData)
        self.lobbyContext.addHeaderNavigationConfirmator(self.__confirmHeaderNavigation)
        self.lobbyContext.getServerSettings().onServerSettingsChange += self.__onServerSettingChanged
        self.service.onCarouselFilter += self.__onCarouselFilter
        self.service.onRemoveItems += self.removeItems
        self.service.onOutfitChanged += self.__onOutfitChanged
        g_eventBus.addListener(CameraRelatedEvents.IDLE_CAMERA, self.__onNotifyHangarCameraIdleStateChanged)
        g_hangarSpace.onSpaceCreate += self.__onSpaceCreateHandler
        g_hangarSpace.onSpaceDestroy += self.__onSpaceDestroyHandler
        g_hangarSpace.onSpaceRefresh += self.__onSpaceRefreshHandler
        self.service.onRegionHighlighted += self.__onRegionHighlighted
        self.itemsCache.onSyncCompleted += self.__onCacheResync
        self.__carveUpOutfits()
        self._carouselDP = CustomizationCarouselDataProvider(g_currentVehicle, self._carouseItemWrapper, self)
        self._carouselDP.setFlashObject(self.as_getDataProviderS())
        self._carouselDP.setEnvironment(self.app)
        self.__setHeaderInitData()
        self.__setFooterInitData()
        self.__setBuyingPanelData()
        self.__setSeasonData()
        self._vehicleCustomizationAnchorsUpdater = _VehicleCustomizationAnchorsUpdater(self.service)
        self._vehicleCustomizationAnchorsUpdater.startUpdater(self.settingsCore.interfaceScale.get())
        self.refreshOutfit()
        self.settingsCore.interfaceScale.onScaleExactlyChanged += self.__onInterfaceScaleChanged
        self.settingsCore.onSettingsChanged += self.__onSettingsChanged
        self.__updateCameraParalaxFlag()
        self.service.startHighlighter(chooseMode(tabToItem(self._tabIndex), g_currentVehicle.item))

    def _dispose(self):
        if g_appLoader.getSpaceID() != _SPACE_ID.LOGIN:
            self.__releaseItemSound()
            self.soundManager.playInstantSound(SOUNDS.EXIT)
        self.settingsCore.onSettingsChanged -= self.__onSettingsChanged
        self.settingsCore.interfaceScale.onScaleExactlyChanged -= self.__onInterfaceScaleChanged
        self._vehicleCustomizationAnchorsUpdater.stopUpdater()
        self._vehicleCustomizationAnchorsUpdater = None
        if self.__locatedOnEmbelem and g_hangarSpace.spaceInited:
            space = g_hangarSpace.space
            space.clearSelectedEmblemInfo()
            space.locateCameraToPreview()
        self.__viewLifecycleWatcher.stop()
        self.service.stopHighlighter()
        g_clientUpdateManager.removeObjectCallbacks(self)
        self.lobbyContext.deleteHeaderNavigationConfirmator(self.__confirmHeaderNavigation)
        self.lobbyContext.getServerSettings().onServerSettingsChange -= self.__onServerSettingChanged
        self.service.onCarouselFilter -= self.__onCarouselFilter
        self.service.onRemoveItems -= self.removeItems
        self.service.onOutfitChanged -= self.__onOutfitChanged
        g_eventBus.removeListener(CameraRelatedEvents.IDLE_CAMERA, self.__onNotifyHangarCameraIdleStateChanged)
        g_hangarSpace.onSpaceCreate -= self.__onSpaceCreateHandler
        g_hangarSpace.onSpaceDestroy -= self.__onSpaceDestroyHandler
        g_hangarSpace.onSpaceRefresh -= self.__onSpaceRefreshHandler
        self.service.onRegionHighlighted -= self.__onRegionHighlighted
        self.itemsCache.onSyncCompleted -= self.__onCacheResync
        if g_currentVehicle.item:
            g_tankActiveCamouflage[g_currentVehicle.item.intCD] = self._currentSeason
            g_currentVehicle.refreshModel()
        super(CamoSelectorMainView, self)._dispose()

    def _itemCtxMenuSelected(self, ctxMenuID, itemIntCD):
        item = self.itemsCache.items.getItemByCD(itemIntCD)
        if ctxMenuID == CustomizationOptions.REMOVE_FROM_TANK:
            self.removeItems(item.intCD)

    def _getUpdatedAnchorPositions(self):
        anchorVOs = []
        anchorPosData = []
        cType = tabToItem(self._tabIndex)
        outfit = self.service.getEmptyOutfit()
        for container in outfit.containers():
            for slot in (x for x in container.slots() if x.getType() == cType):
                for regionId, region in enumerate(slot.getRegions()):
                    slotId = CustomizationSlotIdVO(container.getAreaID(), slot.getType(), regionId)
                    anchorData = self.__getAnchorPositionData(slotId, region)
                    if anchorData is not None:
                        anchorPosData.append(anchorData)
        for zIdx, posData in enumerate(anchorPosData):
            anchorVOs.append(CustomizationAnchorPositionVO(zIdx, posData.slotId._asdict())._asdict())
        return CustomizationAnchorsSetVO(anchorVOs)._asdict()

    def _buildCustomizationCarouselDataVO(self):
        isZeroCount = self._carouselDP.itemCount == 0
        countStyle = text_styles.error if isZeroCount else text_styles.main
        displayString = text_styles.main(
            '{} / {}'.format(countStyle(str(self._carouselDP.itemCount)), str(self._carouselDP.totalItemCount)))
        shouldShow = self._carouselDP.itemCount < self._carouselDP.totalItemCount
        return CustomizationCarouselDataVO(displayString, isZeroCount, shouldShow,
                                           itemLayoutSize=self._carouselDP.getItemSizeData(),
                                           bookmarks=self._carouselDP.getBookmarkData())._asdict()

    def _carouseItemWrapper(self, itemCD):
        item = self.itemsCache.items.getItemByCD(itemCD)
        itemInventoryCount = self.getItemInventoryCount(item)
        if item.itemTypeID == GUI_ITEM_TYPE.MODIFICATION:
            showUnsupportedAlert = not self._isDeferredRenderer
        else:
            showUnsupportedAlert = False
        isCurrentlyApplied = itemCD in self._carouselDP.getCurrentlyApplied()
        return buildCustomizationItemDataVO(item, itemInventoryCount, showUnsupportedAlert=showUnsupportedAlert,
                                            isCurrentlyApplied=isCurrentlyApplied, plainView=True)

    def __carveUpOutfits(self):
        from ..processors import applyCamoCache, applyPlayerCache
        self._setupOutfit = self.service.getEmptyOutfit()
        descriptor = g_currentVehicle.item.descriptor
        nationName, vehName = descriptor.name.split(':')
        for season in SeasonType.COMMON_SEASONS:
            outfit = self.service.getCustomOutfit(season).copy()
            seasonName = SEASON_TYPE_TO_NAME[season]
            seasonCache = g_config.outfitCache.get(nationName, {}).get(vehName, {}).get(seasonName, {})
            applyCamoCache(outfit, vehName, seasonCache.get('camo', {}))
            applyPlayerCache(outfit, vehName, seasonCache)
            self._originalOutfits[season] = outfit.copy()
            applyCamoCache(outfit, vehName, g_config.hangarCamoCache.get(nationName, {}).get(vehName, {}).get(seasonName, {}))
            self._modifiedOutfits[season] = outfit.copy()
        if self._mode == C11nMode.INSTALL:
            self._currentOutfit = self._modifiedOutfits[self._currentSeason]
        else:
            self._currentOutfit = self._setupOutfit

    def __updateAnchorPositions(self, _=None):
        self.as_setAnchorPositionsS(self._getUpdatedAnchorPositions())

    def __onRegionHighlighted(self, typeID, tankPartID, regionID, selected, hovered):
        slotId = None
        if hovered:
            self.soundManager.playInstantSound(SOUNDS.HOVER)
            return
        if self._tabIndex == C11nTabs.EFFECT:
            tankPartID = Area.MISC
            typeID = GUI_ITEM_TYPE.MODIFICATION
        if tankPartID != -1 and regionID != -1:
            slotId = CustomizationSlotIdVO(tankPartID if self._mode == C11nMode.INSTALL else 1, typeID,
                                           regionID)._asdict()
            if selected:
                self.soundManager.playInstantSound(SOUNDS.CHOOSE)
        if self._mode == C11nMode.INSTALL or slotId is not None:
            self.as_onRegionHighlightedS(slotId)

    def __onSpaceCreateHandler(self):
        Waiting.hide('loadHangarSpace')
        self.refreshOutfit()
        self.__updateAnchorPositions()

    def __onSpaceDestroyHandler(self, _):
        Waiting.hide('loadHangarSpace')
        self.__onConfirmCloseWindow(proceed=True)

    def __onSpaceRefreshHandler(self):
        Waiting.show('loadHangarSpace')

    def __onConfirmCloseWindow(self, proceed):
        if proceed:
            self.fireEvent(events.LoadViewEvent(VIEW_ALIAS.LOBBY_HANGAR), scope=EVENT_BUS_SCOPE.LOBBY)

    def __onCarouselFilter(self, **kwargs):
        if 'group' in kwargs:
            self._carouselDP.setActiveGroupIndex(kwargs['group'])
        if 'historic' in kwargs:
            self._carouselDP.setHistoricalFilter(kwargs['historic'])
        if 'inventory' in kwargs:
            self._carouselDP.setOwnedFilter(kwargs['inventory'])
        if 'applied' in kwargs:
            self._carouselDP.setAppliedFilter(kwargs['applied'])
        self.refreshCarousel(rebuild=True)
        self.refreshHotFilters()

    def __onOutfitChanged(self):
        self.refreshOutfit()
        self.__setBuyingPanelData()

    def __onCacheResync(self, *_):
        if not g_currentVehicle.isPresent():
            self.__onConfirmCloseWindow(proceed=True)
            return
        self.__preserveState()
        self.__carveUpOutfits()
        self.__restoreState()
        self.__setHeaderInitData()
        self.__setBuyingPanelData()
        self.refreshCarousel(rebuild=self._needFullRebuild)
        self.refreshOutfit()
        self.as_refreshAnchorPropertySheetS()
        self._needFullRebuild = False

    def __preserveState(self):
        self._state.update(modifiedOutfits={season: outfit.copy() for season, outfit in self._modifiedOutfits.iteritems()})

    def __restoreState(self):
        self._modifiedOutfits = self._state.get('modifiedOutfits')
        self._state.clear()

    def __onServerSettingChanged(self, diff):
        if 'isCustomizationEnabled' in diff and not diff.get('isCustomizationEnabled', True):
            SystemMessages.pushI18nMessage(SYSTEM_MESSAGES.CUSTOMIZATION_UNAVAILABLE, type=SystemMessages.SM_TYPE.Warning)
            self.__onConfirmCloseWindow(proceed=True)

    def __setBuyingPanelData(self, *_):
        purchaseItems = self.getPurchaseItems()
        cartInfo = getTotalPurchaseInfo(purchaseItems)
        totalPriceVO = getItemPricesVO(cartInfo.totalPrice)
        cleanSettings = self._cleanSettings(self._currentSettings, checkSeasons=False)
        keys = []
        if cartInfo.numTotal:
            keys.append('install')
        if cartInfo.totalPrice != ITEM_PRICE_EMPTY:
            self.as_showBuyingPanelS()
        else:
            self.as_hideBuyingPanelS()
        if any(cleanSettings.itervalues()) or not keys:
            keys.append('apply')
        label = g_config.i18n['UI_flash_commit_' + '_and_'.join(keys)]
        isApplyEnabled = bool(cartInfo.numTotal) or any(cleanSettings.itervalues())
        shortage = self.itemsCache.items.stats.money.getShortage(cartInfo.totalPrice.price)
        self.as_setBottomPanelHeaderS({'buyBtnEnabled': isApplyEnabled,
                                       'buyBtnLabel': label,
                                       'enoughMoney': getItemPricesVO(ItemPrice(shortage, shortage))[0],
                                       'pricePanel': totalPriceVO[0]})

    def __setSeasonData(self):
        seasonRenderersList = []
        for season in SEASONS_ORDER:
            seasonName = SEASON_TYPE_TO_NAME.get(season)
            seasonRenderersList.append({'seasonName': VEHICLE_CUSTOMIZATION.getSeasonName(seasonName),
                                        'seasonIconSmall': RES_ICONS.getSeasonIcon(seasonName)})

        self.as_setSeasonPanelDataS({'seasonRenderersList': seasonRenderersList})

    # noinspection PyUnusedLocal
    def __setAnchorsInitData(self, tabIndex, doRegions, update=False):

        def customizationSlotIdToUid(customizationSlotIdVO):
            s = struct.pack('bbh', customizationSlotIdVO.areaId, customizationSlotIdVO.slotId, customizationSlotIdVO.regionId)
            return struct.unpack('I', s)[0]

        anchorVOs = []
        cType = tabToItem(self._tabIndex)
        for container in self._currentOutfit.containers():
            for slot in (x for x in container.slots() if x.getType() == cType):
                for regionId, region in enumerate(slot.getRegions()):
                    slotId = CustomizationSlotIdVO(container.getAreaID(), slot.getType(), regionId)
                    popoverAlias = CUSTOMIZATION_POPOVER_ALIASES[slot.getType()]
                    item = slot.getItem(regionId)
                    itemIntCD = item.intCD if item is not None else 0
                    uid = customizationSlotIdToUid(slotId)
                    if self.__getAnchorPositionData(slotId, region) is not None:
                        anchorVOs.append(CustomizationSlotUpdateVO(slotId._asdict(), popoverAlias, itemIntCD, uid)._asdict())

        if update:
            self.as_updateAnchorDataS(CustomizationAnchorInitVO(anchorVOs, doRegions)._asdict())
        else:
            self.as_setAnchorInitS(CustomizationAnchorInitVO(anchorVOs, doRegions)._asdict())

    def __setHeaderInitData(self):
        vehicle = g_currentVehicle.item
        self.as_setHeaderDataS({'tankTier': str(int2roman(vehicle.level)),
                                'tankName': vehicle.shortUserName,
                                'tankType': '{}_elite'.format(vehicle.type) if vehicle.isElite else vehicle.type,
                                'isElite': vehicle.isElite,
                                'closeBtnTooltip': VEHICLE_CUSTOMIZATION.CUSTOMIZATION_HEADERCLOSEBTN,
                                'historicVO': self.__getHistoricIndicatorData()})

    def __setFooterInitData(self):
        self.as_setBottomPanelInitDataS(
            {'tabData': {
                'tabData': self.__getItemTabsData(),
                'selectedTab': self._tabIndex},
                'tabsAvailableRegions': C11nTabs.AVAILABLE_REGIONS,
                'defaultStyleLabel': VEHICLE_CUSTOMIZATION.DEFAULTSTYLE_LABEL,
                'carouselInitData': self.__getCarouselInitData(),
                'switcherInitData': self.__getSwitcherInitData()})
        self.as_setCarouselFiltersInitDataS(
            {'popoverAlias': VIEW_ALIAS.CUSTOMIZATION_FILTER_POPOVER,
             'mainBtn': {'value': RES_ICONS.MAPS_ICONS_BUTTONS_FILTER,
                         'tooltip': VEHICLE_CUSTOMIZATION.CAROUSEL_FILTER_MAINBTN},
             'hotFilters': [{'value': RES_ICONS.MAPS_ICONS_CUSTOMIZATION_STORAGE_ICON,
                             'tooltip': VEHICLE_CUSTOMIZATION.CAROUSEL_FILTER_STORAGEBTN,
                             'selected': self._carouselDP.getOwnedFilter()},
                            {'value': RES_ICONS.MAPS_ICONS_BUTTONS_EQUIPPED_ICON,
                             'tooltip': VEHICLE_CUSTOMIZATION.CAROUSEL_FILTER_EQUIPPEDBTN,
                             'selected': self._carouselDP.getAppliedFilter()}]})

    def onSelectHotFilter(self, index, value):
        (self._carouselDP.setOwnedFilter, self._carouselDP.setAppliedFilter)[index](value)
        self.refreshCarousel(rebuild=True)

    def __getSwitcherInitData(self):
        return {'leftLabel': g_config.i18n['UI_flash_switcher_' + C11nMode.NAMES[self._mode]],
                'rightLabel': g_config.i18n['UI_flash_switcher_' + C11nMode.NAMES[not self._mode]],
                'leftEvent': 'installStyle' + ('s' if self._mode else ''),
                'rightEvent': 'installStyle' + ('s' if not self._mode else ''),
                'isLeft': True}

    def __getHistoricIndicatorData(self):
        isDefault = all((outfit.isEmpty() for outfit in self._modifiedOutfits.itervalues()))
        isHistorical = all((outfit.isHistorical() for outfit in self._modifiedOutfits.itervalues()))
        name = _ms(VEHICLE_CUSTOMIZATION.HISTORICINDICATOR_STYLENAME_CUSTSOMSTYLE) if not isDefault else ''
        txtStyle = text_styles.stats if isHistorical else text_styles.tutorial
        return {'historicText': txtStyle(toUpper(name)),
                'isDefaultAppearance': isDefault,
                'isHistoric': isHistorical,
                'tooltip': TOOLTIPS.CUSTOMIZATION_NONHISTORICINDICATOR if not isHistorical else ''}

    @staticmethod
    def __getCarouselInitData():
        return {'message': '{}{}\n{}'.format(icons.makeImageTag(RES_ICONS.MAPS_ICONS_LIBRARY_ATTENTIONICONFILLED, vSpace=-3),
                                             text_styles.neutral(VEHICLE_CUSTOMIZATION.CAROUSEL_MESSAGE_HEADER),
                                             text_styles.main(VEHICLE_CUSTOMIZATION.CAROUSEL_MESSAGE_DESCRIPTION))}

    def __getAnchorPositionData(self, slotId, region):
        if self._tabIndex in C11nTabs.REGIONS:
            anchorPos = self.service.getPointForRegionLeaderLine(region)
            anchorNorm = anchorPos
        else:
            anchorPos = self.service.getPointForAnchorLeaderLine(slotId.areaId, slotId.slotId, slotId.regionId)
            anchorNorm = self.service.getNormalForAnchorLeaderLine(slotId.areaId, slotId.slotId, slotId.regionId)
        return None if anchorPos is None or anchorNorm is None else AnchorPositionData(
            cameras.get2DAngleFromCamera(anchorNorm), cameras.projectPoint(anchorPos), slotId)

    def __getItemTabsData(self):
        data = []
        for tabIdx in self.__getVisibleTabs():
            data.append({'label': g_config.i18n['UI_flash_tabs_%s_label' % tabIdx],
                         'tooltip': makeTooltip(g_config.i18n['UI_flashCol_tabs_%s_text' % tabIdx],
                                                g_config.i18n['UI_flashCol_tabs_%s_tooltip' % tabIdx]),
                         'id': tabIdx})

        return data

    def __getVisibleTabs(self):
        visibleTabs = []
        anchorsData = g_currentVehicle.hangarSpace.getSlotPositions()
        for tabIdx in C11nTabs.VISIBLE:
            if self._mode == C11nMode.SETUP and tabIdx not in C11nTabs.CAMO:
                continue
            itemTypeID = tabToItem(tabIdx)
            data = self._carouselDP.getSeasonAndTabData(tabIdx, self._currentSeason)
            if not data.itemCount:
                continue
            if itemTypeID in (GUI_ITEM_TYPE.INSCRIPTION, GUI_ITEM_TYPE.EMBLEM):
                for areaData in anchorsData.itervalues():
                    if areaData.get(itemTypeID):
                        hasSlots = True
                        break
                else:
                    hasSlots = False

                if not hasSlots:
                    continue
            visibleTabs.append(tabIdx)

        return visibleTabs

    def __onNotifyHangarCameraIdleStateChanged(self, event):
        isIdle = event.ctx.get('started', False)
        self.as_cameraAutoRotateChangedS(isIdle)

    @async
    @adisp_process
    def __confirmHeaderNavigation(self, callback):
        purchaseItems = self.getPurchaseItems()
        cart = getTotalPurchaseInfo(purchaseItems)
        if cart.numTotal:
            result = yield DialogsInterface.showI18nConfirmDialog('customization/close')
        else:
            result = True
        callback(result)

    def __releaseItemSound(self):
        if self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.RELEASE)
            self.itemIsPicked = False

    def __onSettingsChanged(self, diff):
        if GAME.HANGAR_CAM_PARALLAX_ENABLED in diff:
            self.__updateCameraParalaxFlag()

    def __updateCameraParalaxFlag(self):
        paralaxEnabled = bool(self.settingsCore.getSetting(GAME.HANGAR_CAM_PARALLAX_ENABLED))
        self.as_setParallaxFlagS(paralaxEnabled)

    def __onInterfaceScaleChanged(self, scale):
        if self._vehicleCustomizationAnchorsUpdater is not None:
            self._vehicleCustomizationAnchorsUpdater.setInterfaceScale(scale)
class MainView(CustomizationMainViewMeta):
    _COMMON_SOUND_SPACE = C11N_SOUND_SPACE
    _ZOOM_ON_EMBLEM = 0.6
    _ZOOM_ON_INSCRIPTION = 0.1
    lobbyContext = dependency.descriptor(ILobbyContext)
    itemsCache = dependency.descriptor(IItemsCache)
    service = dependency.descriptor(ICustomizationService)
    hangarSpace = dependency.descriptor(IHangarSpace)

    def __init__(self, _=None):
        super(MainView, self).__init__()
        self.__viewLifecycleWatcher = ViewLifecycleWatcher()
        self.fadeAnchorsOut = False
        self.__locatedOnEmbelem = False
        self.itemIsPicked = False
        self.__propertiesSheet = None
        self._seasonSoundAnimantion = None
        self._isPropertySheetShown = False
        self.__ctx = None
        self.__renderEnv = None
        return

    def showBuyWindow(self):
        self.changeVisible(False)
        self.__releaseItemSound()
        self.soundManager.playInstantSound(SOUNDS.SELECT)
        purchaseItems = self.__ctx.getPurchaseItems()
        cart = getTotalPurchaseInfo(purchaseItems)
        if cart.totalPrice == ITEM_PRICE_EMPTY:
            self.__ctx.applyItems(purchaseItems)
        else:
            self.fireEvent(
                events.LoadViewEvent(VIEW_ALIAS.CUSTOMIZATION_PURCHASE_WINDOW),
                EVENT_BUS_SCOPE.LOBBY)

    def onPressClearBtn(self):
        self.__ctx.cancelChanges()

    def onPressEscBtn(self):
        tabIndex = self.__ctx.currentTab
        if tabIndex not in C11nTabs.REGIONS and self.__ctx.isAnyAnchorSelected(
        ) or self.service.isRegionSelected():
            self.__clearItem()
            if self._isPropertySheetShown:
                self.__hidePropertiesSheet()
            self.as_enableDNDS(True)
        else:
            self.onCloseWindow()

    def onPressSelectNextItem(self):
        self.__installNextCarouselItem(shift=1)

    def onPressSelectPrevItem(self):
        self.__installNextCarouselItem(shift=-1)

    def __installNextCarouselItem(self, shift=1):
        if not self.__ctx.isAnyAnchorSelected(
        ) or self.__ctx.isCaruselItemSelected():
            return
        else:
            c11nBottomPanel = self.components[
                VIEW_ALIAS.CUSTOMIZATION_BOTTOM_PANEL]
            tabIndex = self.__ctx.currentTab
            if tabIndex == C11nTabs.STYLE:
                item = self.__ctx.modifiedStyle
            else:
                item = self.__ctx.getItemFromSelectedRegion()
            if item is not None:
                carouselItems = c11nBottomPanel.carouselItems
                index = carouselItems.index(item.intCD) + shift
                if 0 <= index < len(carouselItems):
                    intCD = carouselItems[index]
                    self.__ctx.caruselItemSelected(index, intCD)
            return

    def changeVisible(self, value):
        _, slotType, _ = self.__ctx.selectedAnchor
        self.__ctx.anchorSelected(slotType, -1, -1)
        self.as_hideS(value)

    def onReleaseItem(self):
        self.__ctx.caruselItemUnselected()
        self.__releaseItemSound()

    def _onRegisterFlashComponent(self, viewPy, alias):
        if alias == VIEW_ALIAS.CUSTOMIZATION_PROPERTIES_SHEET:
            self.__propertiesSheet = viewPy

    def changeSeason(self, seasonIdx):
        if seasonIdx in SEASON_IDX_TO_TYPE:
            self.__ctx.changeSeason(SEASON_IDX_TO_TYPE[seasonIdx])
        else:
            _logger.error('Wrong season index %(seasonIdx)d',
                          {'seasonIdx': seasonIdx})
        if self.__ctx.isAnyAnchorSelected():
            self.service.selectRegions(ApplyArea.NONE)
            self.__ctx.anchorSelected(-1, -1, -1)
            tabIndex = self.__ctx.currentTab
            if tabIndex not in C11nTabs.REGIONS:
                self.as_updateSelectedRegionsS(-1)
                self.as_enableDNDS(True)
            self.__resetCameraFocus()
            if self._isPropertySheetShown:
                self.__hidePropertiesSheet()

    def __onSeasonChanged(self, seasonType):
        seasonName = SEASON_TYPE_TO_NAME.get(seasonType)
        self.soundManager.playInstantSound(
            SOUNDS.SEASON_SELECT.format(seasonName))
        self.__setAnchorsInitData(True)

    def __onTabChanged(self, tabIndex):
        self.soundManager.playInstantSound(SOUNDS.TAB_SWITCH)
        self.service.stopHighlighter()
        if tabIndex in C11nTabs.REGIONS:
            itemTypeID = TABS_ITEM_MAPPING[tabIndex]
            self.service.startHighlighter(
                chooseMode(itemTypeID, g_currentVehicle.item))
        self.__setAnchorsInitData()
        if self.__locatedOnEmbelem and self.__ctx.c11CameraManager is not None:
            self.__ctx.c11CameraManager.clearSelectedEmblemInfo()
            self.__ctx.c11CameraManager.locateCameraToCustomizationPreview()
        self.__updateAnchorsData()
        if tabIndex == C11nTabs.STYLE:
            slotIdVO = CustomizationSlotIdVO(Area.MISC, GUI_ITEM_TYPE.STYLE,
                                             0)._asdict()
        elif tabIndex == C11nTabs.EFFECT:
            slotIdVO = CustomizationSlotIdVO(Area.MISC,
                                             GUI_ITEM_TYPE.MODIFICATION,
                                             0)._asdict()
        else:
            slotIdVO = None
        self.as_updateSelectedRegionsS(slotIdVO)
        self.__updateDnd()
        self.__hidePropertiesSheet()
        return

    def __onItemsInstalled(self, item, slotId, limitReached):
        if self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.APPLY)
        else:
            areaId, slotType, regionIdx = self.__ctx.selectedAnchor
            self.__locateCameraOnAnchor(areaId, slotType, regionIdx)
            self.__showPropertiesSheet(areaId, slotType, regionIdx)
        self.__setHeaderInitData()
        self.__setSeasonData()
        self.__setAnchorsInitData(True)
        if limitReached:
            self.__clearItem()

    def __onItemsRemoved(self):
        self.soundManager.playInstantSound(SOUNDS.REMOVE)
        self.__setHeaderInitData()
        self.__setSeasonData()
        self.__setAnchorsInitData(True)
        self.__clearItem()

    def __onModeChanged(self, mode):
        self.__setHeaderInitData()
        self.__setSeasonData(True)

    def fadeOutAnchors(self, isFadeOut):
        self.fadeAnchorsOut = isFadeOut

    def onCloseWindow(self):
        if self.isDisposed():
            return
        if self._isPropertySheetShown:
            self.__clearItem()
        if self.__ctx.isOutfitsModified():
            DialogsInterface.showDialog(
                I18nConfirmDialogMeta('customization/close'),
                self.__onCloseWindow)
        else:
            self.__onCloseWindow(proceed=True)

    def itemContextMenuDisplayed(self):
        cmHandler = self.app.contextMenuManager.getCurrentHandler()
        if isinstance(cmHandler, CustomizationItemCMHandler):
            cmHandler.onSelected += self._itemCtxMenuSelected

    def onLobbyClick(self):
        if self.__ctx.currentTab in (C11nTabs.EMBLEM, C11nTabs.INSCRIPTION,
                                     C11nTabs.PROJECTION_DECAL):
            self.__hidePropertiesSheet()
            self.__clearItem()
        if not self.__ctx.isCaruselItemSelected():
            self.as_enableDNDS(True)

    def onChangeSize(self):
        self.__updateAnchorsData()

    def playCustomSound(self, sound):
        self.soundManager.playInstantSound(sound)

    def onSelectAnchor(self, areaId, slotType, regionIdx):
        if not self.__ctx.isCaruselItemSelected():
            self.as_enableDNDS(False)
        anchorSelected = self.__ctx.isAnchorSelected(slotType=slotType,
                                                     areaId=areaId,
                                                     regionIdx=regionIdx)
        itemInstalled = self.__ctx.anchorSelected(slotType, areaId, regionIdx)
        self.__hideAnchorSwitchers()
        if self.__ctx.isSlotFilled(self.__ctx.selectedAnchor):
            if self.__ctx.isCaruselItemSelected():
                if anchorSelected and not itemInstalled:
                    self.__locateCameraOnAnchor(areaId, slotType, regionIdx)
                    self.__showPropertiesSheet(areaId, slotType, regionIdx)
            else:
                self.__locateCameraOnAnchor(areaId, slotType, regionIdx)
                self.__showPropertiesSheet(areaId, slotType, regionIdx)
        else:
            self.__locateCameraOnAnchor(areaId, slotType, regionIdx)
            self.__hidePropertiesSheet()

    def __locateCameraOnAnchor(self, areaId, slotType, regionIdx):
        if self.__ctx.c11CameraManager is None:
            return
        else:
            self.__updateAnchorsData()
            if slotType in (GUI_ITEM_TYPE.EMBLEM, GUI_ITEM_TYPE.INSCRIPTION):
                if slotType == GUI_ITEM_TYPE.EMBLEM:
                    emblemType = 'player'
                    zoom = MainView._ZOOM_ON_EMBLEM
                else:
                    emblemType = 'inscription'
                    zoom = MainView._ZOOM_ON_INSCRIPTION
                self.__locatedOnEmbelem = self.__ctx.c11CameraManager.locateCameraOnEmblem(
                    areaId == Area.HULL, emblemType, regionIdx, zoom)
            else:
                anchorParams = self.service.getAnchorParams(
                    areaId, slotType, regionIdx)
                if slotType == GUI_ITEM_TYPE.PROJECTION_DECAL:
                    self.__locatedOnEmbelem = self.__ctx.c11CameraManager.locateCameraOnAnchor(
                        anchorParams.pos, anchorParams.normal)
                else:
                    self.__locatedOnEmbelem = self.__ctx.c11CameraManager.locateCameraOnAnchor(
                        anchorParams.pos, None)
            return

    def __onItemsBought(self, purchaseItems, results):
        errorCount = 0
        for result in results:
            if not result.success:
                errorCount += 1
            if result.userMsg:
                SystemMessages.pushI18nMessage(result.userMsg,
                                               type=result.sysMsgType)

        if not errorCount:
            cart = getTotalPurchaseInfo(purchaseItems)
            if cart.totalPrice != ITEM_PRICE_EMPTY:
                msgCtx = {
                    'money': formatPrice(cart.totalPrice.price),
                    'count': cart.numSelected
                }
                SystemMessages.pushI18nMessage(
                    MESSENGER.
                    SERVICECHANNELMESSAGES_SYSMSG_CONVERTER_CUSTOMIZATIONSBUY,
                    type=CURRENCY_TO_SM_TYPE.get(
                        cart.totalPrice.getCurrency(byWeight=True),
                        SM_TYPE.PurchaseForGold),
                    **msgCtx)
            else:
                SystemMessages.pushI18nMessage(
                    MESSENGER.
                    SERVICECHANNELMESSAGES_SYSMSG_CONVERTER_CUSTOMIZATIONS,
                    type=SM_TYPE.Information)
            self.__onCloseWindow(proceed=True)

    def onAnchorsShown(self, anchors):
        if self.__ctx.vehicleAnchorsUpdater is not None:
            self.__ctx.vehicleAnchorsUpdater.setAnchors(anchors)
        return

    def propertiesSheetSet(self, sheet, width, height, crnterX, centerY):
        if self.__ctx.vehicleAnchorsUpdater is not None:
            self.__ctx.vehicleAnchorsUpdater.setMenuParams(
                sheet, width, height, crnterX, centerY)
        return

    def _populate(self):
        super(MainView, self)._populate()
        self.__ctx = self.service.getCtx()
        self.__ctx.onCustomizationSeasonChanged += self.__onSeasonChanged
        self.__ctx.onCustomizationModeChanged += self.__onModeChanged
        self.__ctx.onCustomizationTabChanged += self.__onTabChanged
        self.__ctx.onCustomizationItemInstalled += self.__onItemsInstalled
        self.__ctx.onCustomizationItemsRemoved += self.__onItemsRemoved
        self.__ctx.onCustomizationItemsBought += self.__onItemsBought
        self.__ctx.onCacheResync += self.__onCacheResync
        self.__ctx.onChangesCanceled += self.__onChangesCanceled
        self.__ctx.onCaruselItemSelected += self.__onCarouselItemSelected
        self.__ctx.onPropertySheetHidden += self.__onPropertySheetHidden
        self.__ctx.onPropertySheetShown += self.__onPropertySheetShown
        self.__ctx.onClearItem += self.__onClearItem
        self.soundManager.playInstantSound(SOUNDS.ENTER)
        self.__viewLifecycleWatcher.start(self.app.containerManager, [
            _ModalWindowsPopupHandler(),
            _C11ViewsPopupHandler(self.__hidePropertiesSheet)
        ])
        self.lobbyContext.addHeaderNavigationConfirmator(
            self.__confirmHeaderNavigation)
        self.lobbyContext.getServerSettings(
        ).onServerSettingsChange += self.__onServerSettingChanged
        self.hangarSpace.onSpaceCreate += self.__onSpaceCreateHandler
        self.hangarSpace.onSpaceDestroy += self.__onSpaceDestroyHandler
        self.hangarSpace.onSpaceRefresh += self.__onSpaceRefreshHandler
        self.service.onRegionHighlighted += self.__onRegionHighlighted
        self._seasonSoundAnimantion = _SeasonSoundAnimantion(
            len(SeasonType.COMMON_SEASONS), self.soundManager)
        self.__setHeaderInitData()
        self.__setSeasonData()
        self.__ctx.refreshOutfit()
        self.as_selectSeasonS(SEASON_TYPE_TO_IDX[self.__ctx.currentSeason])
        self.fireEvent(
            CameraRelatedEvents(
                CameraRelatedEvents.FORCE_DISABLE_IDLE_PARALAX_MOVEMENT,
                ctx={'isDisable': True}), EVENT_BUS_SCOPE.LOBBY)
        self.fireEvent(
            LobbyHeaderMenuEvent(
                LobbyHeaderMenuEvent.TOGGLE_VISIBILITY,
                ctx={'state': HeaderMenuVisibilityState.ONLINE_COUNTER}),
            EVENT_BUS_SCOPE.LOBBY)
        self._isPropertySheetShown = False
        if self.__ctx.c11CameraManager is not None:
            self.__ctx.c11CameraManager.locateCameraToCustomizationPreview(
                forceLocate=True)
        self.__renderEnv = BigWorld.CustomizationEnvironment()
        self.__renderEnv.enable(True)
        if self.__ctx.vehicleAnchorsUpdater is not None:
            self.__ctx.vehicleAnchorsUpdater.setMainView(self.flashObject)
        return

    def _dispose(self):
        self.fireEvent(events.HangarCustomizationEvent(
            events.HangarCustomizationEvent.RESET_VEHICLE_MODEL_TRANSFORM),
                       scope=EVENT_BUS_SCOPE.LOBBY)
        self.fireEvent(
            LobbyHeaderMenuEvent(LobbyHeaderMenuEvent.TOGGLE_VISIBILITY,
                                 ctx={'state': HeaderMenuVisibilityState.ALL}),
            EVENT_BUS_SCOPE.LOBBY)
        self.fireEvent(
            CameraRelatedEvents(
                CameraRelatedEvents.FORCE_DISABLE_IDLE_PARALAX_MOVEMENT,
                ctx={'isDisable': False}), EVENT_BUS_SCOPE.LOBBY)
        if g_appLoader.getSpaceID() != _SPACE_ID.LOGIN:
            self.__releaseItemSound()
            self.soundManager.playInstantSound(SOUNDS.EXIT)
        if self.__ctx.c11CameraManager is not None:
            if self.__locatedOnEmbelem:
                self.__ctx.c11CameraManager.clearSelectedEmblemInfo()
            self.__ctx.c11CameraManager.locateCameraToStartState()
        self._seasonSoundAnimantion = None
        self.__renderEnv.enable(False)
        self.__renderEnv = None
        self.__viewLifecycleWatcher.stop()
        self.service.stopHighlighter()
        self.lobbyContext.deleteHeaderNavigationConfirmator(
            self.__confirmHeaderNavigation)
        self.lobbyContext.getServerSettings(
        ).onServerSettingsChange -= self.__onServerSettingChanged
        self.hangarSpace.onSpaceCreate -= self.__onSpaceCreateHandler
        self.hangarSpace.onSpaceDestroy -= self.__onSpaceDestroyHandler
        self.hangarSpace.onSpaceRefresh -= self.__onSpaceRefreshHandler
        self.service.onRegionHighlighted -= self.__onRegionHighlighted
        if g_currentVehicle.item:
            g_tankActiveCamouflage[
                g_currentVehicle.item.intCD] = self.__ctx.currentSeason
            g_currentVehicle.refreshModel()
        self.__hidePropertiesSheet()
        self.__propertiesSheet = None
        self.__ctx.onPropertySheetShown -= self.__onPropertySheetShown
        self.__ctx.onPropertySheetHidden -= self.__onPropertySheetHidden
        self.__ctx.onCaruselItemSelected -= self.__onCarouselItemSelected
        self.__ctx.onChangesCanceled -= self.__onChangesCanceled
        self.__ctx.onCacheResync -= self.__onCacheResync
        self.__ctx.onCustomizationItemsBought -= self.__onItemsBought
        self.__ctx.onCustomizationItemsRemoved -= self.__onItemsRemoved
        self.__ctx.onCustomizationItemInstalled -= self.__onItemsInstalled
        self.__ctx.onCustomizationTabChanged -= self.__onTabChanged
        self.__ctx.onCustomizationModeChanged -= self.__onModeChanged
        self.__ctx.onCustomizationSeasonChanged -= self.__onSeasonChanged
        self.__ctx.onClearItem -= self.__onClearItem
        self.__ctx = None
        self.service.destroyCtx()
        super(MainView, self)._dispose()
        return

    @adisp_process
    def _itemCtxMenuSelected(self, ctxMenuID, itemIntCD):
        item = self.itemsCache.items.getItemByCD(itemIntCD)
        if ctxMenuID == CustomizationOptions.BUY:
            yield DialogsInterface.showDialog(ConfirmC11nBuyMeta(itemIntCD))
        elif ctxMenuID == CustomizationOptions.SELL:
            inventoryCount = self.__ctx.getItemInventoryCount(item)
            yield DialogsInterface.showDialog(
                ConfirmC11nSellMeta(itemIntCD, inventoryCount,
                                    self.__ctx.sellItem))
        elif ctxMenuID == CustomizationOptions.REMOVE_FROM_TANK:
            if self.__ctx.mode == C11nMode.CUSTOM:
                self.__ctx.removeItems(False, itemIntCD)
            else:
                self.__ctx.removeStyle(itemIntCD)

    def _getUpdatedAnchorsData(self):
        tabIndex = self.__ctx.currentTab
        cType = TABS_ITEM_MAPPING[tabIndex]
        slotIds = []
        if tabIndex == C11nTabs.STYLE:
            slotId = CustomizationSlotIdVO(Area.MISC, GUI_ITEM_TYPE.STYLE, 0)
            slotIds.append(slotId)
        else:
            for areaId in Area.ALL:
                regionsIndexes = getAppliedRegionsForCurrentHangarVehicle(
                    areaId, cType)
                for regionsIndex in regionsIndexes:
                    slotId = CustomizationSlotIdVO(areaId, cType, regionsIndex)
                    slotIds.append(slotId)

        anchorVOs = []
        for zIdx, slotId in enumerate(slotIds):
            anchorVOs.append(
                CustomizationAnchorPositionVO(zIdx,
                                              slotId._asdict())._asdict())

        return CustomizationAnchorsSetVO(anchorVOs)._asdict()

    def __updateAnchorsData(self):
        self.as_setAnchorsDataS(self._getUpdatedAnchorsData())

    def __onRegionHighlighted(self, slotType, areaId, regionIdx,
                              highlightingType, highlightingResult):
        region = None
        if self.__ctx.currentTab == C11nTabs.EFFECT:
            areaId = Area.MISC
            slotType = GUI_ITEM_TYPE.MODIFICATION
        if areaId != -1 and regionIdx != -1:
            region = CustomizationSlotIdVO(areaId, slotType,
                                           regionIdx)._asdict()
        else:
            self.__hidePropertiesSheet()
        self.as_onRegionHighlightedS(region, highlightingType,
                                     highlightingResult)
        if highlightingType:
            if highlightingResult:
                anchorSelected = self.__ctx.isAnchorSelected(
                    slotType=slotType, areaId=areaId, regionIdx=regionIdx)
                itemInstalled = self.__ctx.anchorSelected(
                    slotType, areaId, regionIdx)
                slotFilled = self.__ctx.isSlotFilled(self.__ctx.selectedAnchor)
                if self.__ctx.currentTab in (C11nTabs.EFFECT, C11nTabs.STYLE):
                    applyArea = ApplyArea.ALL
                else:
                    applyArea = appliedToFromSlotsIds(
                        [self.__ctx.selectedAnchor])
                if self.__ctx.isCaruselItemSelected():
                    self.service.highlightRegions(self.__ctx.getEmptyRegions())
                    if slotFilled and anchorSelected and not itemInstalled:
                        self.service.selectRegions(applyArea)
                        self.__locateCameraOnAnchor(areaId, slotType,
                                                    regionIdx)
                        self.__showPropertiesSheet(areaId, slotType, regionIdx)
                        self.soundManager.playInstantSound(SOUNDS.CHOOSE)
                    else:
                        self.service.selectRegions(ApplyArea.NONE)
                        self.__hidePropertiesSheet()
                        self.soundManager.playInstantSound(SOUNDS.CHOOSE)
                else:
                    if slotFilled:
                        self.__locateCameraOnAnchor(areaId, slotType,
                                                    regionIdx)
                        self.__showPropertiesSheet(areaId, slotType, regionIdx)
                    else:
                        self.__resetCameraFocus()
                        self.__hidePropertiesSheet()
                    self.service.selectRegions(applyArea)
            else:
                self.__clearItem()
        elif highlightingResult:
            self.soundManager.playInstantSound(SOUNDS.HOVER)
        return

    def __onSpaceCreateHandler(self):
        Waiting.hide(_WAITING_MESSAGE)
        self.__ctx.refreshOutfit()
        self.__updateAnchorsData()

    def __onSpaceDestroyHandler(self, _):
        Waiting.hide(_WAITING_MESSAGE)
        self.__onCloseWindow(proceed=True)

    def __onSpaceRefreshHandler(self):
        Waiting.show(_WAITING_MESSAGE)

    def __onCloseWindow(self, proceed):
        if proceed:
            if self._isPropertySheetShown:
                self.__clearItem()
            self.fireEvent(events.LoadViewEvent(VIEW_ALIAS.LOBBY_HANGAR),
                           scope=EVENT_BUS_SCOPE.LOBBY)

    def __onCacheResync(self, *_):
        if not g_currentVehicle.isPresent():
            self.__onCloseWindow(proceed=True)
            return
        self.__setHeaderInitData()
        self.__setSeasonData()

    def __onChangesCanceled(self):
        self.__setHeaderInitData()
        self.__setSeasonData()
        self.__setAnchorsInitData(True)
        self.__hidePropertiesSheet()
        self.__clearItem()

    def __onCarouselItemSelected(self, index, intCD):
        tabIndex = self.__ctx.currentTab
        if tabIndex == C11nTabs.STYLE or tabIndex == C11nTabs.EFFECT:
            self.service.selectRegions(ApplyArea.ALL)
            areaId, slotType, regionIdx = self.__ctx.selectedAnchor
            self.onSelectAnchor(areaId, slotType, regionIdx)
        if not self.__propertiesSheet.isVisible and not self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.PICK)
            self.itemIsPicked = True
        if self.__ctx.isAnyAnchorSelected(
        ) and not self.__ctx.isCaruselItemSelected():
            areaId, slotType, regionIdx = self.__ctx.selectedAnchor
            self.__showPropertiesSheet(areaId, slotType, regionIdx)

    def __onServerSettingChanged(self, diff):
        if 'isCustomizationEnabled' in diff and not diff.get(
                'isCustomizationEnabled', True):
            SystemMessages.pushI18nMessage(
                SYSTEM_MESSAGES.CUSTOMIZATION_UNAVAILABLE,
                type=SystemMessages.SM_TYPE.Warning)
            self.__onCloseWindow(proceed=True)

    def __onPropertySheetShown(self):
        self._isPropertySheetShown = True
        self.__updateDnd()

    def __onPropertySheetHidden(self):
        tabIndex = self.__ctx.currentTab
        if tabIndex in C11nTabs.REGIONS:
            self.service.resetHighlighting()
        else:
            self.__hideAnchorSwitchers()
        self._isPropertySheetShown = False
        self.__updateDnd()

    def __updateDnd(self):
        isDndEnable = False
        if not self._isPropertySheetShown:
            isDndEnable = self.__ctx.currentTab not in DRAG_AND_DROP_INACTIVE_TABS
        self.as_enableDNDS(isDndEnable)

    def __setSeasonData(self, forceAnim=False):
        seasonRenderersList = []
        filledSeasonSlots = 0
        for season in SEASONS_ORDER:
            seasonName = SEASON_TYPE_TO_NAME.get(season)
            if self.__ctx.mode == C11nMode.CUSTOM:
                isFilled = self.__ctx.checkSlotsFillingForSeason(season)
            else:
                isFilled = self.__ctx.modifiedStyle is not None
            filledSeasonSlots += int(isFilled)
            seasonRenderersList.append({
                'nameText':
                VEHICLE_CUSTOMIZATION.getSeasonName(seasonName),
                'nameSelectedText':
                VEHICLE_CUSTOMIZATION.getSeasonSelectedName(seasonName),
                'seasonImageSrc':
                RES_ICONS.getSeasonImage(seasonName),
                'seasonBGImageSrc':
                RES_ICONS.getSeasonBGImage(seasonName),
                'seasonShineImageSrc':
                RES_ICONS.getSeasonShineImage(seasonName),
                'isFilled':
                isFilled,
                'forceAnim':
                forceAnim,
                'tooltip':
                makeTooltip(
                    body=VEHICLE_CUSTOMIZATION.getSheetSeasonName(seasonName))
            })

        self.as_setSeasonsBarDataS(seasonRenderersList)
        self._seasonSoundAnimantion.setFilledSeasonSlots(
            filledSeasonSlots, forceAnim)
        return

    def __setAnchorsInitData(self, update=False):
        def customizationSlotIdToUid(customizationSlotIdVO):
            s = struct.pack('bbh', customizationSlotIdVO.areaId,
                            customizationSlotIdVO.slotId,
                            customizationSlotIdVO.regionId)
            return struct.unpack('I', s)[0]

        tabIndex = self.__ctx.currentTab
        anchorVOs = []
        cType = TABS_ITEM_MAPPING[tabIndex]
        maxItemsReached = False
        if tabIndex == C11nTabs.STYLE:
            anchorId = CustomizationSlotIdVO(Area.MISC, GUI_ITEM_TYPE.STYLE, 0)
            uid = customizationSlotIdToUid(anchorId)
            anchorVOs.append(
                CustomizationSlotUpdateVO(
                    anchorId._asdict(), self.__ctx.modifiedStyle.intCD
                    if self.__ctx.modifiedStyle is not None else 0, uid,
                    None)._asdict())
        else:
            potentialPlaceTooltip = None
            if cType in QUANTITY_LIMITED_CUSTOMIZATION_TYPES:
                outfit = self.__ctx.getModifiedOutfit(self.__ctx.currentSeason)
                if self.__ctx.isC11nItemsQuantityLimitReached(outfit, cType):
                    maxItemsReached = True
                    potentialPlaceTooltip = makeTooltip(
                        VEHICLE_CUSTOMIZATION.
                        CUSTOMIZATION_TOOLTIP_POTENTIALPROJDECALPLACE_TITLE,
                        VEHICLE_CUSTOMIZATION.
                        CUSTOMIZATION_TOOLTIP_POTENTIALPROJDECALPLACE_TEXT)
            for areaId in Area.ALL:
                regionsIndexes = getAppliedRegionsForCurrentHangarVehicle(
                    areaId, cType)
                slot = self.__ctx.currentOutfit.getContainer(areaId).slotFor(
                    cType)
                for regionsIndex in regionsIndexes:
                    anchorId = CustomizationSlotIdVO(areaId, cType,
                                                     regionsIndex)
                    slotId = self.__ctx.getSlotIdByAnchorId(
                        C11nId(areaId=areaId,
                               slotType=cType,
                               regionIdx=regionsIndex))
                    itemIntCD = 0
                    if slotId is not None:
                        item = slot.getItem(slotId.regionIdx)
                        itemIntCD = item.intCD if item is not None else 0
                    tooltip = None
                    if not itemIntCD:
                        tooltip = potentialPlaceTooltip
                    uid = customizationSlotIdToUid(anchorId)
                    anchorVOs.append(
                        CustomizationSlotUpdateVO(anchorId._asdict(),
                                                  itemIntCD, uid,
                                                  tooltip)._asdict())

        if tabIndex in C11nTabs.REGIONS:
            typeRegions = CUSTOMIZATION_ALIASES.ANCHOR_TYPE_REGION
        elif tabIndex == C11nTabs.PROJECTION_DECAL:
            typeRegions = CUSTOMIZATION_ALIASES.ANCHOR_TYPE_PROJECTION_DECAL
        else:
            typeRegions = CUSTOMIZATION_ALIASES.ANCHOR_TYPE_DECAL
        if update:
            self.as_updateAnchorDataS(
                CustomizationAnchorInitVO(anchorVOs, typeRegions,
                                          maxItemsReached)._asdict())
        else:
            self.as_setAnchorInitS(
                CustomizationAnchorInitVO(anchorVOs, typeRegions,
                                          maxItemsReached)._asdict())
        return

    def __setHeaderInitData(self):
        vehicle = g_currentVehicle.item
        self.as_setHeaderDataS({
            'tankTier':
            str(int2roman(vehicle.level)),
            'tankName':
            vehicle.shortUserName,
            'tankType':
            '{}_elite'.format(vehicle.type)
            if vehicle.isElite else vehicle.type,
            'isElite':
            vehicle.isElite,
            'closeBtnTooltip':
            VEHICLE_CUSTOMIZATION.CUSTOMIZATION_HEADERCLOSEBTN
        })

    def __showPropertiesSheet(self, areaId, slotType, regionIdx):
        if self.__propertiesSheet:
            if self.__ctx.vehicleAnchorsUpdater is not None:
                self.__ctx.vehicleAnchorsUpdater.attachMenuToAnchor(
                    self.__ctx.selectedAnchor)
                if self.__ctx.currentTab in C11nTabs.REGIONS:
                    self.__ctx.vehicleAnchorsUpdater.changeAnchorParams(
                        self.__ctx.selectedAnchor, True, False)
            if self.__propertiesSheet.isVisible:
                self.soundManager.playInstantSound(SOUNDS.CHOOSE)
            self.__propertiesSheet.show(areaId, slotType, regionIdx)
            tabIndex = self.__ctx.currentTab
            if tabIndex not in C11nTabs.REGIONS:
                self.__showAnchorSwitchers(tabIndex == C11nTabs.EMBLEM)
        return

    def __hidePropertiesSheet(self):
        if self.__propertiesSheet:
            if self.__ctx.vehicleAnchorsUpdater is not None and self.__ctx.currentTab in C11nTabs.REGIONS:
                self.__ctx.vehicleAnchorsUpdater.changeAnchorParams(
                    self.__ctx.selectedAnchor, True, True)
            self.__propertiesSheet.hide()
        return

    def getItemTabsData(self):
        data = []
        pluses = []
        for tabIdx in self.__ctx.visibleTabs:
            itemTypeID = TABS_ITEM_MAPPING[tabIdx]
            typeName = GUI_ITEM_TYPE_NAMES[itemTypeID]
            showPlus = not self.__checkSlotsFilling(itemTypeID,
                                                    self._currentSeason)
            data.append({
                'label':
                _ms(ITEM_TYPES.customizationPlural(typeName)),
                'tooltip':
                makeTooltip(ITEM_TYPES.customizationPlural(typeName),
                            TOOLTIPS.customizationItemTab(typeName)),
                'id':
                tabIdx
            })
            pluses.append(showPlus)

        return (data, pluses)

    @async
    @adisp_process
    def __confirmHeaderNavigation(self, callback):
        if self._isPropertySheetShown:
            self.__clearItem()
        if self.__ctx.isOutfitsModified():
            result = yield DialogsInterface.showI18nConfirmDialog(
                'customization/close')
        else:
            result = True
        if result:
            self.__onCloseWindow(proceed=True)
        callback(result)

    def __releaseItemSound(self):
        if self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.RELEASE)
            self.itemIsPicked = False

    def __resetCameraFocus(self):
        if self.__locatedOnEmbelem:
            if self.__ctx.c11CameraManager is not None:
                self.__ctx.c11CameraManager.clearSelectedEmblemInfo()
                self.__ctx.c11CameraManager.locateCameraToCustomizationPreview(
                    preserveAngles=True)
            self.__updateAnchorsData()
            self.__locatedOnEmbelem = False
        return

    def __resetUIFocus(self):
        self.as_onRegionHighlightedS(-1, True, False)

    def __onClearItem(self):
        self.__clearItem()

    def __clearItem(self):
        self.service.highlightRegions(ApplyArea.NONE)
        self.service.selectRegions(ApplyArea.NONE)
        self.__resetCameraFocus()
        _, slotType, _ = self.__ctx.selectedAnchor
        self.__ctx.anchorSelected(slotType, -1, -1)
        self.__resetUIFocus()
        self.as_releaseItemS()

    def __hideAnchorSwitchers(self, anchor=None):
        if anchor is not None:
            areaId, slotType, regionIdx = anchor
        else:
            areaId, slotType, regionIdx = (-1, -1, -1)
        self.as_setAnchorSwitchersVisibleS(
            False,
            CustomizationSlotIdVO(areaId, slotType, regionIdx)._asdict())
        return

    def __showAnchorSwitchers(self, isNarrowSlot):
        areaId, slotType, regionIdx = self.__ctx.selectedAnchor
        self.as_setAnchorSwitchersVisibleS(
            True,
            CustomizationSlotIdVO(areaId, slotType, regionIdx)._asdict(),
            isNarrowSlot)
class MainView(CustomizationMainViewMeta):
    _COMMON_SOUND_SPACE = C11N_SOUND_SPACE
    lobbyContext = dependency.descriptor(ILobbyContext)
    itemsCache = dependency.descriptor(IItemsCache)
    service = dependency.descriptor(ICustomizationService)
    settingsCore = dependency.descriptor(ISettingsCore)

    def __init__(self, _=None):
        super(MainView, self).__init__()
        self.__viewLifecycleWatcher = ViewLifecycleWatcher()
        self.fadeAnchorsOut = False
        self._currentSeason = SeasonType.SUMMER
        self._tabIndex = C11nTabs.PAINT
        self._lastTab = C11nTabs.PAINT
        self._originalStyle = None
        self._modifiedStyle = None
        self._isCurrentStyleInstalled = False
        self._originalOutfits = {}
        self._modifiedOutfits = {}
        self._currentOutfit = None
        self._mode = C11nMode.CUSTOM
        self._isDeferredRenderer = True
        self.__anchorPositionCallbackID = None
        self._state = {}
        self._needFullRebuild = False
        self.__locatedOnEmbelem = False
        self.itemIsPicked = False
        self._vehicleCustomizationAnchorsUpdater = None
        return

    def showBuyWindow(self):
        self.__releaseItemSound()
        self.soundManager.playInstantSound(SOUNDS.SELECT)
        purchaseItems = self.getPurchaseItems()
        cart = getTotalPurchaseInfo(purchaseItems)
        if cart.totalPrice == ITEM_PRICE_EMPTY:
            self.buyAndExit(purchaseItems)
        else:
            self.as_hideAnchorPropertySheetS()
            self.fireEvent(events.LoadViewEvent(VIEW_ALIAS.CUSTOMIZATION_PURCHASE_WINDOW), EVENT_BUS_SCOPE.LOBBY)

    def onSelectItem(self, index):
        self._carouselDP.selectItemIdx(index)
        self.soundManager.playInstantSound(SOUNDS.SELECT)

    def onPickItem(self):
        if not self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.PICK)
            self.itemIsPicked = True

    def onReleaseItem(self):
        self.__releaseItemSound()

    def changeSeason(self, seasonIdx):
        self._currentSeason = SEASON_IDX_TO_TYPE[seasonIdx]
        seasonName = SEASON_TYPE_TO_NAME.get(self._currentSeason)
        self.soundManager.playInstantSound(SOUNDS.SEASON_SELECT.format(seasonName))
        self.refreshOutfit()
        self.refreshCarousel(rebuild=True)
        self.as_refreshAnchorPropertySheetS()
        doRegions = self._tabIndex in C11nTabs.REGIONS
        self.__setAnchorsInitData(self._tabIndex, doRegions, True)

    def refreshCarousel(self, rebuild=False):
        if rebuild:
            self._carouselDP.buildList(self._tabIndex, self._currentSeason, refresh=False)
            self.as_setCarouselDataS(self._buildCustomizationCarouselDataVO())
        self._carouselDP.refresh()

    def refreshHotFilters(self):
        self.as_setCarouselFiltersDataS({'hotFilters': [self._carouselDP.getOwnedFilter(), self._carouselDP.getAppliedFilter()]})

    def refreshOutfit(self):
        if self._mode == C11nMode.STYLE:
            if self._modifiedStyle:
                self._currentOutfit = self._modifiedStyle.getOutfit(self._currentSeason)
            else:
                self._currentOutfit = self.service.getEmptyOutfit()
        else:
            self._currentOutfit = self._modifiedOutfits[self._currentSeason]
        self.service.tryOnOutfit(self._currentOutfit)
        g_tankActiveCamouflage[g_currentVehicle.item.intCD] = self._currentSeason

    def showGroupFromTab(self, tabIndex):
        self.soundManager.playInstantSound(SOUNDS.TAB_SWITCH)
        self._tabIndex = tabIndex
        doRegions = self._tabIndex in C11nTabs.REGIONS
        self.service.stopHighlighter()
        if doRegions:
            itemTypeID = TABS_ITEM_MAPPING[self._tabIndex]
            self.service.startHighlighter(chooseMode(itemTypeID, g_currentVehicle.item))
        self.__setAnchorsInitData(self._tabIndex, doRegions)
        if self.__locatedOnEmbelem and g_hangarSpace.spaceInited:
            space = g_hangarSpace.space
            space.clearSelectedEmblemInfo()
            space.locateCameraToPreview()
        self.__updateAnchorPositions()
        if self._tabIndex == C11nTabs.STYLE:
            slotIdVO = CustomizationSlotIdVO(0, GUI_ITEM_TYPE.STYLE, 0)._asdict()
        elif self._tabIndex == C11nTabs.EFFECT:
            slotIdVO = CustomizationSlotIdVO(Area.MISC, GUI_ITEM_TYPE.MODIFICATION, 0)._asdict()
        else:
            slotIdVO = None
        self.as_updateSelectedRegionsS(slotIdVO)
        self.refreshCarousel(rebuild=True)
        return

    def installCustomizationElement(self, intCD, areaId, slotId, regionId, seasonIdx):
        if self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.APPLY)
        item = self.itemsCache.items.getItemByCD(intCD)
        if item.isHidden and not self.getItemInventoryCount(item):
            SystemMessages.pushI18nMessage(SYSTEM_MESSAGES.CUSTOMIZATION_PROHIBITED, type=SystemMessages.SM_TYPE.Warning, itemName=item.userName)
            return
        if self._mode == C11nMode.STYLE:
            self._modifiedStyle = item
        else:
            season = SEASON_IDX_TO_TYPE.get(seasonIdx, self._currentSeason)
            outfit = self._modifiedOutfits[season]
            outfit.getContainer(areaId).slotFor(slotId).set(item, idx=regionId)
            outfit.invalidate()
        self.refreshOutfit()
        self.__setBuyingPanelData()
        self.__setHeaderInitData()
        self.refreshCarousel(rebuild=self._carouselDP.getAppliedFilter() or self._carouselDP.getOwnedFilter())

    def clearCustomizationItem(self, areaId, slotId, regionId, seasonIdx):
        self.soundManager.playInstantSound(SOUNDS.REMOVE)
        season = SEASON_IDX_TO_TYPE.get(seasonIdx, self._currentSeason)
        outfit = self._modifiedOutfits[season]
        outfit.getContainer(areaId).slotFor(slotId).remove(idx=regionId)
        self.refreshOutfit()
        doRegions = self._tabIndex in C11nTabs.REGIONS
        self.__setAnchorsInitData(self._tabIndex, doRegions, True)
        self.__setBuyingPanelData()
        self.__setHeaderInitData()
        self.refreshCarousel(rebuild=self._carouselDP.getAppliedFilter() or self._carouselDP.getOwnedFilter())

    def switchToCustom(self, updateUI=True):
        self.soundManager.playInstantSound(SOUNDS.TAB_SWITCH)
        self._mode = C11nMode.CUSTOM
        self._tabIndex = self._lastTab
        self.refreshOutfit()
        self.as_setBottomPanelTabsDataS({'tabData': self.__getItemTabsData(),
         'selectedTab': self._tabIndex})
        self._carouselDP.selectItem()
        self.__setBuyingPanelData()
        self.__setHeaderInitData()

    def switchToStyle(self):
        self.soundManager.playInstantSound(SOUNDS.TAB_SWITCH)
        self._mode = C11nMode.STYLE
        self._lastTab = self._tabIndex
        self._tabIndex = C11nTabs.STYLE
        self.refreshOutfit()
        self.as_setBottomPanelTabsDataS({'tabData': self.__getItemTabsData(),
         'selectedTab': self._tabIndex})
        self._carouselDP.selectItem()
        self.__setBuyingPanelData()
        self.__setHeaderInitData()

    def fadeOutAnchors(self, isFadeOut):
        self.fadeAnchorsOut = isFadeOut

    def closeWindow(self):
        purchaseItems = self.getPurchaseItems()
        cart = getTotalPurchaseInfo(purchaseItems)
        if cart.numTotal:
            DialogsInterface.showDialog(I18nConfirmDialogMeta('customization/close'), self.__onConfirmCloseWindow)
        else:
            self.__onConfirmCloseWindow(proceed=True)

    def itemContextMenuDisplayed(self):
        cmHandler = self.app.contextMenuManager.getCurrentHandler()
        if isinstance(cmHandler, CustomizationItemCMHandler):
            cmHandler.onSelected += self._itemCtxMenuSelected

    def resetFilter(self):
        self.clearFilter()
        self.refreshFilterData()
        self.refreshHotFilters()
        self.refreshCarousel(rebuild=True)

    def clearFilter(self):
        self._carouselDP.clearFilter()

    def refreshFilterData(self):
        self.as_setFilterDataS(self._carouselDP.getFilterData())

    def getHistoricalPopoverData(self):
        self.soundManager.playInstantSound(SOUNDS.SELECT)
        if self._mode == C11nMode.STYLE and self._modifiedStyle:
            if not self._modifiedStyle.isHistorical():
                return {'items': [self._modifiedStyle.intCD]}
            return {'items': []}
        items = []
        for outfit in self._modifiedOutfits.itervalues():
            items.extend((item for item in outfit.items() if not item.isHistorical()))

        return {'items': [ item.intCD for item in sorted(items, key=comparisonKey) ]}

    def removeItems(self, *intCDs):
        self.soundManager.playInstantSound(SOUNDS.REMOVE)
        if self._modifiedStyle and self._modifiedStyle.intCD in intCDs:
            self._modifiedStyle = None
        for outfit in self._modifiedOutfits.itervalues():
            for slot in outfit.slots():
                for idx in range(slot.capacity()):
                    item = slot.getItem(idx)
                    if item and item.intCD in intCDs:
                        slot.remove(idx)

        self.refreshOutfit()
        self.__setHeaderInitData()
        self.__setBuyingPanelData()
        self.as_refreshAnchorPropertySheetS()
        doRegions = self._tabIndex in C11nTabs.REGIONS
        self.__setAnchorsInitData(self._tabIndex, doRegions, True)
        self.refreshCarousel(rebuild=self._carouselDP.getAppliedFilter() or self._carouselDP.getOwnedFilter())
        return

    def updatePropertySheetButtons(self, areaId, slotId, regionId):
        self.service.onPropertySheetShow(areaId, slotId, regionId)

    def onLobbyClick(self):
        if self._tabIndex in (C11nTabs.EMBLEM, C11nTabs.INSCRIPTION):
            self.as_hideAnchorPropertySheetS()
            if self.__locatedOnEmbelem and g_hangarSpace.spaceInited:
                space = g_hangarSpace.space
                space.clearSelectedEmblemInfo()
                space.locateCameraToPreview()
                self.__updateAnchorPositions()
                self.__locatedOnEmbelem = False

    def setEnableMultiselectRegions(self, isEnabled):
        pass

    def onChangeSize(self):
        self.__updateAnchorPositions()

    def onSelectAnchor(self, areaID, regionID):
        if g_hangarSpace.spaceInited:
            self.soundManager.playInstantSound(SOUNDS.CHOOSE)
            if self._tabIndex == C11nTabs.EMBLEM:
                emblemType = 'player'
                zoom = 0.06
            else:
                emblemType = 'inscription'
                zoom = 0.1
            self.__updateAnchorPositions()
            self.__locatedOnEmbelem = g_hangarSpace.space.locateCameraOnEmblem(areaID < 2, emblemType, regionID, zoom)
            self.as_cameraAutoRotateChangedS(True)
            BigWorld.callback(5, self.__cameraRotationFinished)

    def __cameraRotationFinished(self):
        self.as_cameraAutoRotateChangedS(False)

    def getOutfitsInfo(self):
        outfitsInfo = {}
        for season in SEASONS_ORDER:
            outfitsInfo[season] = OutfitInfo(self._originalOutfits[season], self._modifiedOutfits[season])

        return outfitsInfo

    def getStyleInfo(self):
        return OutfitInfo(self._originalStyle, self._modifiedStyle)

    def getPurchaseItems(self):
        return getCustomPurchaseItems(self.getOutfitsInfo()) if self._mode == C11nMode.CUSTOM else getStylePurchaseItems(self.getStyleInfo(), self._isCurrentStyleInstalled)

    def getItemInventoryCount(self, item):
        return getItemInventoryCount(item, self.getOutfitsInfo()) if self._mode == C11nMode.CUSTOM else getStyleInventoryCount(item, self.getStyleInfo())

    def getCurrentOutfit(self):
        return self._currentOutfit

    def getModifiedStyle(self):
        return self._modifiedStyle

    def getModifiedOutfit(self, season):
        return self._modifiedOutfits.get(season)

    def getMode(self):
        return self._mode

    def getCurrentSeason(self):
        return self._currentSeason

    def getCurrentTab(self):
        return self._tabIndex

    def getAppliedItems(self, isOriginal=True):
        outfits = self._originalOutfits if isOriginal else self._modifiedOutfits
        style = self._originalStyle if isOriginal else self._modifiedStyle
        seasons = SeasonType.COMMON_SEASONS if isOriginal else (self._currentSeason,)
        appliedItems = set()
        for seasonType in seasons:
            outfit = outfits[seasonType]
            appliedItems.update((i.intCD for i in outfit.items()))

        if style:
            appliedItems.add(style.intCD)
        return appliedItems

    def isItemInOutfit(self, item):
        return any((outfit.has(item) for outfit in self._originalOutfits.itervalues())) or any((outfit.has(item) for outfit in self._modifiedOutfits.itervalues()))

    @process('buyAndInstall')
    def buyAndExit(self, purchaseItems):
        self.itemsCache.onSyncCompleted -= self.__onCacheResync
        cart = getTotalPurchaseInfo(purchaseItems)
        groupHasItems = {AdditionalPurchaseGroups.STYLES_GROUP_ID: False,
         SeasonType.WINTER: False,
         SeasonType.SUMMER: False,
         SeasonType.DESERT: False}
        modifiedOutfits = {season:outfit.copy() for season, outfit in self._modifiedOutfits.iteritems()}
        results = []
        for pItem in purchaseItems:
            if not pItem.selected:
                if pItem.slot:
                    slot = modifiedOutfits[pItem.group].getContainer(pItem.areaID).slotFor(pItem.slot)
                    slot.remove(pItem.regionID)
            groupHasItems[pItem.group] = True

        if self._mode == C11nMode.CUSTOM:
            groupHasItems[self._currentSeason] = True
        empty = self.service.getEmptyOutfit()
        for season in SeasonType.COMMON_SEASONS:
            if groupHasItems[season]:
                yield OutfitApplier(g_currentVehicle.item, empty, season).request()

        for season in SeasonType.COMMON_SEASONS:
            if groupHasItems[season]:
                outfit = modifiedOutfits[season]
                result = yield OutfitApplier(g_currentVehicle.item, outfit, season).request()
                results.append(result)

        if groupHasItems[AdditionalPurchaseGroups.STYLES_GROUP_ID]:
            result = yield StyleApplier(g_currentVehicle.item, self._modifiedStyle).request()
            results.append(result)
        errorCount = 0
        for result in results:
            if not result.success:
                errorCount += 1
            if result.userMsg:
                SystemMessages.pushI18nMessage(result.userMsg, type=result.sysMsgType)

        if not errorCount:
            if cart.totalPrice != ITEM_PRICE_EMPTY:
                msgCtx = {'money': formatPrice(cart.totalPrice.price),
                 'count': cart.numSelected}
                SystemMessages.pushI18nMessage(MESSENGER.SERVICECHANNELMESSAGES_SYSMSG_CONVERTER_CUSTOMIZATIONSBUY, type=CURRENCY_TO_SM_TYPE.get(cart.totalPrice.getCurrency(byWeight=True), SM_TYPE.PurchaseForGold), **msgCtx)
            else:
                SystemMessages.pushI18nMessage(MESSENGER.SERVICECHANNELMESSAGES_SYSMSG_CONVERTER_CUSTOMIZATIONS, type=SM_TYPE.Information)
            self.__onConfirmCloseWindow(proceed=True)
        else:
            self.__onCacheResync()
        self.itemsCache.onSyncCompleted += self.__onCacheResync

    @process('sellItem')
    def sellItem(self, intCD, count):
        if not count:
            return
        self._needFullRebuild = self._carouselDP.getOwnedFilter()
        item = self.itemsCache.items.getItemByCD(intCD)
        if item.fullInventoryCount(g_currentVehicle.item) < count:
            if self._mode == C11nMode.CUSTOM:
                for season, outfit in getOutfitWithoutItems(self.getOutfitsInfo(), intCD, count):
                    yield OutfitApplier(g_currentVehicle.item, outfit, season).request()

            else:
                yield StyleApplier(g_currentVehicle.item).request()
        yield CustomizationsSeller(g_currentVehicle.item, item, count).request()
        nextTick(self.refreshOutfit)()

    def onAnchorsShown(self, anchors):
        if self._vehicleCustomizationAnchorsUpdater is not None:
            self._vehicleCustomizationAnchorsUpdater.setAnchors(anchors, self._tabIndex in C11nTabs.REGIONS)
        return

    def _populate(self):
        super(MainView, self)._populate()
        self.soundManager.playInstantSound(SOUNDS.ENTER)
        self.__viewLifecycleWatcher.start(self.app.containerManager, [_C11nWindowsLifecycleHandler()])
        self._isDeferredRenderer = self.settingsCore.getSetting(GRAPHICS.RENDER_PIPELINE) == 0
        g_clientUpdateManager.addMoneyCallback(self.__setBuyingPanelData)
        self.lobbyContext.addHeaderNavigationConfirmator(self.__confirmHeaderNavigation)
        self.lobbyContext.getServerSettings().onServerSettingsChange += self.__onServerSettingChanged
        self.service.onCarouselFilter += self.__onCarouselFilter
        self.service.onRemoveItems += self.removeItems
        self.service.onOutfitChanged += self.__onOutfitChanged
        g_eventBus.addListener(CameraRelatedEvents.IDLE_CAMERA, self.__onNotifyHangarCameraIdleStateChanged)
        g_hangarSpace.onSpaceCreate += self.__onSpaceCreateHandler
        g_hangarSpace.onSpaceDestroy += self.__onSpaceDestroyHandler
        g_hangarSpace.onSpaceRefresh += self.__onSpaceRefreshHandler
        self.service.onRegionHighlighted += self.__onRegionHighlighted
        self.itemsCache.onSyncCompleted += self.__onCacheResync
        self.__carveUpOutfits()
        if self._originalStyle and self._isCurrentStyleInstalled:
            self._mode = C11nMode.STYLE
        elif self._originalOutfits[self._currentSeason].isInstalled():
            self._mode = C11nMode.CUSTOM
        else:
            self._mode = C11nMode.STYLE
        self._carouselDP = CustomizationCarouselDataProvider(g_currentVehicle, self._carouseItemWrapper, self)
        self._carouselDP.setFlashObject(self.as_getDataProviderS())
        self._carouselDP.setEnvironment(self.app)
        self.__setHeaderInitData()
        self.__setFooterInitData()
        self.__setBuyingPanelData()
        self.__setSeasonData()
        self._vehicleCustomizationAnchorsUpdater = _VehicleCustomizationAnchorsUpdater(self.service)
        self._vehicleCustomizationAnchorsUpdater.startUpdater(self.settingsCore.interfaceScale.get())
        self.refreshOutfit()
        self.settingsCore.interfaceScale.onScaleExactlyChanged += self.__onInterfaceScaleChanged
        self.settingsCore.onSettingsChanged += self.__onSettingsChanged
        self.__updateCameraParalaxFlag()

    def _dispose(self):
        if g_appLoader.getSpaceID() != _SPACE_ID.LOGIN:
            self.__releaseItemSound()
            self.soundManager.playInstantSound(SOUNDS.EXIT)
        self.settingsCore.onSettingsChanged -= self.__onSettingsChanged
        self.settingsCore.interfaceScale.onScaleExactlyChanged -= self.__onInterfaceScaleChanged
        self._vehicleCustomizationAnchorsUpdater.stopUpdater()
        self._vehicleCustomizationAnchorsUpdater = None
        if self.__locatedOnEmbelem and g_hangarSpace.spaceInited:
            space = g_hangarSpace.space
            space.clearSelectedEmblemInfo()
            space.locateCameraToPreview()
        self.__viewLifecycleWatcher.stop()
        self.service.stopHighlighter()
        g_clientUpdateManager.removeObjectCallbacks(self)
        self.lobbyContext.deleteHeaderNavigationConfirmator(self.__confirmHeaderNavigation)
        self.lobbyContext.getServerSettings().onServerSettingsChange -= self.__onServerSettingChanged
        self.service.onCarouselFilter -= self.__onCarouselFilter
        self.service.onRemoveItems -= self.removeItems
        self.service.onOutfitChanged -= self.__onOutfitChanged
        g_eventBus.removeListener(CameraRelatedEvents.IDLE_CAMERA, self.__onNotifyHangarCameraIdleStateChanged)
        g_hangarSpace.onSpaceCreate -= self.__onSpaceCreateHandler
        g_hangarSpace.onSpaceDestroy -= self.__onSpaceDestroyHandler
        g_hangarSpace.onSpaceRefresh -= self.__onSpaceRefreshHandler
        self.service.onRegionHighlighted -= self.__onRegionHighlighted
        self.itemsCache.onSyncCompleted -= self.__onCacheResync
        if g_currentVehicle.item:
            g_tankActiveCamouflage[g_currentVehicle.item.intCD] = self._currentSeason
            g_currentVehicle.refreshModel()
        self._isCurrentStyleInstalled = False
        super(MainView, self)._dispose()
        return

    @adisp_process
    def _itemCtxMenuSelected(self, ctxMenuID, itemIntCD):
        item = self.itemsCache.items.getItemByCD(itemIntCD)
        if ctxMenuID == CustomizationOptions.BUY:
            yield DialogsInterface.showDialog(ConfirmC11nBuyMeta(itemIntCD))
        elif ctxMenuID == CustomizationOptions.SELL:
            inventoryCount = self.getItemInventoryCount(item)
            yield DialogsInterface.showDialog(ConfirmC11nSellMeta(itemIntCD, inventoryCount, self.sellItem))
        elif ctxMenuID == CustomizationOptions.REMOVE_FROM_TANK:
            self.removeItems(itemIntCD)

    def _getUpdatedAnchorPositions(self):
        anchorVOs = []
        anchorPosData = []
        cType = TABS_ITEM_MAPPING[self._tabIndex]
        if self._tabIndex == C11nTabs.STYLE:
            slotId = CustomizationSlotIdVO(0, GUI_ITEM_TYPE.STYLE, 0)
            anchorData = self.__getAnchorPositionData(slotId, 0)
            anchorPosData.append(anchorData)
        else:
            outfit = self.service.getEmptyOutfit()
            for container in outfit.containers():
                for slot in (x for x in container.slots() if x.getType() == cType):
                    for regionId, region in enumerate(slot.getRegions()):
                        slotId = CustomizationSlotIdVO(container.getAreaID(), slot.getType(), regionId)
                        anchorData = self.__getAnchorPositionData(slotId, region)
                        if anchorData is not None:
                            anchorPosData.append(anchorData)

        for zIdx, posData in enumerate(anchorPosData):
            anchorVOs.append(CustomizationAnchorPositionVO(zIdx, posData.slotId._asdict())._asdict())

        return CustomizationAnchorsSetVO(anchorVOs)._asdict()

    def _buildCustomizationCarouselDataVO(self):
        isZeroCount = self._carouselDP.itemCount == 0
        countStyle = text_styles.error if isZeroCount else text_styles.main
        displayString = text_styles.main('{} / {}'.format(countStyle(str(self._carouselDP.itemCount)), str(self._carouselDP.totalItemCount)))
        shouldShow = self._carouselDP.itemCount < self._carouselDP.totalItemCount
        return CustomizationCarouselDataVO(displayString, isZeroCount, shouldShow, itemLayoutSize=self._carouselDP.getItemSizeData(), bookmarks=self._carouselDP.getBookmarkData())._asdict()

    def _carouseItemWrapper(self, itemCD):
        item = self.itemsCache.items.getItemByCD(itemCD)
        itemInventoryCount = self.getItemInventoryCount(item)
        if item.itemTypeID == GUI_ITEM_TYPE.MODIFICATION:
            showUnsupportedAlert = not self._isDeferredRenderer
        else:
            showUnsupportedAlert = False
        isCurrentlyApplied = itemCD in self._carouselDP.getCurrentlyApplied()
        return buildCustomizationItemDataVO(item, itemInventoryCount, showUnsupportedAlert=showUnsupportedAlert, isCurrentlyApplied=isCurrentlyApplied)

    def __carveUpOutfits(self):
        for season in SeasonType.COMMON_SEASONS:
            outfit = self.service.getCustomOutfit(season)
            self._modifiedOutfits[season] = outfit.copy()
            if outfit.isInstalled():
                self._originalOutfits[season] = outfit.copy()
            self._originalOutfits[season] = self.service.getEmptyOutfit()
            for slot in self._modifiedOutfits[season].slots():
                for idx in range(slot.capacity()):
                    item = slot.getItem(idx)
                    if item and item.isHidden and item.fullInventoryCount(g_currentVehicle.item) == 0:
                        slot.remove(idx)

        style = self.service.getCurrentStyle()
        self._isCurrentStyleInstalled = self.service.isCurrentStyleInstalled()
        if self._isCurrentStyleInstalled:
            self._originalStyle = style
            self._modifiedStyle = style
        else:
            self._originalStyle = None
            if style and style.isHidden and style.fullInventoryCount(g_currentVehicle.item) == 0:
                self._modifiedStyle = None
            else:
                self._modifiedStyle = style
        return

    def __updateAnchorPositions(self, _=None):
        self.as_setAnchorPositionsS(self._getUpdatedAnchorPositions())

    def __onRegionHighlighted(self, typeID, tankPartID, regionID, selected, hovered):
        slotId = None
        if hovered:
            self.soundManager.playInstantSound(SOUNDS.HOVER)
            return
        else:
            if self._tabIndex == C11nTabs.EFFECT:
                tankPartID = Area.MISC
                typeID = GUI_ITEM_TYPE.MODIFICATION
            if tankPartID != -1 and regionID != -1:
                slotId = CustomizationSlotIdVO(tankPartID, typeID, regionID)._asdict()
                if selected:
                    self.soundManager.playInstantSound(SOUNDS.CHOOSE)
            self.as_onRegionHighlightedS(slotId)
            return

    def __onSpaceCreateHandler(self):
        Waiting.hide('loadHangarSpace')
        self.refreshOutfit()
        self.__updateAnchorPositions()

    def __onSpaceDestroyHandler(self, _):
        Waiting.hide('loadHangarSpace')
        self.__onConfirmCloseWindow(proceed=True)

    def __onSpaceRefreshHandler(self):
        Waiting.show('loadHangarSpace')

    def __onConfirmCloseWindow(self, proceed):
        if proceed:
            self.fireEvent(events.LoadViewEvent(VIEW_ALIAS.LOBBY_HANGAR), scope=EVENT_BUS_SCOPE.LOBBY)

    def __onCarouselFilter(self, **kwargs):
        if 'group' in kwargs:
            self._carouselDP.setActiveGroupIndex(kwargs['group'])
        if 'historic' in kwargs:
            self._carouselDP.setHistoricalFilter(kwargs['historic'])
        if 'inventory' in kwargs:
            self._carouselDP.setOwnedFilter(kwargs['inventory'])
        if 'applied' in kwargs:
            self._carouselDP.setAppliedFilter(kwargs['applied'])
        self.refreshCarousel(rebuild=True)
        self.refreshHotFilters()

    def __onOutfitChanged(self):
        self.refreshOutfit()
        self.__setBuyingPanelData()

    def __onCacheResync(self, *_):
        if not g_currentVehicle.isPresent():
            self.__onConfirmCloseWindow(proceed=True)
            return
        self.__preserveState()
        self.__carveUpOutfits()
        self.__restoreState()
        self.__setHeaderInitData()
        self.__setBuyingPanelData()
        if self._mode == C11nMode.CUSTOM:
            self.as_hideAnchorPropertySheetS()
        else:
            self.as_refreshAnchorPropertySheetS()
        self.refreshCarousel(rebuild=self._needFullRebuild)
        self.refreshOutfit()
        self._needFullRebuild = False

    def __preserveState(self):
        self._state.update(modifiedStyle=self._modifiedStyle, modifiedOutfits={season:outfit.copy() for season, outfit in self._modifiedOutfits.iteritems()})

    def __restoreState(self):
        self._modifiedStyle = self._state.get('modifiedStyle')
        self._modifiedOutfits = self._state.get('modifiedOutfits')
        if self._modifiedStyle:
            self._modifiedStyle = self.itemsCache.items.getItemByCD(self._modifiedStyle.intCD)
        self._state.clear()

    def __onServerSettingChanged(self, diff):
        if 'isCustomizationEnabled' in diff and not diff.get('isCustomizationEnabled', True):
            SystemMessages.pushI18nMessage(SYSTEM_MESSAGES.CUSTOMIZATION_UNAVAILABLE, type=SystemMessages.SM_TYPE.Warning)
            self.__onConfirmCloseWindow(proceed=True)

    def __setBuyingPanelData(self, *_):
        purchaseItems = self.getPurchaseItems()
        cartInfo = getTotalPurchaseInfo(purchaseItems)
        totalPriceVO = getItemPricesVO(cartInfo.totalPrice)
        accountMoney = self.itemsCache.items.stats.money
        if cartInfo.totalPrice != ITEM_PRICE_EMPTY:
            label = _ms(VEHICLE_CUSTOMIZATION.COMMIT_BUY)
            self.as_showBuyingPanelS()
        else:
            label = _ms(VEHICLE_CUSTOMIZATION.COMMIT_APPLY)
            self.as_hideBuyingPanelS()
        isAtLeastOneOufitNotEmpty = False
        for season in SeasonType.COMMON_SEASONS:
            if not self._modifiedOutfits[season].isEmpty():
                isAtLeastOneOufitNotEmpty = True
                break

        isApplyEnabled = cartInfo.minPriceItem.isDefined() and cartInfo.minPriceItem <= accountMoney or cartInfo.isAtLeastOneItemFromInventory or cartInfo.isAtLeastOneItemDismantled or self._mode == C11nMode.CUSTOM and not self._originalOutfits[self._currentSeason].isInstalled() and isAtLeastOneOufitNotEmpty
        shortage = self.itemsCache.items.stats.money.getShortage(cartInfo.totalPrice.price)
        self.as_setBottomPanelHeaderS({'buyBtnEnabled': isApplyEnabled,
         'buyBtnLabel': label,
         'enoughMoney': getItemPricesVO(ItemPrice(shortage, shortage))[0],
         'pricePanel': totalPriceVO[0]})

    def __setSeasonData(self):
        seasonRenderersList = []
        for season in SEASONS_ORDER:
            seasonName = SEASON_TYPE_TO_NAME.get(season)
            seasonRenderersList.append({'seasonName': VEHICLE_CUSTOMIZATION.getSeasonName(seasonName),
             'seasonIconSmall': RES_ICONS.getSeasonIcon(seasonName)})

        self.as_setSeasonPanelDataS({'seasonRenderersList': seasonRenderersList})

    def __setAnchorsInitData(self, tabIndex, doRegions, update=False):

        def customizationSlotIdToUid(customizationSlotIdVO):
            s = struct.pack('bbh', customizationSlotIdVO.areaId, customizationSlotIdVO.slotId, customizationSlotIdVO.regionId)
            return struct.unpack('I', s)[0]

        anchorVOs = []
        cType = TABS_ITEM_MAPPING[tabIndex]
        if tabIndex == C11nTabs.STYLE:
            slotId = CustomizationSlotIdVO(0, GUI_ITEM_TYPE.STYLE, 0)
            uid = customizationSlotIdToUid(slotId)
            popoverAlias = CUSTOMIZATION_POPOVER_ALIASES[GUI_ITEM_TYPE.STYLE]
            anchorVOs.append(CustomizationSlotUpdateVO(slotId._asdict(), popoverAlias, -1, uid)._asdict())
        else:
            for container in self._currentOutfit.containers():
                for slot in (x for x in container.slots() if x.getType() == cType):
                    for regionId, region in enumerate(slot.getRegions()):
                        slotId = CustomizationSlotIdVO(container.getAreaID(), slot.getType(), regionId)
                        popoverAlias = CUSTOMIZATION_POPOVER_ALIASES[slot.getType()]
                        item = slot.getItem(regionId)
                        itemIntCD = item.intCD if item is not None else 0
                        uid = customizationSlotIdToUid(slotId)
                        if self.__getAnchorPositionData(slotId, region) is not None:
                            anchorVOs.append(CustomizationSlotUpdateVO(slotId._asdict(), popoverAlias, itemIntCD, uid)._asdict())

        if update:
            self.as_updateAnchorDataS(CustomizationAnchorInitVO(anchorVOs, doRegions)._asdict())
        else:
            self.as_setAnchorInitS(CustomizationAnchorInitVO(anchorVOs, doRegions)._asdict())
        return

    def __setHeaderInitData(self):
        vehicle = g_currentVehicle.item
        self.as_setHeaderDataS({'tankTier': str(int2roman(vehicle.level)),
         'tankName': vehicle.shortUserName,
         'tankType': '{}_elite'.format(vehicle.type) if vehicle.isElite else vehicle.type,
         'isElite': vehicle.isElite,
         'closeBtnTooltip': VEHICLE_CUSTOMIZATION.CUSTOMIZATION_HEADERCLOSEBTN,
         'historicVO': self.__getHistoricIndicatorData()})

    def __setFooterInitData(self):
        tabsData = self.__getItemTabsData()
        self._tabIndex = first(tabsData, {}).get('id')
        self.as_setBottomPanelInitDataS({'tabData': {'tabData': tabsData,
                     'selectedTab': self._tabIndex},
         'tabsAvailableRegions': C11nTabs.AVAILABLE_REGIONS,
         'defaultStyleLabel': VEHICLE_CUSTOMIZATION.DEFAULTSTYLE_LABEL,
         'carouselInitData': self.__getCarouselInitData(),
         'switcherInitData': self.__getSwitcherInitData()})
        self.as_setCarouselFiltersInitDataS({'popoverAlias': VIEW_ALIAS.CUSTOMIZATION_FILTER_POPOVER,
         'mainBtn': {'value': RES_ICONS.MAPS_ICONS_BUTTONS_FILTER,
                     'tooltip': VEHICLE_CUSTOMIZATION.CAROUSEL_FILTER_MAINBTN},
         'hotFilters': [{'value': RES_ICONS.MAPS_ICONS_CUSTOMIZATION_STORAGE_ICON,
                         'tooltip': VEHICLE_CUSTOMIZATION.CAROUSEL_FILTER_STORAGEBTN,
                         'selected': self._carouselDP.getOwnedFilter()}, {'value': RES_ICONS.MAPS_ICONS_BUTTONS_EQUIPPED_ICON,
                         'tooltip': VEHICLE_CUSTOMIZATION.CAROUSEL_FILTER_EQUIPPEDBTN,
                         'selected': self._carouselDP.getAppliedFilter()}]})

    def onSelectHotFilter(self, index, value):
        (self._carouselDP.setOwnedFilter, self._carouselDP.setAppliedFilter)[index](value)
        self.refreshCarousel(rebuild=True)

    def __getSwitcherInitData(self):
        return {'leftLabel': VEHICLE_CUSTOMIZATION.SWITCHER_NAME_CUSTSOMSTYLE,
         'rightLabel': VEHICLE_CUSTOMIZATION.SWITCHER_NAME_DEFAULTSTYLE,
         'leftEvent': 'installStyle',
         'rightEvent': 'installStyles',
         'isLeft': self._mode == C11nMode.CUSTOM}

    def __getHistoricIndicatorData(self):
        isDefault = all((outfit.isEmpty() for outfit in self._modifiedOutfits.itervalues()))
        if self._mode == C11nMode.STYLE:
            if self._modifiedStyle:
                isHistorical = self._modifiedStyle.isHistorical()
                name = self._modifiedStyle.userName
            else:
                isHistorical = True
                name = ''
        else:
            isHistorical = all((outfit.isHistorical() for outfit in self._modifiedOutfits.itervalues()))
            name = _ms(VEHICLE_CUSTOMIZATION.HISTORICINDICATOR_STYLENAME_CUSTSOMSTYLE) if not isDefault else ''
        txtStyle = text_styles.stats if isHistorical else text_styles.tutorial
        return {'historicText': txtStyle(toUpper(name)),
         'isDefaultAppearance': isDefault,
         'isHistoric': isHistorical,
         'tooltip': TOOLTIPS.CUSTOMIZATION_NONHISTORICINDICATOR if not isHistorical else ''}

    @staticmethod
    def __getCarouselInitData():
        return {'message': '{}{}\n{}'.format(icons.makeImageTag(RES_ICONS.MAPS_ICONS_LIBRARY_ATTENTIONICONFILLED, vSpace=-3), text_styles.neutral(VEHICLE_CUSTOMIZATION.CAROUSEL_MESSAGE_HEADER), text_styles.main(VEHICLE_CUSTOMIZATION.CAROUSEL_MESSAGE_DESCRIPTION))}

    def __getAnchorPositionData(self, slotId, region):
        if self._tabIndex in C11nTabs.REGIONS:
            anchorPos = self.service.getPointForRegionLeaderLine(region)
            anchorNorm = anchorPos
        else:
            anchorPos = self.service.getPointForAnchorLeaderLine(slotId.areaId, slotId.slotId, slotId.regionId)
            anchorNorm = self.service.getNormalForAnchorLeaderLine(slotId.areaId, slotId.slotId, slotId.regionId)
        return None if anchorPos is None or anchorNorm is None else AnchorPositionData(cameras.get2DAngleFromCamera(anchorNorm), cameras.projectPoint(anchorPos), slotId)

    def __getItemTabsData(self):
        data = []
        for tabIdx in self.__getVisibleTabs():
            itemTypeID = TABS_ITEM_MAPPING[tabIdx]
            typeName = GUI_ITEM_TYPE_NAMES[itemTypeID]
            data.append({'label': _ms(ITEM_TYPES.customizationPlural(typeName)),
             'tooltip': makeTooltip(ITEM_TYPES.customizationPlural(typeName), TOOLTIPS.customizationItemTab(typeName)),
             'id': tabIdx})

        return data

    def __getVisibleTabs(self):
        visibleTabs = []
        anchorsData = g_currentVehicle.hangarSpace.getSlotPositions()
        for tabIdx in C11nTabs.VISIBLE:
            if tabIdx == C11nTabs.CAMOUFLAGE and g_currentVehicle.item.descriptor.type.hasCustomDefaultCamouflage:
                continue
            itemTypeID = TABS_ITEM_MAPPING[tabIdx]
            data = self._carouselDP.getSeasonAndTabData(tabIdx, self._currentSeason)
            if not data.itemCount:
                continue
            if itemTypeID in (GUI_ITEM_TYPE.INSCRIPTION, GUI_ITEM_TYPE.EMBLEM):
                for areaData in anchorsData.itervalues():
                    if areaData.get(itemTypeID):
                        hasSlots = True
                        break
                else:
                    hasSlots = False

                if not hasSlots:
                    continue
            visibleTabs.append(tabIdx)

        return visibleTabs

    def __onNotifyHangarCameraIdleStateChanged(self, event):
        isIdle = event.ctx.get('started', False)
        self.as_cameraAutoRotateChangedS(isIdle)

    @async
    @adisp_process
    def __confirmHeaderNavigation(self, callback):
        purchaseItems = self.getPurchaseItems()
        cart = getTotalPurchaseInfo(purchaseItems)
        if cart.numTotal:
            result = yield DialogsInterface.showI18nConfirmDialog('customization/close')
        else:
            result = True
        callback(result)

    def __releaseItemSound(self):
        if self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.RELEASE)
            self.itemIsPicked = False

    def __onSettingsChanged(self, diff):
        if GAME.HANGAR_CAM_PARALLAX_ENABLED in diff:
            self.__updateCameraParalaxFlag()

    def __updateCameraParalaxFlag(self):
        paralaxEnabled = bool(self.settingsCore.getSetting(GAME.HANGAR_CAM_PARALLAX_ENABLED))
        self.as_setParallaxFlagS(paralaxEnabled)

    def __onInterfaceScaleChanged(self, scale):
        if self._vehicleCustomizationAnchorsUpdater is not None:
            self._vehicleCustomizationAnchorsUpdater.setInterfaceScale(scale)
        return
class MainView(CustomizationMainViewMeta):
    _COMMON_SOUND_SPACE = C11N_SOUND_SPACE
    _ZOOM_ON_EMBLEM = 0.6
    _ZOOM_ON_INSCRIPTION = 0.1
    lobbyContext = dependency.descriptor(ILobbyContext)
    itemsCache = dependency.descriptor(IItemsCache)
    service = dependency.descriptor(ICustomizationService)
    settingsCore = dependency.descriptor(ISettingsCore)
    hangarSpace = dependency.descriptor(IHangarSpace)

    def __init__(self, _=None):
        super(MainView, self).__init__()
        self.__viewLifecycleWatcher = ViewLifecycleWatcher()
        self.fadeAnchorsOut = False
        self.__locatedOnEmbelem = False
        self.itemIsPicked = False
        self.__propertiesSheet = None
        self._seasonSoundAnimantion = None
        self._vehicleCustomizationAnchorsUpdater = None
        self._isPropertySheetShown = False
        self.__ctx = None
        self.__renderEnv = None
        return

    def showBuyWindow(self):
        self.changeVisible(False)
        self.__releaseItemSound()
        self.soundManager.playInstantSound(SOUNDS.SELECT)
        purchaseItems = self.__ctx.getPurchaseItems()
        cart = getTotalPurchaseInfo(purchaseItems)
        if cart.totalPrice == ITEM_PRICE_EMPTY:
            self.__ctx.applyItems(purchaseItems)
        else:
            self.fireEvent(
                events.LoadViewEvent(VIEW_ALIAS.CUSTOMIZATION_PURCHASE_WINDOW),
                EVENT_BUS_SCOPE.LOBBY)

    def onPressClearBtn(self):
        self.__ctx.cancelChanges()

    def onPressEscBtn(self):
        if self._isPropertySheetShown:
            self.__clearItem()
        else:
            self.onCloseWindow()

    def changeVisible(self, value):
        slotType, _, _ = self.__ctx.selectedRegion
        self.__ctx.regionSelected(slotType, -1, -1)
        self.as_hideS(value)

    def onReleaseItem(self):
        self.__ctx.caruselItemUnselected()
        self.__releaseItemSound()

    def _onRegisterFlashComponent(self, viewPy, alias):
        if alias == VIEW_ALIAS.CUSTOMIZATION_PROPERTIES_SHEET:
            self.__propertiesSheet = viewPy

    def changeSeason(self, seasonIdx):
        self.__ctx.changeSeason(seasonIdx)

    def __onSeasonChanged(self, seasonIdx):
        seasonName = SEASON_TYPE_TO_NAME.get(self.__ctx.currentSeason)
        self.soundManager.playInstantSound(
            SOUNDS.SEASON_SELECT.format(seasonName))
        self.__setAnchorsInitData(True)

    def __onTabChanged(self, tabIndex):
        self.soundManager.playInstantSound(SOUNDS.TAB_SWITCH)
        self.service.stopHighlighter()
        if tabIndex in C11nTabs.REGIONS:
            itemTypeID = TABS_ITEM_MAPPING[tabIndex]
            self.service.startHighlighter(
                chooseMode(itemTypeID, g_currentVehicle.item))
        self.__setAnchorsInitData()
        if self.__locatedOnEmbelem and self.hangarSpace.spaceInited:
            space = self.hangarSpace.space
            space.clearSelectedEmblemInfo()
            space.locateCameraToCustomizationPreview()
        self.__updateAnchorPositions()
        if tabIndex == C11nTabs.STYLE:
            slotIdVO = CustomizationSlotIdVO(0, GUI_ITEM_TYPE.STYLE,
                                             0)._asdict()
        elif tabIndex == C11nTabs.EFFECT:
            slotIdVO = CustomizationSlotIdVO(Area.MISC,
                                             GUI_ITEM_TYPE.MODIFICATION,
                                             0)._asdict()
        else:
            slotIdVO = None
        self.as_updateSelectedRegionsS(slotIdVO)
        self.as_enableDNDS(tabIndex not in DRAG_AND_DROP_INACTIVE_TABS)
        self.__hidePropertiesSheet()
        return

    def __onItemsInstalled(self):
        if self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.APPLY)
        self.__setHeaderInitData()
        self.__setSeasonData()
        slotType, areaId, regionIdx = self.__ctx.selectedRegion
        self.__showPropertiesSheet(areaId, slotType, regionIdx)
        self.__setAnchorsInitData(True)

    def __onItemsRemoved(self):
        self.soundManager.playInstantSound(SOUNDS.REMOVE)
        self.__setHeaderInitData()
        self.__setSeasonData()
        self.__setAnchorsInitData(True)

    def __onModeChanged(self, mode):
        self.__setHeaderInitData()
        self.__setSeasonData(True)

    def fadeOutAnchors(self, isFadeOut):
        self.fadeAnchorsOut = isFadeOut

    def onCloseWindow(self):
        if self.__ctx.isOutfitsModified():
            DialogsInterface.showDialog(
                I18nConfirmDialogMeta('customization/close'),
                self.__onCloseWindow)
        else:
            self.__onCloseWindow(proceed=True)

    def itemContextMenuDisplayed(self):
        cmHandler = self.app.contextMenuManager.getCurrentHandler()
        if isinstance(cmHandler, CustomizationItemCMHandler):
            cmHandler.onSelected += self._itemCtxMenuSelected

    def onLobbyClick(self):
        if self.__ctx.currentTab in (C11nTabs.EMBLEM, C11nTabs.INSCRIPTION):
            self.__clearItem()

    def onChangeSize(self):
        self.__updateAnchorPositions()

    def onSelectAnchor(self, areaId, slotType, regionIdx):
        if slotType not in (GUI_ITEM_TYPE.EMBLEM, GUI_ITEM_TYPE.INSCRIPTION):
            return
        if self.hangarSpace.spaceInited:
            self.soundManager.playInstantSound(SOUNDS.CHOOSE)
            if slotType == GUI_ITEM_TYPE.EMBLEM:
                emblemType = 'player'
                zoom = MainView._ZOOM_ON_EMBLEM
            else:
                emblemType = 'inscription'
                zoom = MainView._ZOOM_ON_INSCRIPTION
            self.__updateAnchorPositions()
            self.__locatedOnEmbelem = self.hangarSpace.space.locateCameraOnEmblem(
                areaId == Area.HULL, emblemType, regionIdx, zoom)
            self.as_cameraAutoRotateChangedS(True)
            BigWorld.callback(5, self.__cameraRotationFinished)
        self.__ctx.regionSelected(slotType, areaId, regionIdx)
        self.__showPropertiesSheet(areaId, slotType, regionIdx)

    def __cameraRotationFinished(self):
        self.as_cameraAutoRotateChangedS(False)

    def __onItemsBought(self, purchaseItems, results):
        errorCount = 0
        for result in results:
            if not result.success:
                errorCount += 1
            if result.userMsg:
                SystemMessages.pushI18nMessage(result.userMsg,
                                               type=result.sysMsgType)

        if not errorCount:
            cart = getTotalPurchaseInfo(purchaseItems)
            if cart.totalPrice != ITEM_PRICE_EMPTY:
                msgCtx = {
                    'money': formatPrice(cart.totalPrice.price),
                    'count': cart.numSelected
                }
                SystemMessages.pushI18nMessage(
                    MESSENGER.
                    SERVICECHANNELMESSAGES_SYSMSG_CONVERTER_CUSTOMIZATIONSBUY,
                    type=CURRENCY_TO_SM_TYPE.get(
                        cart.totalPrice.getCurrency(byWeight=True),
                        SM_TYPE.PurchaseForGold),
                    **msgCtx)
            else:
                SystemMessages.pushI18nMessage(
                    MESSENGER.
                    SERVICECHANNELMESSAGES_SYSMSG_CONVERTER_CUSTOMIZATIONS,
                    type=SM_TYPE.Information)
            self.__onCloseWindow(proceed=True)

    def onAnchorsShown(self, anchors):
        if self._vehicleCustomizationAnchorsUpdater is not None:
            self._vehicleCustomizationAnchorsUpdater.setAnchors(
                anchors, self.__ctx.currentTab in C11nTabs.REGIONS)
        return

    def _populate(self):
        super(MainView, self)._populate()
        self.__ctx = self.service.getCtx()
        self.__ctx.onCustomizationSeasonChanged += self.__onSeasonChanged
        self.__ctx.onCustomizationModeChanged += self.__onModeChanged
        self.__ctx.onCustomizationTabChanged += self.__onTabChanged
        self.__ctx.onCustomizationItemInstalled += self.__onItemsInstalled
        self.__ctx.onCustomizationItemsRemoved += self.__onItemsRemoved
        self.__ctx.onCustomizationItemsBought += self.__onItemsBought
        self.__ctx.onCacheResync += self.__onCacheResync
        self.__ctx.onChangesCanceled += self.__onChangesCanceled
        self.__ctx.onCaruselItemSelected += self.__onCaruselItemSelected
        self.__ctx.onPropertySheetHidden += self.__onPropertySheetHidden
        self.__ctx.onPropertySheetShown += self.__onPropertySheetShown
        self.soundManager.playInstantSound(SOUNDS.ENTER)
        self.__viewLifecycleWatcher.start(self.app.containerManager, [
            _ModalWindowsPopupHandler(),
            _C11ViewsPopupHandler(self.__hidePropertiesSheet)
        ])
        self.lobbyContext.addHeaderNavigationConfirmator(
            self.__confirmHeaderNavigation)
        self.lobbyContext.getServerSettings(
        ).onServerSettingsChange += self.__onServerSettingChanged
        g_eventBus.addListener(CameraRelatedEvents.IDLE_CAMERA,
                               self.__onNotifyHangarCameraIdleStateChanged)
        self.hangarSpace.onSpaceCreate += self.__onSpaceCreateHandler
        self.hangarSpace.onSpaceDestroy += self.__onSpaceDestroyHandler
        self.hangarSpace.onSpaceRefresh += self.__onSpaceRefreshHandler
        self.service.onRegionHighlighted += self.__onRegionHighlighted
        self._seasonSoundAnimantion = _SeasonSoundAnimantion(
            len(SeasonType.COMMON_SEASONS), self.soundManager)
        self.__setHeaderInitData()
        self.__setSeasonData()
        self._vehicleCustomizationAnchorsUpdater = _VehicleCustomizationAnchorsUpdater(
            self.service)
        self._vehicleCustomizationAnchorsUpdater.startUpdater(
            self.settingsCore.interfaceScale.get())
        self.__ctx.refreshOutfit()
        self.settingsCore.interfaceScale.onScaleExactlyChanged += self.__onInterfaceScaleChanged
        self.fireEvent(
            CameraRelatedEvents(
                CameraRelatedEvents.FORCE_DISABLE_IDLE_PARALAX_MOVEMENT,
                ctx={'isDisable': True}), EVENT_BUS_SCOPE.LOBBY)
        if self.hangarSpace.spaceInited:
            self.hangarSpace.space.locateCameraToCustomizationPreview()
        self.__renderEnv = BigWorld.CustomizationEnvironment()
        self.__renderEnv.enable(True)
        self._isPropertySheetShown = False

    def _dispose(self):
        self.fireEvent(events.HangarCustomizationEvent(
            events.HangarCustomizationEvent.RESET_VEHICLE_MODEL_TRANSFORM),
                       scope=EVENT_BUS_SCOPE.LOBBY)
        self.fireEvent(
            CameraRelatedEvents(
                CameraRelatedEvents.FORCE_DISABLE_IDLE_PARALAX_MOVEMENT,
                ctx={'isDisable': False}), EVENT_BUS_SCOPE.LOBBY)
        if g_appLoader.getSpaceID() != _SPACE_ID.LOGIN:
            self.__releaseItemSound()
            self.soundManager.playInstantSound(SOUNDS.EXIT)
        self.settingsCore.interfaceScale.onScaleExactlyChanged -= self.__onInterfaceScaleChanged
        self._vehicleCustomizationAnchorsUpdater.stopUpdater()
        self._vehicleCustomizationAnchorsUpdater = None
        if self.hangarSpace.spaceInited:
            space = self.hangarSpace.space
            if self.__locatedOnEmbelem:
                space.clearSelectedEmblemInfo()
            space.locateCameraToStartState()
        self._seasonSoundAnimantion = None
        self.__renderEnv.enable(False)
        self.__renderEnv = None
        self.__viewLifecycleWatcher.stop()
        self.service.stopHighlighter()
        self.lobbyContext.deleteHeaderNavigationConfirmator(
            self.__confirmHeaderNavigation)
        self.lobbyContext.getServerSettings(
        ).onServerSettingsChange -= self.__onServerSettingChanged
        g_eventBus.removeListener(CameraRelatedEvents.IDLE_CAMERA,
                                  self.__onNotifyHangarCameraIdleStateChanged)
        self.hangarSpace.onSpaceCreate -= self.__onSpaceCreateHandler
        self.hangarSpace.onSpaceDestroy -= self.__onSpaceDestroyHandler
        self.hangarSpace.onSpaceRefresh -= self.__onSpaceRefreshHandler
        self.service.onRegionHighlighted -= self.__onRegionHighlighted
        if g_currentVehicle.item:
            g_tankActiveCamouflage[
                g_currentVehicle.item.intCD] = self.__ctx.currentSeason
            g_currentVehicle.refreshModel()
        self.__hidePropertiesSheet()
        self.__propertiesSheet = None
        self.__ctx.onPropertySheetShown -= self.__onPropertySheetShown
        self.__ctx.onPropertySheetHidden -= self.__onPropertySheetHidden
        self.__ctx.onCaruselItemSelected -= self.__onCaruselItemSelected
        self.__ctx.onChangesCanceled -= self.__onChangesCanceled
        self.__ctx.onCacheResync -= self.__onCacheResync
        self.__ctx.onCustomizationItemsBought -= self.__onItemsBought
        self.__ctx.onCustomizationItemsRemoved -= self.__onItemsRemoved
        self.__ctx.onCustomizationItemInstalled -= self.__onItemsInstalled
        self.__ctx.onCustomizationTabChanged -= self.__onTabChanged
        self.__ctx.onCustomizationModeChanged -= self.__onModeChanged
        self.__ctx.onCustomizationSeasonChanged -= self.__onSeasonChanged
        self.__ctx = None
        self.service.destroyCtx()
        super(MainView, self)._dispose()
        return

    @adisp_process
    def _itemCtxMenuSelected(self, ctxMenuID, itemIntCD):
        item = self.itemsCache.items.getItemByCD(itemIntCD)
        if ctxMenuID == CustomizationOptions.BUY:
            yield DialogsInterface.showDialog(ConfirmC11nBuyMeta(itemIntCD))
        elif ctxMenuID == CustomizationOptions.SELL:
            inventoryCount = self.__ctx.getItemInventoryCount(item)
            yield DialogsInterface.showDialog(
                ConfirmC11nSellMeta(itemIntCD, inventoryCount,
                                    self.__ctx.sellItem))
        elif ctxMenuID == CustomizationOptions.REMOVE_FROM_TANK:
            if self.__ctx.mode == C11nMode.CUSTOM:
                self.__ctx.removeItems(False, itemIntCD)
            else:
                self.__ctx.removeStyle(itemIntCD)

    def _getUpdatedAnchorsData(self):
        tabIndex = self.__ctx.currentTab
        cType = TABS_ITEM_MAPPING[tabIndex]
        slotIds = []
        if tabIndex == C11nTabs.STYLE:
            slotId = CustomizationSlotIdVO(0, GUI_ITEM_TYPE.STYLE, 0)
            slotIds.append(slotId)
        else:
            for areaId in Area.ALL:
                regionsIndexes = getAppliedRegionsForCurrentHangarVehicle(
                    areaId, cType)
                for regionsIndex in regionsIndexes:
                    slotId = CustomizationSlotIdVO(areaId, cType, regionsIndex)
                    slotIds.append(slotId)

        anchorVOs = []
        for zIdx, slotId in enumerate(slotIds):
            anchorVOs.append(
                CustomizationAnchorPositionVO(zIdx,
                                              slotId._asdict())._asdict())

        return CustomizationAnchorsSetVO(anchorVOs)._asdict()

    def __updateAnchorPositions(self, _=None):
        self.as_setAnchorsDataS(self._getUpdatedAnchorsData())

    def __onRegionHighlighted(self, slotType, areaId, regionIdx, selected,
                              hovered):
        region = None
        if hovered:
            self.soundManager.playInstantSound(SOUNDS.HOVER)
            return
        else:
            if self.__ctx.currentTab == C11nTabs.EFFECT:
                areaId = Area.MISC
                slotType = GUI_ITEM_TYPE.MODIFICATION
            if areaId != -1 and regionIdx != -1:
                region = CustomizationSlotIdVO(areaId, slotType,
                                               regionIdx)._asdict()
                if selected:
                    self.soundManager.playInstantSound(SOUNDS.CHOOSE)
            else:
                self.__hidePropertiesSheet()
            self.__ctx.regionSelected(slotType, areaId, regionIdx)
            if self.__ctx.isRegionSelected():
                self.as_onRegionHighlightedS(region)
                self.__showPropertiesSheet(areaId, slotType, regionIdx)
            else:
                self.__clearItem()
            return

    def __onSpaceCreateHandler(self):
        Waiting.hide(_WAITING_MESSAGE)
        self.__ctx.refreshOutfit()
        self.__updateAnchorPositions()

    def __onSpaceDestroyHandler(self, _):
        Waiting.hide(_WAITING_MESSAGE)
        self.__onCloseWindow(proceed=True)

    def __onSpaceRefreshHandler(self):
        Waiting.show(_WAITING_MESSAGE)

    def __onCloseWindow(self, proceed):
        if proceed:
            if self._isPropertySheetShown:
                self.__clearItem()
            self.fireEvent(events.LoadViewEvent(VIEW_ALIAS.LOBBY_HANGAR),
                           scope=EVENT_BUS_SCOPE.LOBBY)

    def __onCacheResync(self, *_):
        if not g_currentVehicle.isPresent():
            self.__onCloseWindow(proceed=True)
            return
        self.__setHeaderInitData()
        self.__setSeasonData()

    def __onChangesCanceled(self):
        self.__hidePropertiesSheet()

    def __onCaruselItemSelected(self, index, intCD):
        tabIndex = self.__ctx.currentTab
        if tabIndex == C11nTabs.STYLE or tabIndex == C11nTabs.EFFECT:
            slotType, areaId, regionIdx = self.__ctx.selectedRegion
            self.__onRegionHighlighted(slotType, areaId, regionIdx, True,
                                       False)
        if not self.__propertiesSheet.isVisible and not self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.PICK)
            self.itemIsPicked = True

    def __onServerSettingChanged(self, diff):
        if 'isCustomizationEnabled' in diff and not diff.get(
                'isCustomizationEnabled', True):
            SystemMessages.pushI18nMessage(
                SYSTEM_MESSAGES.CUSTOMIZATION_UNAVAILABLE,
                type=SystemMessages.SM_TYPE.Warning)
            self.__onCloseWindow(proceed=True)

    def __onPropertySheetShown(self):
        self._isPropertySheetShown = True

    def __onPropertySheetHidden(self):
        self.service.resetHighlighting()
        self.__clearItem()
        self._isPropertySheetShown = False

    def __setSeasonData(self, forceAnim=False):
        seasonRenderersList = []
        filledSeasonSlots = 0
        for season in SEASONS_ORDER:
            seasonName = SEASON_TYPE_TO_NAME.get(season)
            if self.__ctx.mode == C11nMode.CUSTOM:
                isFilled = self.__ctx.checkSlotsFillingForSeason(season)
            else:
                isFilled = self.__ctx.modifiedStyle is not None
            filledSeasonSlots += int(isFilled)
            seasonRenderersList.append({
                'nameText':
                VEHICLE_CUSTOMIZATION.getSeasonName(seasonName),
                'nameSelectedText':
                VEHICLE_CUSTOMIZATION.getSeasonSelectedName(seasonName),
                'seasonImageSrc':
                RES_ICONS.getSeasonImage(seasonName),
                'seasonBGImageSrc':
                RES_ICONS.getSeasonBGImage(seasonName),
                'isFilled':
                isFilled,
                'forceAnim':
                forceAnim
            })

        self.as_setSeasonsBarDataS(seasonRenderersList)
        self._seasonSoundAnimantion.setFilledSeasonSlots(
            filledSeasonSlots, forceAnim)
        return

    def __setAnchorsInitData(self, update=False):
        def customizationSlotIdToUid(customizationSlotIdVO):
            s = struct.pack('bbh', customizationSlotIdVO.areaId,
                            customizationSlotIdVO.slotId,
                            customizationSlotIdVO.regionId)
            return struct.unpack('I', s)[0]

        tabIndex = self.__ctx.currentTab
        anchorVOs = []
        cType = TABS_ITEM_MAPPING[tabIndex]
        if tabIndex == C11nTabs.STYLE:
            slotId = CustomizationSlotIdVO(0, GUI_ITEM_TYPE.STYLE, 0)
            uid = customizationSlotIdToUid(slotId)
            anchorVOs.append(
                CustomizationSlotUpdateVO(slotId._asdict(), -1, uid)._asdict())
        else:
            for areaId in Area.ALL:
                regionsIndexes = getAppliedRegionsForCurrentHangarVehicle(
                    areaId, cType)
                slot = self.__ctx.currentOutfit.getContainer(areaId).slotFor(
                    cType)
                for regionsIndex in regionsIndexes:
                    slotId = CustomizationSlotIdVO(areaId, cType, regionsIndex)
                    item = slot.getItem(regionsIndex)
                    itemIntCD = item.intCD if item is not None else 0
                    uid = customizationSlotIdToUid(slotId)
                    anchorVOs.append(
                        CustomizationSlotUpdateVO(slotId._asdict(), itemIntCD,
                                                  uid)._asdict())

        doRegions = tabIndex in C11nTabs.REGIONS
        if update:
            self.as_updateAnchorDataS(
                CustomizationAnchorInitVO(anchorVOs, doRegions)._asdict())
        else:
            self.as_setAnchorInitS(
                CustomizationAnchorInitVO(anchorVOs, doRegions)._asdict())
        return

    def __setHeaderInitData(self):
        vehicle = g_currentVehicle.item
        self.as_setHeaderDataS({
            'tankTier':
            str(int2roman(vehicle.level)),
            'tankName':
            vehicle.shortUserName,
            'tankType':
            '{}_elite'.format(vehicle.type)
            if vehicle.isElite else vehicle.type,
            'isElite':
            vehicle.isElite,
            'closeBtnTooltip':
            VEHICLE_CUSTOMIZATION.CUSTOMIZATION_HEADERCLOSEBTN
        })

    def __showPropertiesSheet(self, areaId, slotId, regionId):
        if self.__propertiesSheet:
            self.__propertiesSheet.show(areaId, slotId, regionId)

    def __hidePropertiesSheet(self):
        if self.__propertiesSheet:
            self.__propertiesSheet.hide()

    def getItemTabsData(self):
        data = []
        pluses = []
        for tabIdx in self.__ctx.visibleTabs:
            itemTypeID = TABS_ITEM_MAPPING[tabIdx]
            typeName = GUI_ITEM_TYPE_NAMES[itemTypeID]
            showPlus = not self.__checkSlotsFilling(itemTypeID,
                                                    self._currentSeason)
            data.append({
                'label':
                _ms(ITEM_TYPES.customizationPlural(typeName)),
                'tooltip':
                makeTooltip(ITEM_TYPES.customizationPlural(typeName),
                            TOOLTIPS.customizationItemTab(typeName)),
                'id':
                tabIdx
            })
            pluses.append(showPlus)

        return (data, pluses)

    def __onNotifyHangarCameraIdleStateChanged(self, event):
        isIdle = event.ctx.get('started', False)
        self.as_cameraAutoRotateChangedS(isIdle)

    @async
    @adisp_process
    def __confirmHeaderNavigation(self, callback):
        if self.__ctx.isOutfitsModified():
            result = yield DialogsInterface.showI18nConfirmDialog(
                'customization/close')
        else:
            result = True
        if result:
            self.__onCloseWindow(proceed=True)
        callback(result)

    def __releaseItemSound(self):
        if self.itemIsPicked:
            self.soundManager.playInstantSound(SOUNDS.RELEASE)
            self.itemIsPicked = False

    def __onInterfaceScaleChanged(self, scale):
        if self._vehicleCustomizationAnchorsUpdater is not None:
            self._vehicleCustomizationAnchorsUpdater.setInterfaceScale(scale)
        return

    def __resetCameraFocus(self):
        if self.__locatedOnEmbelem and self.hangarSpace.spaceInited:
            space = self.hangarSpace.space
            space.clearSelectedEmblemInfo()
            space.locateCameraToCustomizationPreview()
            self.__updateAnchorPositions()
            self.__locatedOnEmbelem = False

    def __resetUIFocus(self):
        self.as_onRegionHighlightedS(-1)

    def __clearItem(self):
        self.__hidePropertiesSheet()
        if self.__ctx.currentTab in (C11nTabs.EMBLEM, C11nTabs.INSCRIPTION):
            self.__resetCameraFocus()
        slotType, _, _ = self.__ctx.selectedRegion
        self.__ctx.regionSelected(slotType, -1, -1)
        self.__resetUIFocus()
Beispiel #12
0
class ChallengeRewardView(ViewImpl):
    __eventsCache = dependency.descriptor(IEventsCache)
    __nyController = dependency.descriptor(INewYearController)

    def __init__(self, layoutID, questID):
        settings = ViewSettings(layoutID)
        settings.model = ChallengeRewardViewModel()
        self.__questID = questID
        self.__viewLifecycleWatcher = ViewLifecycleWatcher()
        self._tooltips = {}
        super(ChallengeRewardView, self).__init__(settings)

    @property
    def viewModel(self):
        return super(ChallengeRewardView, self).getViewModel()

    def createPopOverContent(self, event):
        if event.contentID == R.views.common.pop_over_window.backport_pop_over.BackportPopOverContent(
        ):
            if event.getArgument(
                    'popoverId'
            ) == Discount.NEW_YEAR_DISCOUNT_APPLY_POPOVER_ID:
                alias = VIEW_ALIAS.NY_SELECT_VEHICLE_FOR_DISCOUNT_POPOVER
                variadicID = event.getArgument('variadicID')
                data = createPopOverData(alias, {'variadicID': variadicID})
                return BackportPopOverContent(popOverData=data)
        return super(ChallengeRewardView, self).createPopOverContent(event)

    @nyCreateToolTipContentDecorator
    def createToolTipContent(self, event, contentID):
        if contentID == R.views.lobby.new_year.tooltips.NyVehicleSlotTooltip():
            tooltipData = self._tooltips.get(event.getArgument('tooltipId'))
            if tooltipData is None:
                return
            return NyVehicleSlotTooltip(*tooltipData.specialArgs)
        else:
            return super(ChallengeRewardView,
                         self).createToolTipContent(event, contentID)

    @backportTooltipDecorator()
    def createToolTip(self, event):
        return super(ChallengeRewardView, self).createToolTip(event)

    def _onLoaded(self, *args, **kwargs):
        setOverlayHangarGeneral(onState=True)
        NewYearSoundsManager.playEvent(
            NewYearSoundEvents.CELEBRITY_SCREEN_REWARD)
        super(ChallengeRewardView, self)._onLoaded(*args, **kwargs)

    def _onLoading(self, *args, **kwargs):
        quest = self.__eventsCache.getQuestByID(self.__questID)
        with self.viewModel.transaction() as model:
            model.setRecommendedGraphicsPreset(
                BigWorld.detectGraphicsPresetFromSystemSettings())
            packBonusModelAndTooltipData(quest.getBonuses(),
                                         model.getRewards(),
                                         getNewYearBonusPacker(),
                                         self._tooltips)
            tokenCount = marathonTokenCountExtractor(quest)
            self.viewModel.setCompletedQuests(tokenCount)
            finalQuest = getFinalCelebrityMarathonQuest()
            self.viewModel.setIsFinal(finalQuest.getID() == quest.getID())

    def _initialize(self, *args, **kwargs):
        super(ChallengeRewardView, self)._initialize()
        self.__addListeners()

    def _finalize(self):
        self.__removeListeners()
        setOverlayHangarGeneral(onState=False)

    def __updateVariadicRewards(self):
        with self.viewModel.transaction() as model:
            for reward in model.getRewards():
                if reward.getName() == VARIADIC_DISCOUNT_NAME:
                    updateSelectedVehicleForBonus(reward)

            model.setSyncInitiator((model.getSyncInitiator() + 1) % 1000)

    def __addListeners(self):
        model = self.viewModel
        model.sendCloseEvent += self.__onClose
        self.viewModel.goToNyVehicleBranch += self.__onToVehicleBranch
        self.__nyController.onDataUpdated += self.__onDataUpdated
        self.__nyController.onStateChanged += self.__onEventStateChanged
        self.__viewLifecycleWatcher.start(
            dependency.instance(IAppLoader).getApp().containerManager, [
                DiscountPopoverHandler(self.__onDiscountPopoverOpened,
                                       self.__onDiscountPopoverClosed)
            ])

    def __removeListeners(self):
        model = self.viewModel
        model.sendCloseEvent -= self.__onClose
        self.viewModel.goToNyVehicleBranch -= self.__onToVehicleBranch
        self.__nyController.onDataUpdated -= self.__onDataUpdated
        self.__nyController.onStateChanged -= self.__onEventStateChanged
        self.__viewLifecycleWatcher.stop()

    def __onDataUpdated(self, keys):
        if SyncDataKeys.SELECTED_DISCOUNTS in keys:
            self.__updateVariadicRewards()

    def __onToVehicleBranch(self):
        showNewYearVehiclesView()
        self.__onClose()

    def __onClose(self, *_):
        self.destroyWindow()

    def __onEventStateChanged(self):
        if not self.__nyController.isEnabled():
            self.destroyWindow()

    def __onDiscountPopoverOpened(self):
        self.viewModel.setIsDiscountPopoverOpened(True)

    def __onDiscountPopoverClosed(self):
        self.viewModel.setIsDiscountPopoverOpened(False)