local GUI = {
	-- GUI Survival
	{type = 'header', text = 'Survival', align = 'center'},
	{type = 'checkbox', text = 'Enable Lay on Hands', key = 'S_LOHE', default = true},
	{type = 'spinner', text = '', key = 'S_LOH', default = 10},
	{type = 'checkbox', text = 'Enable Flash of Light', key = 'S_FOLE', default = true},
	{type = 'spinner', text = '', key = 'S_FOL', default = 40},
	{type = 'checkbox', text = 'Enable Shield of Vengeance', key = 'S_SOVE', default = true},
	{type = 'spinner', text = '', key = 'S_SOV', default = 75},
	{type = 'checkbox', text = 'Enable Eye for an Eye', key = 'S_EFAEE', default = true},
	{type = 'spinner', text = '', key = 'S_EFAE', default = 90},
	--{type = 'checkbox', text = 'Enable Every Man for Himself (Stun)', key = 'S_EMFHSE', default = true},
	--{type = 'checkbox', text = 'Enable Blessing of Freedom (Root)', key = 'S_BOFRE', default = true},
	--{type = 'checkbox', text = 'Enable Blessing of Freedom (Snare)', key = 'S_BOFSE', default = true},
	{type = 'checkbox', text = 'Enable Gift of the Naaru', key = 'S_GOTNE', default = true},
	{type = 'spinner', text = '', key = 'S_GOTN', default = 40},
	{type = 'checkbox', text = 'Enable Healthstone', key = 'S_HSE', default = true},
	{type = 'spinner', text = '', key = 'S_HS', default = 20},
	{type = 'checkbox', text = 'Enable Ancient Healing Potion', key = 'S_AHPE', default = true},
	{type = 'spinner', text = '', key = 'S_AHP', default = 20},
	{type = 'ruler'},{type = 'spacer'},

	-- GUI Emergency Group Assistance
	{type = 'header', text = 'Emergency Group Assistance', align = 'center'},
	{type = 'checkbox', text = 'Enable Emergency Group Assistance', key = 'E_GAE', default = false},
	{type = 'text', text = 'Flash of Light'},
	{type = 'spinner', text = '', key = 'E_FOL', default = 35},
	{type = 'text', text = 'Lay on Hands'},
	{type = 'spinner', text = '', key = 'E_LOH', default = 10},
	{type = 'text', text = 'Blessing of Protection'},
	{type = 'spinner', text = '', key = 'E_BOP', default = 10},
	{type = 'ruler'},{type = 'spacer'},

	-- GUI Blessings
	{type = 'header', text = 'Blessings', align = 'center'},
	{type = 'text', text = 'Check to enable blessings on yourself'},
	{type = 'checkbox', text = 'Blessing of Kings', key = 'B_BOKP', default = false},
	{type = 'checkbox', text = 'Blessing of Wisdom', key = 'B_BOWP', default = false},
	{type = 'ruler'},{type = 'spacer'},
}

local exeOnLoad = function()
	-- Rotation loaded message.
	print('|cfff58cba ----------------------------------------------------------------------|r')
	print('|cfff58cba --- |rPaladin: |cfff58cbaRETRIBUTION|r')
	print('|cfff58cba --- |rTalents: 1/2 - 2/2 - 3/1 - 4/2 - 5/2 - 6/1 - 7/2|r')
	print('|cfff58cba --- |rNo Multitarget (AoE) enabled settings|r')
	print('|cfff58cba ----------------------------------------------------------------------|r')
	print('|cffff0000 Configuration: |rRight-click the MasterToggle and go to Combat Routines Settings|r')

	NeP.Interface:AddToggle({
		-- Cleanse Toxin
		key = 'yuCT',
		name = 'Cleanse Toxin',
		text = 'Enable/Disable: Automatic removal of Poison and Diseases',
		icon = 'Interface\\ICONS\\spell_holy_renew',
	})
	NeP.Interface:AddToggle({
		-- Emergency Group Assistance
		key = 'yuEGA',
		name = 'Emergency Group Assistance',
		text = 'Enable/Disable: Automatic LoH/BoP/FoL on group members',
		icon = 'Interface\\ICONS\\ability_fiegndead',
	})
end

local Survival = {
	-- Lay on Hands usage if enabled in UI.
	{'&Lay on Hands', 'UI(S_LOHE)&{!player.debuff(Ignite Soul)}&player.health<=UI(S_LOH)'},
	-- Shield of Vengeance usage if enabled in UI.
	{'&Shield of Vengeance', 'UI(S_SOVE)&player.health<=UI(S_SOV)'},
	-- Eye for an Eye usage if enabled in UI.
	{'Eye for an Eye', 'talent(5,2)&UI(S_EFAEE)&player.health<=UI(S_EFAE)'},
	-- Every Man for Himself usage if enabled in UI.
	--{'&Every Man for Himself', 'UI(S_EMFHSE)&player.state(stun)'},
	-- Blessing of Freedom usage if enabled in UI.
	--{'!Blessing of Freedom', 'UI(S_BOFRE)&player.state(root)'},
	-- Blessing of Freedom usage if enabled in UI.
	--{'!Blessing of Freedom', 'UI(S_BOFSE)&player.state(snare)'},
	-- Gift of the Naaru usage if enabled in UI.
	{'&Gift of the Naaru', 'UI(S_GOTNE)&{!player.debuff(Ignite Soul)}&player.health<=UI(S_GOTN)'},
	-- Healthstone usage if enabled in UI.
	{'#5512', 'UI(S_HSE)&{!player.debuff(Ignite Soul)}&player.health<=UI(S_HS)'},
	-- Ancient Healing Potion usage if enabled in UI.
	{'#127834', 'UI(S_AHPE)&{!player.debuff(Ignite Soul)}&player.health<=UI(S_AHP)'},
}

local Player = {
	-- Flash of Light usage if enabled in UI.
	{'!Flash of Light', 'UI(S_FOLE)&{!player.debuff(Ignite Soul)}&player.health<=UI(S_FOL)', 'player'},
}

local Emergency = {
	-- Flash of Light usage if enabled in UI.
	{'!Flash of Light', 'UI(E_GAE)&{!lowest.debuff(Ignite Soul)}&lowest.health<=UI(E_FOL)', 'lowest'},
	-- Lay on Hands usage if enabled in UI.
	{'!Lay on Hands', 'UI(E_GAE)&{!lowest.debuff(Ignite Soul)}&lowest.health<=UI(E_LOH)', 'lowest'},
	-- Blessing of Protection usage if enabled in UI.
	{'!Blessing of Protection', 'UI(E_GAE)&{!lowest.debuff(Ignite Soul)}&lowest.health<=UI(E_BOP)', 'lowest'},
}

local Interrupts = {
	{'&Rebuke', 'target.range<=5'},
	{'Hammer of Justice', '!equipped(Justice Gaze)&target.range<=10&spell(Rebuke).cooldown>gcd&!lastgcd(Rebuke)'},
	{'Hammer of Justice', 'equipped(Justice Gaze)&target.health>=75&target.range<=10&spell(Rebuke).cooldown>gcd&!lastgcd(Rebuke)'},
	{'Blinding Light', 'talent(3,3)&target.range<=10&spell(Rebuke).cooldown>gcd&!lastgcd(Rebuke)'},
	{'&Arcane Torrent', 'target.range<=8&spell(Rebuke).cooldown>gcd&!lastgcd(Rebuke)'},
}

local Dispel = {
	{'%dispelself'},
}

local Blessings = {
	{'Greater Blessing of Kings', 'UI(B_BOKP)&!player.buff(Greater Blessing of Kings)', 'player'},
	{'Greater Blessing of Wisdom', 'UI(B_BOWP)&!player.buff(Greater Blessing of Wisdom)', 'player'},
}

-- ####################################################################################
-- Primairly sourced from legion-dev SimC.
-- Updates to rotations from sources are considered for implementation.
-- ####################################################################################

-- SimC APL 4/20/2017
-- https://github.com/simulationcraft/simc/blob/legion-dev/profiles/Tier19M/Paladin_Retribution_T19M.simc

local Cooldowns = {
	--actions+=/arcane_torrent,if=holy_power<5&(buff.crusade.up|buff.avenging_wrath.up|time<2)
	{'&Arcane Torrent', 'holypower<5&{player.buff(Crusade)||player.buff(Avenging Wrath)||combat(player).time<2}'},
	--actions+=/holy_wrath
	{'Holy Wrath', 'talent(7,3)'},
	--actions+=/avenging_wrath
	{'&Avenging Wrath', '!talent(7,2)'},
	--actions+=/crusade,if=holy_power>=5&!equipped.137048|((equipped.137048|race.blood_elf)&holy_power>=2)
	{'&Crusade', 'talent(7,2)&{holypower>=5&!equipped(Liadrin\'s Fury Unleashed)||{equipped(Liadrin\'s Fury Unleashed)&holypower>=2}}'},
}

local Combat = {
	{'/startattack', '!isattacking'},
	--actions+=/judgment,if=time<2
	{'Judgment', 'combat(player).time<2'},
	--actions+=/blade_of_justice,if=time<2&(equipped.137048|race.blood_elf)
	{'Blade of Justice', 'combat(player).time<2&equipped(Liadrin\'s Fury Unleashed)'},
	--actions+=/divine_hammer,if=time<2&(equipped.137048|race.blood_elf)
	{'Divine Hammer', 'talent(4,3)&combat(player).time<2&equipped(Liadrin\'s Fury Unleashed)'},
	--actions+=/wake_of_ashes,if=holy_power<=1&time<2
	{'Wake of Ashes', 'holypower<=1&combat(player).time<2'},
	--actions+=/execution_sentence,if=spell_targets.divine_storm<=3&(cooldown.judgment.remains<gcd*4.5|debuff.judgment.remains>gcd*4.5)&(!talent.crusade.enabled|cooldown.crusade.remains>gcd*2)
	{'Execution Sentence','talent(1,2)&player.area(8).enemies<=3&{spell(Judgment).cooldown<gcd*4.5||target.debuff(judgment).duration>gcd*4.5}&{!talent(7,2)||talent(7,2)&!toggle(cooldowns)||spell(Crusade).cooldown>gcd*2}'},
	--actions+=/divine_storm,if=debuff.judgment.up&spell_targets.divine_storm>=2&buff.divine_purpose.up&buff.divine_purpose.remains<gcd*2
	{'Divine Storm', 'target.debuff(Judgment)&player.area(8).enemies>=2&player.buff(Divine Purpose).duration<gcd*2'},
	--actions+=/divine_storm,if=debuff.judgment.up&spell_targets.divine_storm>=2&holy_power>=5&buff.divine_purpose.react
	{'Divine Storm', 'target.debuff(Judgment)&player.area(8).enemies>=2&holypower>=5&player.buff(Divine Purpose)'},
	--actions+=/divine_storm,if=debuff.judgment.up&spell_targets.divine_storm>=2&holy_power>=3&(buff.crusade.up&(buff.crusade.stack<15|buff.bloodlust.up)|buff.liadrins_fury_unleashed.up)
	{'Divine Storm', 'target.debuff(Judgment)&player.area(8).enemies>=2&holypower>=3&{player.buff(Crusade)&{player.buff(Crusade).count<15||hashero}||player.buff(Liadrin\'s Fury Unleashed)}'},
	--actions+=/divine_storm,if=debuff.judgment.up&spell_targets.divine_storm>=2&holy_power>=5&(!talent.crusade.enabled|cooldown.crusade.remains>gcd*3)
	{'Divine Storm', 'target.debuff(Judgment)&player.area(8).enemies>=2&holypower>=5&{!talent(7,2)||talent(7,2)&!toggle(cooldowns)||spell(Crusade).cooldown>gcd*3}'},
	--actions+=/templars_verdict,if=debuff.judgment.up&buff.divine_purpose.up&buff.divine_purpose.remains<gcd*2
	{'Templar\'s Verdict', 'target.debuff(Judgment)&player.buff(Divine Purpose).duration<gcd*2'},
	--actions+=/templars_verdict,if=debuff.judgment.up&holy_power>=5&buff.divine_purpose.react
	{'Templar\'s Verdict', 'target.debuff(Judgment)&holypower>=5&player.buff(Divine Purpose)'},
	--actions+=/templars_verdict,if=debuff.judgment.up&holy_power>=3&(buff.crusade.up&(buff.crusade.stack<15|buff.bloodlust.up)|buff.liadrins_fury_unleashed.up)
	{'Templar\'s Verdict', 'target.debuff(Judgment)&holypower>=3&{player.buff(Crusade)&{player.buff(Crusade).count<15||hashero}||player.buff(Liadrin\'s Fury Unleashed)}'},
	--actions+=/templars_verdict,if=debuff.judgment.up&holy_power>=5&(!talent.crusade.enabled|cooldown.crusade.remains>gcd*3)&(!talent.execution_sentence.enabled|cooldown.execution_sentence.remains>gcd)
	{'Templar\'s Verdict', 'target.debuff(Judgment)&holypower>=5&{!talent(7,2)||talent(7,2)&!toggle(cooldowns)||spell(Crusade).cooldown>gcd*3}&{!talent(1,2)||spell(Execution Sentence).cooldown>gcd}'},
	--actions+=/divine_storm,if=debuff.judgment.up&holy_power>=3&spell_targets.divine_storm>=2&(cooldown.wake_of_ashes.remains<gcd*2&artifact.wake_of_ashes.enabled|buff.whisper_of_the_nathrezim.up&buff.whisper_of_the_nathrezim.remains<gcd)&(!talent.crusade.enabled|cooldown.crusade.remains>gcd*4)
	{'Divine Storm', 'target.debuff(Judgment)&holypower>=3&player.area(8).enemies>=2&{spell(Wake of Ashes).cooldown<gcd*2&artifact(Wake of Ashes).enabled||player.buff(Whisper of the Nathrezim).duration<gcd}&{!talent(7,2)||talent(7,2)&!toggle(cooldowns)||spell(Crusade).cooldown>gcd*4}'},
	--actions+=/templars_verdict,if=debuff.judgment.up&holy_power>=3&(cooldown.wake_of_ashes.remains<gcd*2&artifact.wake_of_ashes.enabled|buff.whisper_of_the_nathrezim.up&buff.whisper_of_the_nathrezim.remains<gcd)&(!talent.crusade.enabled|cooldown.crusade.remains>gcd*4)
	{'Templar\'s Verdict', 'target.debuff(Judgment)&holypower>=3&{spell(Wake of Ashes).cooldown<gcd*2&artifact(Wake of Ashes).enabled||player.buff(Whisper of the Nathrezim).duration<gcd}&{!talent(7,2)||talent(7,2)&!toggle(cooldowns)||spell(Crusade).cooldown>gcd*4}'},
	--actions+=/judgment,if=dot.execution_sentence.ticking&dot.execution_sentence.remains<gcd*2&debuff.judgment.remains<gcd*2
	{'Judgment', 'target.debuff(Execution Sentence).duration<gcd*2&target.debuff(Judgment).duration<gcd*2'},
	--actions+=/wake_of_ashes,if=(!raid_event.adds.exists|raid_event.adds.in>15)&(holy_power=0|holy_power=1&(cooldown.blade_of_justice.remains>gcd|cooldown.divine_hammer.remains>gcd)|holy_power=2&(cooldown.zeal.charges_fractional<=0.65|cooldown.crusader_strike.charges_fractional<=0.65))
	{'Wake of Ashes', 'holypower=0||holypower=1&{spell(Blade of Justice).cooldown>gcd||spell(Divine Hammer).cooldown>gcd}||holypower=2&{spell(Zeal).charges<=0.65||spell(Crusader Strike).charges<=0.65}'},
	--actions+=/blade_of_justice,if=(holy_power<=2&set_bonus.tier20_2pc=1|holy_power<=3&set_bonus.tier20_2pc=0)
	{'Blade of Justice', 'holypower<=2&set_bonus(T20)>=2||holypower<=3&set_bonus(T20)=0'},
	--actions+=/divine_hammer,if=(holy_power<=2&set_bonus.tier20_2pc=1|holy_power<=3&set_bonus.tier20_2pc=0)
	{'Divine Hammer', 'holypower<=2&set_bonus(T20)>=2||holypower<=3&set_bonus(T20)=0'},
	--actions+=/hammer_of_justice,if=equipped.137065&target.health.pct>=75&holy_power<=4
	{'Hammer of Justice', 'equipped(Justice Gaze)&target.health>=75&holypower<=4'},
	--actions+=/judgment
	{'Judgment'},
	--actions+=/zeal,if=charges=2&(set_bonus.tier20_2pc=0&holy_power<=2|(holy_power<=4&(cooldown.divine_hammer.remains>gcd*2|cooldown.blade_of_justice.remains>gcd*2)&cooldown.judgment.remains>gcd*2))|(set_bonus.tier20_2pc=1&holy_power<=1|(holy_power<=4&(cooldown.divine_hammer.remains>gcd*2|cooldown.blade_of_justice.remains>gcd*2)&cooldown.judgment.remains>gcd*2))
	{'Zeal', 'talent(2,2)&spell(Zeal).charges=2&{set_bonus(T20)=0&holypower<=2||{holypower<=4&{spell(Divine Hammer).cooldown>gcd*2||spell(Blade of Justice).cooldown>gcd*2}&spell(Judgment).cooldown>gcd*2}}||{set_bonus(T20)>=2&holypower<=1||{holypower<=4&{spell(Divine Hammer).cooldown>gcd*2||spell(Blade of Justice).cooldown>gcd*2}&spell(Judgment).cooldown>gcd*2}}'},
	--actions+=/crusader_strike,if=charges=2&(set_bonus.tier20_2pc=0&holy_power<=2|(holy_power<=4&(cooldown.divine_hammer.remains>gcd*2|cooldown.blade_of_justice.remains>gcd*2)&cooldown.judgment.remains>gcd*2))|(set_bonus.tier20_2pc=1&holy_power<=1|(holy_power<=4&(cooldown.divine_hammer.remains>gcd*2|cooldown.blade_of_justice.remains>gcd*2)&cooldown.judgment.remains>gcd*2))
	{'Crusader Strike', '!talent(2,2)&spell(Crusader Strike).charges=2&{set_bonus(T20)=0&holypower<=2||{holypower<=4&{spell(Divine Hammer).cooldown>gcd*2||spell(Blade of Justice).cooldown>gcd*2}&spell(Judgment).cooldown>gcd*2}}||{set_bonus(T20)>=2&holypower<=1||{holypower<=4&{spell(Divine Hammer).cooldown>gcd*2||spell(Blade of Justice).cooldown>gcd*2}&spell(Judgment).cooldown>gcd*2}}'},
	--actions+=/consecration
	{'Consecration', 'talent(1,3)'},
	--actions+=/divine_storm,if=debuff.judgment.up&spell_targets.divine_storm>=2&buff.divine_purpose.react
	{'Divine Storm', 'target.debuff(Judgment)&player.area(8).enemies>=2&player.buff(Divine Purpose)'},
	--actions+=/divine_storm,if=debuff.judgment.up&spell_targets.divine_storm>=2&buff.the_fires_of_justice.react&(!talent.crusade.enabled|cooldown.crusade.remains>gcd*3)
	{'Divine Storm', 'target.debuff(Judgment)&player.area(8).enemies>=2&player.buff(The Fires of Justice)&{!talent(7,2)||talent(7,2)&!toggle(cooldowns)||spell(Crusade).cooldown>gcd*3}'},
	--actions+=/divine_storm,if=debuff.judgment.up&spell_targets.divine_storm>=2&holy_power>=4&(!talent.crusade.enabled|cooldown.crusade.remains>gcd*4)
	{'Divine Storm', 'target.debuff(Judgment)&player.area(8).enemies>=2&holypower>=4&{!talent(7,2)||talent(7,2)&!toggle(cooldowns)||spell(Crusade).cooldown>gcd*4}'},
	--actions+=/templars_verdict,if=debuff.judgment.up&buff.divine_purpose.react
	{'Templar\'s Verdict', 'target.debuff(Judgment)&player.buff(Divine Purpose)'},
	--actions+=/templars_verdict,if=debuff.judgment.up&buff.the_fires_of_justice.react&(!talent.crusade.enabled|cooldown.crusade.remains>gcd*3)
	{'Templar\'s Verdict', 'target.debuff(Judgment)&player.buff(The Fires of Justice)&{!talent(7,2)||talent(7,2)&!toggle(cooldowns)||spell(Crusade).cooldown>gcd*3}'},
	--actions+=/templars_verdict,if=debuff.judgment.up&holy_power>=4&(!talent.crusade.enabled|cooldown.crusade.remains>gcd*4)&(!talent.execution_sentence.enabled|cooldown.execution_sentence.remains>gcd*2)
	{'Templar\'s Verdict', 'target.debuff(Judgment)&holypower>=4&{!talent(7,2)||talent(7,2)&!toggle(cooldowns)||spell(Crusade).cooldown>gcd*4}&{!talent(1,2)||spell(Execution Sentence).cooldown>gcd*2}'},
	--actions+=/zeal,if=holy_power<=4
	{'Zeal', 'talent(2,2)&holypower<=4'},
	--actions+=/crusader_strike,if=holy_power<=4
	{'Crusader Strike', '!talent(2,2)&holypower<=4'},
	--actions+=/divine_storm,if=debuff.judgment.up&holy_power>=3&spell_targets.divine_storm>=2&(!talent.crusade.enabled|cooldown.crusade.remains>gcd*5)
	{'Divine Storm', 'target.debuff(Judgment)&holypower>=3&player.area(8).enemies>=2&{!talent(7,2)||talent(7,2)&!toggle(cooldowns)||spell(Crusade).cooldown>gcd*5}'},
	--actions+=/templars_verdict,if=debuff.judgment.up&holy_power>=3&(!talent.crusade.enabled|cooldown.crusade.remains>gcd*5)
	{'Templar\'s Verdict', 'target.debuff(Judgment)&holypower>=3&{!talent(7,2)||talent(7,2)&!toggle(cooldowns)||spell(Crusade).cooldown>gcd*5}'},
}

local inCombat = {
	{Dispel, '{!moving||moving}&toggle(yuCT)&spell(Cleanse Toxins).cooldown=0'},
	{Survival, '{!moving||moving}'},
	{Blessings, '{!moving||moving}'},
	{Player, '!moving&{!ingroup||ingroup}'},
	{Emergency, '!moving&ingroup&toggle(yuEGA)'},
	{Interrupts, '{!moving||moving}&toggle(interrupts)&target.interruptAt(70)&target.infront'},
	{Cooldowns, '{!moving||moving}&toggle(cooldowns)'},
	{Combat, '{!moving||moving}&target.infront&target.range<=8'},
}

local outCombat = {
	{Dispel, '{!moving||moving}&toggle(yuCT)&spell(Cleanse Toxins).cooldown=0'},
	{Interrupts, '{!moving||moving}&toggle(interrupts)&target.interruptAt(70)&target.infront'},
	{Blessings, '{!moving||moving}'},
	{Emergency, '!moving&ingroup&toggle(yuEGA)'},
	{'Flash of Light', '!moving&player.health<=70', 'player'},
}

NeP.CR:Add(70, {
	name = '|r[|cff00fff0Yumad|r] |cfff58cbaPaladin|r - |cfff58cbaRETRIBUTION|r',
	ic = inCombat,
	ooc = outCombat,
	gui = GUI,
	load = exeOnLoad
})


import time

import game
import instances
import palette
import registry

from entities import entity
from entities.creatures import player
from statuses import hastestatus

class TwitchChatManager(entity.Entity):
    special_viewers = []

    def __init__(self):
        super().__init__(' ')
        self.hidden = True
        self.time_since_last_help_message = 0
        TwitchChatManager.special_viewers = [viewer.lower() for viewer in game.Game.config['TWITCH']['SpecialViewers'].split('\n') if viewer]

    def get_config_color(self, key):
        return tuple([int(d) for d in game.Game.config['TWITCH'][key].strip(' ').split(',')])

    def handle_events(self, event):
        current_scene = instances.scene_root

        if event.type == 'TWITCHCHATMESSAGE':
            if event.message:
                if event.message.upper() == '!JOIN':
                    player_names = [e.name for e in current_scene.children if hasattr(e, 'name')]

                    bonus = None

                    if not event.nickname in player_names:
                        # Set player color
                        if 'broadcaster' in event.tags['badges']:
                            try:
                                player_color = self.get_config_color('BroadcasterColor')

                            except:
                                player_color = palette.get_nearest((255, 163, 0))

                        elif event.tags['subscriber'] != '0':
                            try:
                                player_color = self.get_config_color('SubscriberColor')

                            except:
                                player_color = palette.BRIGHT_BLUE

                            bonus = registry.Registry.get('weapon')()

                        elif event.nickname.lower() in TwitchChatManager.special_viewers:
                            try:
                                player_color = self.get_config_color('SpecialViewerColor')

                            except:
                                player_color = palette.BRIGHT_RED

                        else:
                            try:
                                player_color = self.get_config_color('ViewerColor')

                            except:
                                player_color = palette.get_nearest((255, 163, 0))

                        # Add player
                        pos = current_scene.get_location_near_stairs()
                        p = player.Player(event.nickname[0], pos, fg=player_color)
                        p.name = event.nickname

                        if bonus:
                            p.equip_weapon(bonus)

                        current_scene.append(p)
                        instances.console.print('{} has joined!'.format(p.display_string))

                elif event.message.upper() == '!LEAVE':
                    for e in current_scene.children:
                        if not e.isinstance('Player'):
                            continue

                        if e.name == event.nickname:
                            e.die()
                            instances.console.print('{} has left.'.format(e.display_string))

                elif event.message.upper().startswith('!CHEER'):
                    s = event.message.split(' ')
                    if len(s) <= 1:
                        return

                    player_names = [p.name for p in instances.scene_root.players if p.state != 'PlayerExitedState']
                    if event.nickname in player_names:
                        return

                    player_name = s[1].lower()
                    if player_name[0] == '@':
                        player_name = player_name[1:]

                    target_player = [p for p in instances.scene_root.players if p.state != 'PlayerExitedState' and p.name == player_name]
                    target_player = target_player[0] if target_player else None
                    if target_player:
                        target_player.add_status(hastestatus.HasteStatus(target_player))

                elif event.message.upper() == '!HELP':
                    current_time = time.time()
                    if current_time - self.time_since_last_help_message > 30:
                        help_message = 'Available commands: !join !leave !move [uldr] !move @username !stop !attack [uldr] !throw [uldr] !drop !cheer @username'
                        instances.game.observer.send_message(help_message, instances.game.channel)
                        self.time_since_last_help_message = current_time


namespace {

using google_breakpad::Minidump;
using google_breakpad::MinidumpContext;
using google_breakpad::MinidumpException;
using google_breakpad::MinidumpMemoryInfo;
using google_breakpad::MinidumpMemoryInfoList;
using google_breakpad::MinidumpMemoryList;
using google_breakpad::MinidumpMemoryRegion;
using google_breakpad::MinidumpModule;
using google_breakpad::MinidumpModuleList;
using google_breakpad::MinidumpSystemInfo;
using google_breakpad::MinidumpThread;
using google_breakpad::MinidumpThreadList;
using google_breakpad::SynthMinidump::Context;
using google_breakpad::SynthMinidump::Dump;
using google_breakpad::SynthMinidump::Exception;
using google_breakpad::SynthMinidump::Memory;
using google_breakpad::SynthMinidump::Module;
using google_breakpad::SynthMinidump::Stream;
using google_breakpad::SynthMinidump::String;
using google_breakpad::SynthMinidump::SystemInfo;
using google_breakpad::SynthMinidump::Thread;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using std::ifstream;
using std::istringstream;
using std::vector;
using ::testing::Return;

class MinidumpTest : public ::testing::Test {
public:
  void SetUp() {
    minidump_file_ = string(getenv("srcdir") ? getenv("srcdir") : ".") +
      "/src/processor/testdata/minidump2.dmp";
  }
  string minidump_file_;
};

TEST_F(MinidumpTest, TestMinidumpFromFile) {
  Minidump minidump(minidump_file_);
  ASSERT_EQ(minidump.path(), minidump_file_);
  ASSERT_TRUE(minidump.Read());
  const MDRawHeader* header = minidump.header();
  ASSERT_NE(header, (MDRawHeader*)NULL);
  ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE));
  //TODO: add more checks here
}

TEST_F(MinidumpTest, TestMinidumpFromStream) {
  // read minidump contents into memory, construct a stringstream around them
  ifstream file_stream(minidump_file_.c_str(), std::ios::in);
  ASSERT_TRUE(file_stream.good());
  vector<char> bytes;
  file_stream.seekg(0, std::ios_base::end);
  ASSERT_TRUE(file_stream.good());
  bytes.resize(file_stream.tellg());
  file_stream.seekg(0, std::ios_base::beg);
  ASSERT_TRUE(file_stream.good());
  file_stream.read(&bytes[0], bytes.size());
  ASSERT_TRUE(file_stream.good());
  string str(&bytes[0], bytes.size());
  istringstream stream(str);
  ASSERT_TRUE(stream.good());

  // now read minidump from stringstream
  Minidump minidump(stream);
  ASSERT_EQ(minidump.path(), "");
  ASSERT_TRUE(minidump.Read());
  const MDRawHeader* header = minidump.header();
  ASSERT_NE(header, (MDRawHeader*)NULL);
  ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE));
  //TODO: add more checks here
}

TEST(Dump, ReadBackEmpty) {
  Dump dump(0);
  dump.Finish();
  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));
  istringstream stream(contents);
  Minidump minidump(stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
}

TEST(Dump, ReadBackEmptyBigEndian) {
  Dump big_minidump(0, kBigEndian);
  big_minidump.Finish();
  string contents;
  ASSERT_TRUE(big_minidump.GetContents(&contents));
  istringstream stream(contents);
  Minidump minidump(stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
}

TEST(Dump, OneStream) {
  Dump dump(0, kBigEndian);
  Stream stream(dump, 0xfbb7fa2bU);
  stream.Append("stream contents");
  dump.Add(&stream);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));
  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());

  const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
  ASSERT_TRUE(dir != NULL);
  EXPECT_EQ(0xfbb7fa2bU, dir->stream_type);

  u_int32_t stream_length;
  ASSERT_TRUE(minidump.SeekToStreamType(0xfbb7fa2bU, &stream_length));
  ASSERT_EQ(15U, stream_length);
  char stream_contents[15];
  ASSERT_TRUE(minidump.ReadBytes(stream_contents, sizeof(stream_contents)));
  EXPECT_EQ(string("stream contents"),
            string(stream_contents, sizeof(stream_contents)));

  EXPECT_FALSE(minidump.GetThreadList());
  EXPECT_FALSE(minidump.GetModuleList());
  EXPECT_FALSE(minidump.GetMemoryList());
  EXPECT_FALSE(minidump.GetException());
  EXPECT_FALSE(minidump.GetAssertion());
  EXPECT_FALSE(minidump.GetSystemInfo());
  EXPECT_FALSE(minidump.GetMiscInfo());
  EXPECT_FALSE(minidump.GetBreakpadInfo());
}

TEST(Dump, OneMemory) {
  Dump dump(0, kBigEndian);
  Memory memory(dump, 0x309d68010bd21b2cULL);
  memory.Append("memory contents");
  dump.Add(&memory);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));
  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());

  const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
  ASSERT_TRUE(dir != NULL);
  EXPECT_EQ((u_int32_t) MD_MEMORY_LIST_STREAM, dir->stream_type);

  MinidumpMemoryList *memory_list = minidump.GetMemoryList();
  ASSERT_TRUE(memory_list != NULL);
  ASSERT_EQ(1U, memory_list->region_count());

  MinidumpMemoryRegion *region1 = memory_list->GetMemoryRegionAtIndex(0);
  ASSERT_EQ(0x309d68010bd21b2cULL, region1->GetBase());
  ASSERT_EQ(15U, region1->GetSize());
  const u_int8_t *region1_bytes = region1->GetMemory();
  ASSERT_TRUE(memcmp("memory contents", region1_bytes, 15) == 0);
}

// One thread --- and its requisite entourage.
TEST(Dump, OneThread) {
  Dump dump(0, kLittleEndian);
  Memory stack(dump, 0x2326a0fa);
  stack.Append("stack for thread");

  MDRawContextX86 raw_context;
  const u_int32_t kExpectedEIP = 0x6913f540;
  raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
  raw_context.edi = 0x3ecba80d;
  raw_context.esi = 0x382583b9;
  raw_context.ebx = 0x7fccc03f;
  raw_context.edx = 0xf62f8ec2;
  raw_context.ecx = 0x46a6a6a8;
  raw_context.eax = 0x6a5025e2;
  raw_context.ebp = 0xd9fabb4a;
  raw_context.eip = kExpectedEIP;
  raw_context.cs = 0xbffe6eda;
  raw_context.eflags = 0xb2ce1e2d;
  raw_context.esp = 0x659caaa4;
  raw_context.ss = 0x2e951ef7;
  Context context(dump, raw_context);

  Thread thread(dump, 0xa898f11b, stack, context,
                0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);

  dump.Add(&stack);
  dump.Add(&context);
  dump.Add(&thread);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));

  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());

  MinidumpMemoryList *md_memory_list = minidump.GetMemoryList();
  ASSERT_TRUE(md_memory_list != NULL);
  ASSERT_EQ(1U, md_memory_list->region_count());

  MinidumpMemoryRegion *md_region = md_memory_list->GetMemoryRegionAtIndex(0);
  ASSERT_EQ(0x2326a0faU, md_region->GetBase());
  ASSERT_EQ(16U, md_region->GetSize());
  const u_int8_t *region_bytes = md_region->GetMemory();
  ASSERT_TRUE(memcmp("stack for thread", region_bytes, 16) == 0);

  MinidumpThreadList *thread_list = minidump.GetThreadList();
  ASSERT_TRUE(thread_list != NULL);
  ASSERT_EQ(1U, thread_list->thread_count());

  MinidumpThread *md_thread = thread_list->GetThreadAtIndex(0);
  ASSERT_TRUE(md_thread != NULL);
  u_int32_t thread_id;
  ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
  ASSERT_EQ(0xa898f11bU, thread_id);
  MinidumpMemoryRegion *md_stack = md_thread->GetMemory();
  ASSERT_TRUE(md_stack != NULL);
  ASSERT_EQ(0x2326a0faU, md_stack->GetBase());
  ASSERT_EQ(16U, md_stack->GetSize());
  const u_int8_t *md_stack_bytes = md_stack->GetMemory();
  ASSERT_TRUE(memcmp("stack for thread", md_stack_bytes, 16) == 0);

  MinidumpContext *md_context = md_thread->GetContext();
  ASSERT_TRUE(md_context != NULL);
  ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU());

  u_int64_t eip;
  ASSERT_TRUE(md_context->GetInstructionPointer(&eip));
  EXPECT_EQ(kExpectedEIP, eip);

  const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
  ASSERT_TRUE(md_raw_context != NULL);
  ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
            (md_raw_context->context_flags
             & (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
  EXPECT_EQ(0x3ecba80dU, raw_context.edi);
  EXPECT_EQ(0x382583b9U, raw_context.esi);
  EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
  EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
  EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
  EXPECT_EQ(0x6a5025e2U, raw_context.eax);
  EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
  EXPECT_EQ(kExpectedEIP, raw_context.eip);
  EXPECT_EQ(0xbffe6edaU, raw_context.cs);
  EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
  EXPECT_EQ(0x659caaa4U, raw_context.esp);
  EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}

TEST(Dump, ThreadMissingMemory) {
  Dump dump(0, kLittleEndian);
  Memory stack(dump, 0x2326a0fa);
  // Stack has no contents.

  MDRawContextX86 raw_context;
  memset(&raw_context, 0, sizeof(raw_context));
  raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
  Context context(dump, raw_context);

  Thread thread(dump, 0xa898f11b, stack, context,
                0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);

  dump.Add(&stack);
  dump.Add(&context);
  dump.Add(&thread);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));

  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());

  // This should succeed even though the thread has no stack memory.
  MinidumpThreadList* thread_list = minidump.GetThreadList();
  ASSERT_TRUE(thread_list != NULL);
  ASSERT_EQ(1U, thread_list->thread_count());

  MinidumpThread* md_thread = thread_list->GetThreadAtIndex(0);
  ASSERT_TRUE(md_thread != NULL);

  u_int32_t thread_id;
  ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
  ASSERT_EQ(0xa898f11bU, thread_id);

  MinidumpContext* md_context = md_thread->GetContext();
  ASSERT_NE(reinterpret_cast<MinidumpContext*>(NULL), md_context);

  MinidumpMemoryRegion* md_stack = md_thread->GetMemory();
  ASSERT_EQ(reinterpret_cast<MinidumpMemoryRegion*>(NULL), md_stack);
}

TEST(Dump, ThreadMissingContext) {
  Dump dump(0, kLittleEndian);
  Memory stack(dump, 0x2326a0fa);
  stack.Append("stack for thread");

  // Context is empty.
  Context context(dump);

  Thread thread(dump, 0xa898f11b, stack, context,
                0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);

  dump.Add(&stack);
  dump.Add(&context);
  dump.Add(&thread);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));

  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());

  // This should succeed even though the thread has no stack memory.
  MinidumpThreadList* thread_list = minidump.GetThreadList();
  ASSERT_TRUE(thread_list != NULL);
  ASSERT_EQ(1U, thread_list->thread_count());

  MinidumpThread* md_thread = thread_list->GetThreadAtIndex(0);
  ASSERT_TRUE(md_thread != NULL);

  u_int32_t thread_id;
  ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
  ASSERT_EQ(0xa898f11bU, thread_id);
  MinidumpMemoryRegion* md_stack = md_thread->GetMemory();
  ASSERT_NE(reinterpret_cast<MinidumpMemoryRegion*>(NULL), md_stack);

  MinidumpContext* md_context = md_thread->GetContext();
  ASSERT_EQ(reinterpret_cast<MinidumpContext*>(NULL), md_context);
}

TEST(Dump, OneModule) {
  static const MDVSFixedFileInfo fixed_file_info = {
    0xb2fba33a,                           // signature
    0x33d7a728,                           // struct_version
    0x31afcb20,                           // file_version_hi
    0xe51cdab1,                           // file_version_lo
    0xd1ea6907,                           // product_version_hi
    0x03032857,                           // product_version_lo
    0x11bf71d7,                           // file_flags_mask
    0x5fb8cdbf,                           // file_flags
    0xe45d0d5d,                           // file_os
    0x107d9562,                           // file_type
    0x5a8844d4,                           // file_subtype
    0xa8d30b20,                           // file_date_hi
    0x651c3e4e                            // file_date_lo
  };

  Dump dump(0, kBigEndian);
  String module_name(dump, "single module");
  Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
                module_name,
                0xb1054d2a,
                0x34571371,
                fixed_file_info, // from synth_minidump_unittest_data.h
                NULL, NULL);

  dump.Add(&module);
  dump.Add(&module_name);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));
  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());

  const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
  ASSERT_TRUE(dir != NULL);
  EXPECT_EQ((u_int32_t) MD_MODULE_LIST_STREAM, dir->stream_type);

  MinidumpModuleList *md_module_list = minidump.GetModuleList();
  ASSERT_TRUE(md_module_list != NULL);
  ASSERT_EQ(1U, md_module_list->module_count());

  const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
  ASSERT_TRUE(md_module != NULL);
  ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address());
  ASSERT_EQ(0xada542bd, md_module->size());
  ASSERT_EQ("single module", md_module->code_file());

  const MDRawModule *md_raw_module = md_module->module();
  ASSERT_TRUE(md_raw_module != NULL);
  ASSERT_EQ(0xb1054d2aU, md_raw_module->time_date_stamp);
  ASSERT_EQ(0x34571371U, md_raw_module->checksum);
  ASSERT_TRUE(memcmp(&md_raw_module->version_info, &fixed_file_info,
                     sizeof(fixed_file_info)) == 0);
}

TEST(Dump, OneSystemInfo) {
  Dump dump(0, kLittleEndian);
  String csd_version(dump, "Petulant Pierogi");
  SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);

  dump.Add(&system_info);
  dump.Add(&csd_version);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));
  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());

  const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
  ASSERT_TRUE(dir != NULL);
  EXPECT_EQ((u_int32_t) MD_SYSTEM_INFO_STREAM, dir->stream_type);

  MinidumpSystemInfo *md_system_info = minidump.GetSystemInfo();
  ASSERT_TRUE(md_system_info != NULL);
  ASSERT_EQ("windows", md_system_info->GetOS());
  ASSERT_EQ("x86", md_system_info->GetCPU());
  ASSERT_EQ("Petulant Pierogi", *md_system_info->GetCSDVersion());
  ASSERT_EQ("GenuineIntel", *md_system_info->GetCPUVendor());
}

TEST(Dump, BigDump) {
  Dump dump(0, kLittleEndian);

  // A SystemInfo stream.
  String csd_version(dump, "Munificent Macaque");
  SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
  dump.Add(&csd_version);
  dump.Add(&system_info);

  // Five threads!
  Memory stack0(dump, 0x70b9ebfc);
  stack0.Append("stack for thread zero");
  MDRawContextX86 raw_context0;
  raw_context0.context_flags = MD_CONTEXT_X86_INTEGER;
  raw_context0.eip = 0xaf0709e4;
  Context context0(dump, raw_context0);
  Thread thread0(dump, 0xbbef4432, stack0, context0,
                 0xd0377e7b, 0xdb8eb0cf, 0xd73bc314, 0x09d357bac7f9a163ULL);
  dump.Add(&stack0);
  dump.Add(&context0);
  dump.Add(&thread0);

  Memory stack1(dump, 0xf988cc45);
  stack1.Append("stack for thread one");
  MDRawContextX86 raw_context1;
  raw_context1.context_flags = MD_CONTEXT_X86_INTEGER;
  raw_context1.eip = 0xe4f56f81;
  Context context1(dump, raw_context1);
  Thread thread1(dump, 0x657c3f58, stack1, context1,
                 0xa68fa182, 0x6f3cf8dd, 0xe3a78ccf, 0x78cc84775e4534bbULL);
  dump.Add(&stack1);
  dump.Add(&context1);
  dump.Add(&thread1);

  Memory stack2(dump, 0xc8a92e7c);
  stack2.Append("stack for thread two");
  MDRawContextX86 raw_context2;
  raw_context2.context_flags = MD_CONTEXT_X86_INTEGER;
  raw_context2.eip = 0xb336a438;
  Context context2(dump, raw_context2);
  Thread thread2(dump, 0xdf4b8a71, stack2, context2,
                 0x674c26b6, 0x445d7120, 0x7e700c56, 0xd89bf778e7793e17ULL);
  dump.Add(&stack2);
  dump.Add(&context2);
  dump.Add(&thread2);

  Memory stack3(dump, 0x36d08e08);
  stack3.Append("stack for thread three");
  MDRawContextX86 raw_context3;
  raw_context3.context_flags = MD_CONTEXT_X86_INTEGER;
  raw_context3.eip = 0xdf99a60c;
  Context context3(dump, raw_context3);
  Thread thread3(dump, 0x86e6c341, stack3, context3,
                 0x32dc5c55, 0x17a2aba8, 0xe0cc75e7, 0xa46393994dae83aeULL);
  dump.Add(&stack3);
  dump.Add(&context3);
  dump.Add(&thread3);

  Memory stack4(dump, 0x1e0ab4fa);
  stack4.Append("stack for thread four");
  MDRawContextX86 raw_context4;
  raw_context4.context_flags = MD_CONTEXT_X86_INTEGER;
  raw_context4.eip = 0xaa646267;
  Context context4(dump, raw_context4);
  Thread thread4(dump, 0x261a28d4, stack4, context4,
                 0x6ebd389e, 0xa0cd4759, 0x30168846, 0x164f650a0cf39d35ULL);
  dump.Add(&stack4);
  dump.Add(&context4);
  dump.Add(&thread4);

  // Three modules!
  String module1_name(dump, "module one");
  Module module1(dump, 0xeb77da57b5d4cbdaULL, 0x83cd5a37, module1_name);
  dump.Add(&module1_name);
  dump.Add(&module1);

  String module2_name(dump, "module two");
  Module module2(dump, 0x8675884adfe5ac90ULL, 0xb11e4ea3, module2_name);
  dump.Add(&module2_name);
  dump.Add(&module2);

  String module3_name(dump, "module three");
  Module module3(dump, 0x95fc1544da321b6cULL, 0x7c2bf081, module3_name);
  dump.Add(&module3_name);
  dump.Add(&module3);

  // Add one more memory region, on top of the five stacks.
  Memory memory5(dump, 0x61979e828040e564ULL);
  memory5.Append("contents of memory 5");
  dump.Add(&memory5);

  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));
  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(4U, minidump.GetDirectoryEntryCount());

  // Check the threads.
  MinidumpThreadList *thread_list = minidump.GetThreadList();
  ASSERT_TRUE(thread_list != NULL);
  ASSERT_EQ(5U, thread_list->thread_count());
  u_int32_t thread_id;
  ASSERT_TRUE(thread_list->GetThreadAtIndex(0)->GetThreadID(&thread_id));
  ASSERT_EQ(0xbbef4432U, thread_id);
  ASSERT_EQ(0x70b9ebfcU,
            thread_list->GetThreadAtIndex(0)->GetMemory()->GetBase());
  ASSERT_EQ(0xaf0709e4U,
            thread_list->GetThreadAtIndex(0)->GetContext()->GetContextX86()
            ->eip);

  ASSERT_TRUE(thread_list->GetThreadAtIndex(1)->GetThreadID(&thread_id));
  ASSERT_EQ(0x657c3f58U, thread_id);
  ASSERT_EQ(0xf988cc45U,
            thread_list->GetThreadAtIndex(1)->GetMemory()->GetBase());
  ASSERT_EQ(0xe4f56f81U,
            thread_list->GetThreadAtIndex(1)->GetContext()->GetContextX86()
            ->eip);

  ASSERT_TRUE(thread_list->GetThreadAtIndex(2)->GetThreadID(&thread_id));
  ASSERT_EQ(0xdf4b8a71U, thread_id);
  ASSERT_EQ(0xc8a92e7cU,
            thread_list->GetThreadAtIndex(2)->GetMemory()->GetBase());
  ASSERT_EQ(0xb336a438U,
            thread_list->GetThreadAtIndex(2)->GetContext()->GetContextX86()
            ->eip);

  ASSERT_TRUE(thread_list->GetThreadAtIndex(3)->GetThreadID(&thread_id));
  ASSERT_EQ(0x86e6c341U, thread_id);
  ASSERT_EQ(0x36d08e08U,
            thread_list->GetThreadAtIndex(3)->GetMemory()->GetBase());
  ASSERT_EQ(0xdf99a60cU,
            thread_list->GetThreadAtIndex(3)->GetContext()->GetContextX86()
            ->eip);

  ASSERT_TRUE(thread_list->GetThreadAtIndex(4)->GetThreadID(&thread_id));
  ASSERT_EQ(0x261a28d4U, thread_id);
  ASSERT_EQ(0x1e0ab4faU,
            thread_list->GetThreadAtIndex(4)->GetMemory()->GetBase());
  ASSERT_EQ(0xaa646267U,
            thread_list->GetThreadAtIndex(4)->GetContext()->GetContextX86()
            ->eip);

  // Check the modules.
  MinidumpModuleList *md_module_list = minidump.GetModuleList();
  ASSERT_TRUE(md_module_list != NULL);
  ASSERT_EQ(3U, md_module_list->module_count());
  EXPECT_EQ(0xeb77da57b5d4cbdaULL,
            md_module_list->GetModuleAtIndex(0)->base_address());
  EXPECT_EQ(0x8675884adfe5ac90ULL,
            md_module_list->GetModuleAtIndex(1)->base_address());
  EXPECT_EQ(0x95fc1544da321b6cULL,
            md_module_list->GetModuleAtIndex(2)->base_address());
}

TEST(Dump, OneMemoryInfo) {
  Dump dump(0, kBigEndian);
  Stream stream(dump, MD_MEMORY_INFO_LIST_STREAM);

  // Add the MDRawMemoryInfoList header.
  const u_int64_t kNumberOfEntries = 1;
  stream.D32(sizeof(MDRawMemoryInfoList))  // size_of_header
        .D32(sizeof(MDRawMemoryInfo))      // size_of_entry
        .D64(kNumberOfEntries);            // number_of_entries

  // Now add a MDRawMemoryInfo entry.
  const u_int64_t kBaseAddress = 0x1000;
  const u_int64_t kRegionSize = 0x2000;
  stream.D64(kBaseAddress)                         // base_address
        .D64(kBaseAddress)                         // allocation_base
        .D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE)  // allocation_protection
        .D32(0)                                    // __alignment1
        .D64(kRegionSize)                          // region_size
        .D32(MD_MEMORY_STATE_COMMIT)               // state
        .D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE)  // protection
        .D32(MD_MEMORY_TYPE_PRIVATE)               // type
        .D32(0);                                   // __alignment2

  dump.Add(&stream);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));
  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());

  const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
  ASSERT_TRUE(dir != NULL);
  EXPECT_EQ((u_int32_t) MD_MEMORY_INFO_LIST_STREAM, dir->stream_type);

  MinidumpMemoryInfoList *info_list = minidump.GetMemoryInfoList();
  ASSERT_TRUE(info_list != NULL);
  ASSERT_EQ(1U, info_list->info_count());

  const MinidumpMemoryInfo *info1 = info_list->GetMemoryInfoAtIndex(0);
  ASSERT_EQ(kBaseAddress, info1->GetBase());
  ASSERT_EQ(kRegionSize, info1->GetSize());
  ASSERT_TRUE(info1->IsExecutable());
  ASSERT_TRUE(info1->IsWritable());

  // Should get back the same memory region here.
  const MinidumpMemoryInfo *info2 =
      info_list->GetMemoryInfoForAddress(kBaseAddress + kRegionSize / 2);
  ASSERT_EQ(kBaseAddress, info2->GetBase());
  ASSERT_EQ(kRegionSize, info2->GetSize());
}

TEST(Dump, OneExceptionX86) {
  Dump dump(0, kLittleEndian);

  MDRawContextX86 raw_context;
  raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
  raw_context.edi = 0x3ecba80d;
  raw_context.esi = 0x382583b9;
  raw_context.ebx = 0x7fccc03f;
  raw_context.edx = 0xf62f8ec2;
  raw_context.ecx = 0x46a6a6a8;
  raw_context.eax = 0x6a5025e2;
  raw_context.ebp = 0xd9fabb4a;
  raw_context.eip = 0x6913f540;
  raw_context.cs = 0xbffe6eda;
  raw_context.eflags = 0xb2ce1e2d;
  raw_context.esp = 0x659caaa4;
  raw_context.ss = 0x2e951ef7;
  Context context(dump, raw_context);

  Exception exception(dump, context,
                      0x1234abcd, // thread id
                      0xdcba4321, // exception code
                      0xf0e0d0c0, // exception flags
                      0x0919a9b9c9d9e9f9ULL); // exception address

  dump.Add(&context);
  dump.Add(&exception);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));

  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());

  MinidumpException *md_exception = minidump.GetException();
  ASSERT_TRUE(md_exception != NULL);

  u_int32_t thread_id;
  ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
  ASSERT_EQ(0x1234abcdU, thread_id);

  const MDRawExceptionStream* raw_exception = md_exception->exception();
  ASSERT_TRUE(raw_exception != NULL);
  EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
  EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
  EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
            raw_exception->exception_record.exception_address);

  MinidumpContext *md_context = md_exception->GetContext();
  ASSERT_TRUE(md_context != NULL);
  ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
  const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
  ASSERT_TRUE(md_raw_context != NULL);
  ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
            (md_raw_context->context_flags
             & (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
  EXPECT_EQ(0x3ecba80dU, raw_context.edi);
  EXPECT_EQ(0x382583b9U, raw_context.esi);
  EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
  EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
  EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
  EXPECT_EQ(0x6a5025e2U, raw_context.eax);
  EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
  EXPECT_EQ(0x6913f540U, raw_context.eip);
  EXPECT_EQ(0xbffe6edaU, raw_context.cs);
  EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
  EXPECT_EQ(0x659caaa4U, raw_context.esp);
  EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}

TEST(Dump, OneExceptionX86XState) {
  Dump dump(0, kLittleEndian);

  MDRawContextX86 raw_context;
  raw_context.context_flags = MD_CONTEXT_X86_INTEGER |
    MD_CONTEXT_X86_CONTROL | MD_CONTEXT_X86_XSTATE;
  raw_context.edi = 0x3ecba80d;
  raw_context.esi = 0x382583b9;
  raw_context.ebx = 0x7fccc03f;
  raw_context.edx = 0xf62f8ec2;
  raw_context.ecx = 0x46a6a6a8;
  raw_context.eax = 0x6a5025e2;
  raw_context.ebp = 0xd9fabb4a;
  raw_context.eip = 0x6913f540;
  raw_context.cs = 0xbffe6eda;
  raw_context.eflags = 0xb2ce1e2d;
  raw_context.esp = 0x659caaa4;
  raw_context.ss = 0x2e951ef7;
  Context context(dump, raw_context);

  Exception exception(dump, context,
                      0x1234abcd, // thread id
                      0xdcba4321, // exception code
                      0xf0e0d0c0, // exception flags
                      0x0919a9b9c9d9e9f9ULL); // exception address

  dump.Add(&context);
  dump.Add(&exception);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));

  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());

  MinidumpException *md_exception = minidump.GetException();
  ASSERT_TRUE(md_exception != NULL);

  u_int32_t thread_id;
  ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
  ASSERT_EQ(0x1234abcdU, thread_id);

  const MDRawExceptionStream* raw_exception = md_exception->exception();
  ASSERT_TRUE(raw_exception != NULL);
  EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
  EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
  EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
            raw_exception->exception_record.exception_address);

  MinidumpContext *md_context = md_exception->GetContext();
  ASSERT_TRUE(md_context != NULL);
  ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
  const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
  ASSERT_TRUE(md_raw_context != NULL);
  ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
            (md_raw_context->context_flags
             & (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
  EXPECT_EQ(0x3ecba80dU, raw_context.edi);
  EXPECT_EQ(0x382583b9U, raw_context.esi);
  EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
  EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
  EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
  EXPECT_EQ(0x6a5025e2U, raw_context.eax);
  EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
  EXPECT_EQ(0x6913f540U, raw_context.eip);
  EXPECT_EQ(0xbffe6edaU, raw_context.cs);
  EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
  EXPECT_EQ(0x659caaa4U, raw_context.esp);
  EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}

TEST(Dump, OneExceptionARM) {
  Dump dump(0, kLittleEndian);

  MDRawContextARM raw_context;
  raw_context.context_flags = MD_CONTEXT_ARM_INTEGER;
  raw_context.iregs[0] = 0x3ecba80d;
  raw_context.iregs[1] = 0x382583b9;
  raw_context.iregs[2] = 0x7fccc03f;
  raw_context.iregs[3] = 0xf62f8ec2;
  raw_context.iregs[4] = 0x46a6a6a8;
  raw_context.iregs[5] = 0x6a5025e2;
  raw_context.iregs[6] = 0xd9fabb4a;
  raw_context.iregs[7] = 0x6913f540;
  raw_context.iregs[8] = 0xbffe6eda;
  raw_context.iregs[9] = 0xb2ce1e2d;
  raw_context.iregs[10] = 0x659caaa4;
  raw_context.iregs[11] = 0xf0e0d0c0;
  raw_context.iregs[12] = 0xa9b8c7d6;
  raw_context.iregs[13] = 0x12345678;
  raw_context.iregs[14] = 0xabcd1234;
  raw_context.iregs[15] = 0x10203040;
  raw_context.cpsr = 0x2e951ef7;
  Context context(dump, raw_context);

  Exception exception(dump, context,
                      0x1234abcd, // thread id
                      0xdcba4321, // exception code
                      0xf0e0d0c0, // exception flags
                      0x0919a9b9c9d9e9f9ULL); // exception address

  dump.Add(&context);
  dump.Add(&exception);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));

  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());

  MinidumpException *md_exception = minidump.GetException();
  ASSERT_TRUE(md_exception != NULL);

  u_int32_t thread_id;
  ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
  ASSERT_EQ(0x1234abcdU, thread_id);

  const MDRawExceptionStream* raw_exception = md_exception->exception();
  ASSERT_TRUE(raw_exception != NULL);
  EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
  EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
  EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
            raw_exception->exception_record.exception_address);

  MinidumpContext *md_context = md_exception->GetContext();
  ASSERT_TRUE(md_context != NULL);
  ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM, md_context->GetContextCPU());
  const MDRawContextARM *md_raw_context = md_context->GetContextARM();
  ASSERT_TRUE(md_raw_context != NULL);
  ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM_INTEGER,
            (md_raw_context->context_flags
             & MD_CONTEXT_ARM_INTEGER));
  EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]);
  EXPECT_EQ(0x382583b9U, raw_context.iregs[1]);
  EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]);
  EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]);
  EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]);
  EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]);
  EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]);
  EXPECT_EQ(0x6913f540U, raw_context.iregs[7]);
  EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]);
  EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]);
  EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]);
  EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]);
  EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]);
  EXPECT_EQ(0x12345678U, raw_context.iregs[13]);
  EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]);
  EXPECT_EQ(0x10203040U, raw_context.iregs[15]);
  EXPECT_EQ(0x2e951ef7U, raw_context.cpsr);
}

TEST(Dump, OneExceptionARMOldFlags) {
  Dump dump(0, kLittleEndian);

  MDRawContextARM raw_context;
  // MD_CONTEXT_ARM_INTEGER, but with _OLD
  raw_context.context_flags = MD_CONTEXT_ARM_OLD | 0x00000002;
  raw_context.iregs[0] = 0x3ecba80d;
  raw_context.iregs[1] = 0x382583b9;
  raw_context.iregs[2] = 0x7fccc03f;
  raw_context.iregs[3] = 0xf62f8ec2;
  raw_context.iregs[4] = 0x46a6a6a8;
  raw_context.iregs[5] = 0x6a5025e2;
  raw_context.iregs[6] = 0xd9fabb4a;
  raw_context.iregs[7] = 0x6913f540;
  raw_context.iregs[8] = 0xbffe6eda;
  raw_context.iregs[9] = 0xb2ce1e2d;
  raw_context.iregs[10] = 0x659caaa4;
  raw_context.iregs[11] = 0xf0e0d0c0;
  raw_context.iregs[12] = 0xa9b8c7d6;
  raw_context.iregs[13] = 0x12345678;
  raw_context.iregs[14] = 0xabcd1234;
  raw_context.iregs[15] = 0x10203040;
  raw_context.cpsr = 0x2e951ef7;
  Context context(dump, raw_context);

  Exception exception(dump, context,
                      0x1234abcd, // thread id
                      0xdcba4321, // exception code
                      0xf0e0d0c0, // exception flags
                      0x0919a9b9c9d9e9f9ULL); // exception address

  dump.Add(&context);
  dump.Add(&exception);
  dump.Finish();

  string contents;
  ASSERT_TRUE(dump.GetContents(&contents));

  istringstream minidump_stream(contents);
  Minidump minidump(minidump_stream);
  ASSERT_TRUE(minidump.Read());
  ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());

  MinidumpException *md_exception = minidump.GetException();
  ASSERT_TRUE(md_exception != NULL);

  u_int32_t thread_id;
  ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
  ASSERT_EQ(0x1234abcdU, thread_id);

  const MDRawExceptionStream* raw_exception = md_exception->exception();
  ASSERT_TRUE(raw_exception != NULL);
  EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
  EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
  EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
            raw_exception->exception_record.exception_address);

  MinidumpContext *md_context = md_exception->GetContext();
  ASSERT_TRUE(md_context != NULL);
  ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM, md_context->GetContextCPU());
  const MDRawContextARM *md_raw_context = md_context->GetContextARM();
  ASSERT_TRUE(md_raw_context != NULL);
  ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM_INTEGER,
            (md_raw_context->context_flags
             & MD_CONTEXT_ARM_INTEGER));
  EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]);
  EXPECT_EQ(0x382583b9U, raw_context.iregs[1]);
  EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]);
  EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]);
  EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]);
  EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]);
  EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]);
  EXPECT_EQ(0x6913f540U, raw_context.iregs[7]);
  EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]);
  EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]);
  EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]);
  EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]);
  EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]);
  EXPECT_EQ(0x12345678U, raw_context.iregs[13]);
  EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]);
  EXPECT_EQ(0x10203040U, raw_context.iregs[15]);
  EXPECT_EQ(0x2e951ef7U, raw_context.cpsr);
}

}  // namespace


//====================================================================================================================//
// File:          qcan_server_memeory.hpp                                                                             //
// Description:   QCAN classes - CAN server                                                                           //
//                                                                                //
// Copyright (C) MicroControl GmbH & Co. KG                                                                           //
// 53844 Troisdorf - Germany                                                                                //
// www.microcontrol.net                                                                                //
//                                                                                //
//--------------------------------------------------------------------------------------------------------------------//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the   //
// following conditions are met:                                                                                //
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions, the following   //
//    disclaimer and the referenced file 'LICENSE'.                                                                   //
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       //
//    following disclaimer in the documentation and/or other materials provided with the distribution.                //
// 3. Neither the name of MicroControl nor the names of its contributors may be used to endorse or promote products   //
//    derived from this software without specific prior written permission.                                           //
//                                                                                //
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance     //
// with the License. You may obtain a copy of the License at                                                          //
//                                                                                //
//    http://www.apache.org/licenses/LICENSE-2.0                                                                      //
//                                                                                //
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed   //
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for  //
// the specific language governing permissions and limitations under the License.                                     //
//                                                                                //
//====================================================================================================================//

/*--------------------------------------------------------------------------------------------------------------------*\
** Include files                                                                                **
**                                                                                **
\*--------------------------------------------------------------------------------------------------------------------*/

#include "qcan_defs.hpp"

/*--------------------------------------------------------------------------------------------------------------------*\
** Definitions                                                                                **
**                                                                                **
\*--------------------------------------------------------------------------------------------------------------------*/

#define  QCAN_MEMORY_KEY         "QCAN_SERVER_SHARED_KEY"

/*--------------------------------------------------------------------------------------------------------------------*\
** Structures                                                                                **
**                                                                                **
\*--------------------------------------------------------------------------------------------------------------------*/

typedef struct Server_s {
   uint32_t ulVersionMajor;
   uint32_t ulVersionMinor;
} Server_ts;

typedef struct Network_s {
   uint32_t ulVersionMajor;
   uint32_t ulVersionMinor;
} Network_ts;

typedef struct ServerSettings_s {
   Server_ts   tsServer;
   Network_ts  atsNetwork[QCAN_NETWORK_MAX];
} ServerSettings_ts;


[react-babylonjs](../README.md) / [Exports](../modules.md) / loaders

# Module: loaders

## Table of contents

### Enumerations

- [LoaderStatus](../enums/loaders.loaderstatus.md)
- [TaskType](../enums/loaders.tasktype.md)

### Classes

- [LoadedModel](../classes/loaders.loadedmodel.md)

### Interfaces

- [ILoadedModel](../interfaces/loaders.iloadedmodel.md)

### Type aliases

- [AssetManagerContextProviderProps](loaders.md#assetmanagercontextproviderprops)
- [AssetManagerContextType](loaders.md#assetmanagercontexttype)
- [AssetManagerOptions](loaders.md#assetmanageroptions)
- [AssetManagerProgressType](loaders.md#assetmanagerprogresstype)
- [BinaryTask](loaders.md#binarytask)
- [MeshTask](loaders.md#meshtask)
- [SceneLoaderContextProviderProps](loaders.md#sceneloadercontextproviderprops)
- [SceneLoaderContextType](loaders.md#sceneloadercontexttype)
- [SceneLoaderOptions](loaders.md#sceneloaderoptions)
- [Task](loaders.md#task)
- [TextureTask](loaders.md#texturetask)

### Variables

- [AssetManagerContext](loaders.md#assetmanagercontext)
- [AssetManagerContextProvider](loaders.md#assetmanagercontextprovider)
- [SceneLoaderContext](loaders.md#sceneloadercontext)
- [SceneLoaderContextProvider](loaders.md#sceneloadercontextprovider)

### Functions

- [useAssetManager](loaders.md#useassetmanager)
- [useSceneLoader](loaders.md#usesceneloader)

## Type aliases

### AssetManagerContextProviderProps

Ƭ **AssetManagerContextProviderProps**: { `children`: React.ReactNode ; `startProgress?`: [*AssetManagerProgressType*](loaders_useassetmanager.md#assetmanagerprogresstype)  }

#### Type declaration:

Name | Type |
------ | ------ |
`children` | React.ReactNode |
`startProgress?` | [*AssetManagerProgressType*](loaders_useassetmanager.md#assetmanagerprogresstype) |

Defined in: [loaders/useAssetManager.tsx:48](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useAssetManager.tsx#L48)

___

### AssetManagerContextType

Ƭ **AssetManagerContextType**: { `lastProgress?`: [*AssetManagerProgressType*](loaders_useassetmanager.md#assetmanagerprogresstype) ; `updateProgress`: (`progress`: [*AssetManagerProgressType*](loaders_useassetmanager.md#assetmanagerprogresstype)) => *void*  } \| *undefined*

Defined in: [loaders/useAssetManager.tsx:36](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useAssetManager.tsx#L36)

___

### AssetManagerOptions

Ƭ **AssetManagerOptions**: { `reportProgress?`: *boolean* ; `scene?`: Scene ; `useDefaultLoadingScreen?`: *boolean*  }

#### Type declaration:

Name | Type |
------ | ------ |
`reportProgress?` | *boolean* |
`scene?` | Scene |
`useDefaultLoadingScreen?` | *boolean* |

Defined in: [loaders/useAssetManager.tsx:61](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useAssetManager.tsx#L61)

___

### AssetManagerProgressType

Ƭ **AssetManagerProgressType**: { `eventData`: IAssetsProgressEvent ; `eventState`: EventState  } \| *undefined*

Defined in: [loaders/useAssetManager.tsx:43](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useAssetManager.tsx#L43)

___

### BinaryTask

Ƭ **BinaryTask**: { `name`: *string* ; `taskType`: [*Binary*](../enums/loaders/useassetmanager.tasktype.md#binary) ; `url`: *string*  }

#### Type declaration:

Name | Type |
------ | ------ |
`name` | *string* |
`taskType` | [*Binary*](../enums/loaders/useassetmanager.tasktype.md#binary) |
`url` | *string* |

Defined in: [loaders/useAssetManager.tsx:11](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useAssetManager.tsx#L11)

___

### MeshTask

Ƭ **MeshTask**: { `meshesNames?`: *any* ; `name`: *string* ; `rootUrl`: *string* ; `sceneFilename`: *string* ; `taskType`: [*Mesh*](../enums/loaders/useassetmanager.tasktype.md#mesh)  }

#### Type declaration:

Name | Type |
------ | ------ |
`meshesNames?` | *any* |
`name` | *string* |
`rootUrl` | *string* |
`sceneFilename` | *string* |
`taskType` | [*Mesh*](../enums/loaders/useassetmanager.tasktype.md#mesh) |

Defined in: [loaders/useAssetManager.tsx:17](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useAssetManager.tsx#L17)

___

### SceneLoaderContextProviderProps

Ƭ **SceneLoaderContextProviderProps**: { `children`: React.ReactNode ; `startProgress?`: ISceneLoaderProgressEvent  }

#### Type declaration:

Name | Type |
------ | ------ |
`children` | React.ReactNode |
`startProgress?` | ISceneLoaderProgressEvent |

Defined in: [loaders/useSceneLoader.tsx:14](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useSceneLoader.tsx#L14)

___

### SceneLoaderContextType

Ƭ **SceneLoaderContextType**: { `lastProgress?`: *Nullable*<ISceneLoaderProgressEvent\> ; `updateProgress`: (`progress`: ISceneLoaderProgressEvent) => *void*  } \| *undefined*

Defined in: [loaders/useSceneLoader.tsx:7](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useSceneLoader.tsx#L7)

___

### SceneLoaderOptions

Ƭ **SceneLoaderOptions**: { `alwaysSelectAsActiveMesh?`: *boolean* ; `onModelLoaded?`: (`loadedModel`: [*ILoadedModel*](../interfaces/loaders/loadedmodel.iloadedmodel.md)) => *void* ; `receiveShadows?`: *boolean* ; `reportProgress?`: *boolean* ; `scaleToDimension?`: *number* ; `scene?`: Scene  }

#### Type declaration:

Name | Type | Description |
------ | ------ | ------ |
`alwaysSelectAsActiveMesh?` | *boolean* | Always select root mesh as active. Defaults to false.   |
`onModelLoaded?` | (`loadedModel`: [*ILoadedModel*](../interfaces/loaders/loadedmodel.iloadedmodel.md)) => *void* | Access to loaded model as soon as it is loaded, so it provides a way to hide or scale the meshes before the first render.   |
`receiveShadows?` | *boolean* | set that all meshes receive shadows. Defaults to false.   |
`reportProgress?` | *boolean* | SceneLoader progress events are set on context provider (when available). Defaults to false.   |
`scaleToDimension?` | *number* | Scale entire model within these square bounds Defaults to no scaling.   |
`scene?` | Scene | - |

Defined in: [loaders/useSceneLoader.tsx:27](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useSceneLoader.tsx#L27)

___

### Task

Ƭ **Task**: [*BinaryTask*](loaders_useassetmanager.md#binarytask) \| [*MeshTask*](loaders_useassetmanager.md#meshtask) \| [*TextureTask*](loaders_useassetmanager.md#texturetask)

Defined in: [loaders/useAssetManager.tsx:34](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useAssetManager.tsx#L34)

___

### TextureTask

Ƭ **TextureTask**: { `invertY?`: *boolean* ; `name`: *string* ; `noMipmap?`: *boolean* ; `samplingMode?`: *number* ; `taskType`: [*Texture*](../enums/loaders/useassetmanager.tasktype.md#texture) ; `url`: *string*  }

#### Type declaration:

Name | Type |
------ | ------ |
`invertY?` | *boolean* |
`name` | *string* |
`noMipmap?` | *boolean* |
`samplingMode?` | *number* |
`taskType` | [*Texture*](../enums/loaders/useassetmanager.tasktype.md#texture) |
`url` | *string* |

Defined in: [loaders/useAssetManager.tsx:25](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useAssetManager.tsx#L25)

## Variables

### AssetManagerContext

• `Const` **AssetManagerContext**: *Context*<[*AssetManagerContextType*](loaders_useassetmanager.md#assetmanagercontexttype)\>

Defined in: [loaders/useAssetManager.tsx:41](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useAssetManager.tsx#L41)

___

### AssetManagerContextProvider

• `Const` **AssetManagerContextProvider**: *React.FC*<[*AssetManagerContextProviderProps*](loaders_useassetmanager.md#assetmanagercontextproviderprops)\>

Defined in: [loaders/useAssetManager.tsx:53](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useAssetManager.tsx#L53)

___

### SceneLoaderContext

• `Const` **SceneLoaderContext**: *Context*<[*SceneLoaderContextType*](loaders_usesceneloader.md#sceneloadercontexttype)\>

Defined in: [loaders/useSceneLoader.tsx:12](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useSceneLoader.tsx#L12)

___

### SceneLoaderContextProvider

• `Const` **SceneLoaderContextProvider**: *React.FC*<[*SceneLoaderContextProviderProps*](loaders_usesceneloader.md#sceneloadercontextproviderprops)\>

Defined in: [loaders/useSceneLoader.tsx:19](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useSceneLoader.tsx#L19)

## Functions

### useAssetManager

▸ `Const`**useAssetManager**(`tasks`: [*Task*](loaders_useassetmanager.md#task)[], `options?`: [*AssetManagerOptions*](loaders_useassetmanager.md#assetmanageroptions)): AssetManagerResult

#### Parameters:

Name | Type |
------ | ------ |
`tasks` | [*Task*](loaders_useassetmanager.md#task)[] |
`options?` | [*AssetManagerOptions*](loaders_useassetmanager.md#assetmanageroptions) |

**Returns:** AssetManagerResult

Defined in: [loaders/useAssetManager.tsx:249](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useAssetManager.tsx#L249)

___

### useSceneLoader

▸ `Const`**useSceneLoader**(`rootUrl`: *string*, `sceneFilename`: *string*, `pluginExtension?`: *string*, `options?`: [*SceneLoaderOptions*](loaders_usesceneloader.md#sceneloaderoptions)): [*LoadedModel*](../classes/loaders/loadedmodel.loadedmodel.md)

#### Parameters:

Name | Type |
------ | ------ |
`rootUrl` | *string* |
`sceneFilename` | *string* |
`pluginExtension?` | *string* |
`options?` | [*SceneLoaderOptions*](loaders_usesceneloader.md#sceneloaderoptions) |

**Returns:** [*LoadedModel*](../classes/loaders/loadedmodel.loadedmodel.md)

Defined in: [loaders/useSceneLoader.tsx:202](https://github.com/brianzinn/react-babylonjs/blob/eba7b00/src/hooks/loaders/useSceneLoader.tsx#L202)


function SendChatMessage(...)
end;

function SendAddonMessage(...)
end;

function GetDefaultLanguage()
	return "Common";
end;


package com.intellij.java.codeInsight.daemon;

import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.redundantCast.RedundantCastInspection;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.lang.java.JavaDocumentationProvider;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.psi.*;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
import com.intellij.util.ArrayUtilRt;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

public class LightAdvHighlightingFixtureTest extends LightJavaCodeInsightFixtureTestCase {

  @Override
  protected String getBasePath() {
    return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/advFixture";
  }

  public void testHidingOnDemandImports() {
    //noinspection StaticNonFinalField
    myFixture.addClass("package foo; public class Foo {" +
                       "  public static String foo;" +
                       "}");
    myFixture.addClass("package foo; public class Bar {" +
                       "  public static void foo(String s) {}" +
                       "}");
    myFixture.configureByFile(getTestName(false) + ".java");
    myFixture.checkHighlighting(false, false, false);
  }

  public void testFilteredCandidates() {
    PsiFile file = myFixture.configureByText("a.java", "class a {{new StringBuilder().ap<caret>pend();}}");
    PsiCallExpression callExpression =
      PsiTreeUtil.getParentOfType(file.findElementAt(myFixture.getEditor().getCaretModel().getOffset()), PsiCallExpression.class);
    assertNotNull(callExpression);
    CandidateInfo[] candidates =
      PsiResolveHelper.getInstance(myFixture.getProject()).getReferencedMethodCandidates(callExpression, false);
    assertSize(27, candidates);
    String generateDoc = new JavaDocumentationProvider().generateDoc(callExpression, callExpression);
    assertEquals("Candidates for method call new StringBuilder().append() are:

  " +
                 "StringBuilder append(Object)
  " +
                 "StringBuilder append(String)
  " +
                 "StringBuilder append(StringBuilder)
  " +
                 "StringBuilder append(StringBuffer)
  " +
                 "StringBuilder append(CharSequence)
  " +
                 "StringBuilder append(CharSequence, int, int)
  " +
                 "StringBuilder append(char[])
  " +
                 "StringBuilder append(char[], int, int)
  " +
                 "StringBuilder append(boolean)
  " +
                 "StringBuilder append(char)
  " +
                 "StringBuilder append(int)
  " +
                 "StringBuilder append(long)
  " +
                 "StringBuilder append(float)
  " +
                 "StringBuilder append(double)
", generateDoc);
  }

  public void testPackageNamedAsClassInDefaultPackage() {
    myFixture.addClass("package test; public class A {}");
    PsiClass aClass = myFixture.addClass("public class test {}");
    doTest();
    assertNull(ProgressManager.getInstance().runProcess(() -> ReferencesSearch.search(aClass).findFirst(), new EmptyProgressIndicator()));
  }

  public void testPackageNameAsClassFQName() {
    myFixture.addClass("package foo.Bar; class A {}");
    myFixture.addClass("package foo; public class Bar { public static class Inner {}}");
    doTest();
  }

  public void testInaccessibleFunctionalTypeParameter() {
    myFixture.addClass("package test; class A {}");
    myFixture.addClass("package test; public interface I { void m(A a);}");
    myFixture.addClass("package test; public interface J { A m();}");
    doTest();
  }

  public void testBoundsPromotionWithCapturedWildcards() {
    myFixture.addClass("package a; public interface Provider {}");
    myFixture.addClass("package b; public interface Provider {}");
    doTest();
  }

  public void testStaticImportCompoundWithInheritance() {
    myFixture.addClass("package a; public interface A { static void foo(Object o){} static void foo(String str) {}}");
    doTest();
  }

  public void testSuppressedInGenerated() {
    myFixture.enableInspections(new RedundantCastInspection());
    myFixture.addClass("package javax.annotation; public @interface Generated {}");
    doTest();
  }

  public void testReferenceThroughInheritance() {
    myFixture.addClass("package test;\n" +
                       "public class A {\n" +
                       "  public static class B {}\n" +
                       "}");
    doTest();
  }

  public void testReferenceThroughInheritance1() {
    //noinspection UnnecessaryInterfaceModifier
    myFixture.addClass("package me;\n" +
                       "import me.Serializer.Format;\n" +
                       "public interface Serializer<F extends Format> {\n" +
                       "    public static interface Format {}\n" +
                       "}\n");
    doTest();
  }

  public void testUsageOfProtectedAnnotationOutsideAPackage() {
    myFixture.addClass("package a;\n" +
                       "import java.lang.annotation.ElementType;\n" +
                       "import java.lang.annotation.Target;\n" +
                       "\n" +
                       "public class A {\n" +
                       "    @Target( { ElementType.METHOD, ElementType.TYPE } )\n" +
                       "    protected @interface Test{\n" +
                       "    }\n" +
                       "}");
    doTest();
  }

  public void testPackageLocalClassUsedInArrayTypeOutsidePackage() {
    myFixture.addClass("package a; class A {}");
    myFixture.addClass("package a; public class B {public static A[] getAs() {return null;}}");
    doTest();
  }

  public void testProtectedFieldUsedInAnnotationParameterOfInheritor() {
    myFixture.addClass("package a; public class A {protected final static String A_FOO = \"A\";}");
    doTest();
  }

  public void testStaticImportClassConflictingWithPackageName() {
    myFixture.addClass("package p.P1; class Unrelated {}");
    myFixture.addClass("package p; public class P1 {public static final int FOO = 1;}");
    doTest();
  }

  public void testAmbiguousMethodCallWhenStaticImported() {
    myFixture.addClass("package p;" +
                       "class A<K> {\n" +
                       "  static <T> A<T> of(T t) {\n" +
                       "    return null;\n" +
                       "  }\n" +
                       "}\n" +
                       "class B<K> {\n" +
                       "  static <T> B<T> of(T t) {\n" +
                       "    return null;\n" +
                       "  }\n" +
                       "  static <T> B<T> of(T... t) {\n" +
                       "    return null;\n" +
                       "  }\n" +
                       "}\n");
    doTest();
  }

  public void testClassPackageConflict() {
    myFixture.addClass("package a; public class b {}");
    myFixture.addClass("package c; public class a {}");
    doTest();
  }

  public void testClassPackageConflict1() {
    myFixture.addClass("package a; public class b {}");
    myFixture.addClass("package c.d; public class a {}");
    doTest();
  }

  public void testTypeAnnotations() {
    myFixture.addClass("import java.lang.annotation.ElementType;\n" +
                       "import java.lang.annotation.Target;\n" +
                       "@Target({ElementType.TYPE_USE})\n" +
                       "@interface Nullable {}\n");
    myFixture.addClass("class Middle<R> extends Base<@Nullable R, String>{}");
    myFixture.addClass("class Child<R> extends Middle<R>{}");
    PsiClass baseClass = myFixture.addClass("class Base<R, C> {}");
    PsiClass fooClass = myFixture.addClass("class Foo {\n" +
                                           "  Child<String> field;\n" +
                                           "}");
    PsiField fooField = fooClass.findFieldByName("field", false);
    PsiType substituted =
      TypeConversionUtil.getSuperClassSubstitutor(baseClass, (PsiClassType)fooField.getType()).substitute(baseClass.getTypeParameters()[0]);
    assertEquals(1, substituted.getAnnotations().length);
  }

  public void testCodeFragmentMayAccessDefaultPackage() {
    myFixture.addClass("public class MainClass { }");

    Project project = getProject();
    PsiElement context = JavaPsiFacade.getInstance(project).findPackage("");
    JavaCodeFragment fragment = JavaCodeFragmentFactory.getInstance(project).createReferenceCodeFragment("MainClass", context, true, true);
    Document document = PsiDocumentManager.getInstance(project).getDocument(fragment);
    Editor editor = EditorFactory.getInstance().createViewer(document, project);
    Disposer.register(myFixture.getTestRootDisposable(), () -> EditorFactory.getInstance().releaseEditor(editor));

    List<HighlightInfo> highlights = CodeInsightTestFixtureImpl.instantiateAndRun(fragment, editor, ArrayUtilRt.EMPTY_INT_ARRAY, false);
    List<HighlightInfo> problems = DaemonAnalyzerTestCase.filter(highlights, HighlightSeverity.WARNING);
    assertThat(problems).isEmpty();
  }

  public void testImplicitConstructorAccessibility() {
    myFixture.addClass("package a; public class Base {" +
                       "private Base() {}\n" +
                       "protected Base(int... i) {}\n" +
                       "}");
    doTest();
  }

  public void testDiamondsWithAnonymousProtectedConstructor() {
    myFixture.addClass("package a; public class Base<T> { protected Base() {}}");
    doTest();
  }

  public void testDiamondsWithProtectedCallInConstruction() {
    myFixture.addClass("package a; public class Base { " +
                       " protected String createString() {return null;}" +
                       "}");
    doTest();
  }

  public void testDefaultAnnotationsApplicability() {
    myFixture.addClass("package foo; public @interface A {}");
    myFixture.configureByFile("module-info.java");
    myFixture.checkHighlighting();
  }

  public void testAlwaysFalseForLoop() {
    doTest();
    IntentionAction action = myFixture.findSingleIntention("Remove 'for' statement");
    myFixture.launchAction(action);
    myFixture.checkResultByFile(getTestName(false) + "_after.java");
  }

  public void testProtectedInnerClass() {
    myFixture.addClass("package a;\n" +
                       "public class Outer {\n" +
                       " public Object get(Inner key) {\n" +
                       "  return null;\n" +
                       " }\n" +
                       " public Inner get1() {return null;} \n" +
                       " public Inner f; \n" +
                       " protected class Inner {}\n" +
                       "}");
    doTest();
  }

  public void testProtectedInnerClass1() {
    myFixture.addClass("package a;\n" +
                       "public class A<T> {\n" +
                       "  public T getData() {return null;}\n" +
                       "}");
    myFixture.addClass("package a;\n" +
                       "public class Outer extends A<Outer.Inner> {\n" +
                       " protected class Inner {}\n" +
                       "}");
    doTest();
  }

  private void doTest() {
    myFixture.configureByFile(getTestName(false) + ".java");
    myFixture.checkHighlighting();
  }
}