from collections import OrderedDict
import json
import os

from siege import game
from siege.graphic import VideoMode
from siege.io import DataStream, Keyboard
from siege.log import Log
from siege.network import Message, NetworkManager
from siege.util import Vector


class SettingsSystem(object):
    # Use singleton instance so that settings are not reloaded everytime we register systems
    _instance = None
    NAME = "settings"
    SETTINGS_PATH = "settings"
    commonResolutions = [
        (1024, 600),
        (1024, 768),
        (1280, 800),
        (1280, 1024),
        (1360, 768),
        (1366, 768),
        (1440, 900),
        (1680, 1050),
        (1920, 1080),
        (2560, 1440)
    ]

    def getName(self):
        return self.NAME

    def __init__(self):
        # Populate resolutions
        self.resolutions = {}
        desktop = VideoMode.getDesktopMode()
        modes = VideoMode.getFullscreenModes()
        for mode in modes:
            if mode.bitsPerPixel == desktop.bitsPerPixel and (mode.width, mode.height) in self.commonResolutions:
                self.resolutions[self.getResolutionText(mode.width, mode.height)] = mode
        self.fullscreen = False
        self.showTutorial = True
        # Mapping between dialog name and position
        self.dialogs = {}
        self.controls = {
            'left': (Keyboard.A, Keyboard.Left),
            'right': (Keyboard.D, Keyboard.Right),
            'down': (Keyboard.S, Keyboard.Down),
            'jump': (Keyboard.Space,),
            'craft': (Keyboard.C,),
            'talent': (Keyboard.T,),
            'equipment': (Keyboard.E,),
            'research': (Keyboard.R,),
            'inventory': (Keyboard.I,),
            'options': (Keyboard.Escape,),
        }
        if not NetworkManager.isStandalone():
            self._load()

    def apply(self, resolution, fullscreen, masterVolume, musicVolume, sfxVolume):
        if self._resolutionChanged(resolution, fullscreen) and resolution in self.resolutions:
            mode = self.resolutions[resolution]
            game.render.setMode(mode, fullscreen)
            if game.network.getClient():
                packet = DataStream(Message.RESOLUTION_CHANGED)
                packet.writePixelVector(game.render.getWindowSize())
                game.network.getClient().send(packet)
        game.audio.masterVolume = masterVolume
        game.audio.musicVolume = musicVolume
        game.audio.sfxVolume = sfxVolume
        self.fullscreen = fullscreen

    def _resolutionChanged(self, resolutionText, fullscreen):
        return self.getCurrentResolutionText() != resolutionText or self.fullscreen != fullscreen

    def getResolutionText(self, width, height):
        return "{} x {}".format(width, height)

    def getCurrentResolutionText(self):
        resolution = game.render.getWindowSize()
        return self.getResolutionText(resolution.x, resolution.y)

    def getDialogPosition(self, dialogName):
        return self.dialogs[dialogName] if dialogName in self.dialogs else None

    def setDialogPosition(self, dialogName, position):
        self.dialogs[dialogName] = position

    def save(self):
        resolution = game.render.getWindowSize()
        windows = {}
        for dialog, position in self.dialogs.iteritems():
            windows[dialog] = OrderedDict([('x', position.x), ('y', position.y)])

        # Convert controls to serializable format
        def _getKeyName(key):
            for k, v in Keyboard.__dict__.iteritems():
                if v is key:
                    return k
        controls = {}
        for control, keys in self.controls.iteritems():
            controls[control] = [_getKeyName(key) for key in keys]

        settings = OrderedDict([
            ('resolution', OrderedDict([('x', resolution.x), ('y', resolution.y)])),
            ('fullscreen', self.fullscreen),
            ('showTutorial', self.showTutorial),
            ('master_volume', game.audio.masterVolume),
            ('music_volume', game.audio.musicVolume),
            ('sfx_volume', game.audio.sfxVolume),
            ('controls', controls),
            ('windows', windows)
        ])
        with open(self.SETTINGS_PATH, 'w') as f:
            json.dump(settings, f, indent=4, separators=(',', ': '))

    def _load(self):
        if os.path.exists(self.SETTINGS_PATH):
            try:
                with open(self.SETTINGS_PATH, 'r') as f:
                    settings = json.load(f)
                    Log.info("Loading settings!")
                    resolution = settings.get('resolution', Vector(1280, 800))
                    fullscreen = settings.get('fullscreen', False)
                    self.showTutorial = settings.get('showTutorial', True)
                    masterVolume = settings.get('master_volume', 100)
                    musicVolume = settings.get('music_volume', 100)
                    sfxVolume = settings.get('sfx_volume', 100)
                    if 'windows' in settings:
                        for dialog, data in settings['windows'].iteritems():
                            self.dialogs[dialog] = Vector(data['x'], data['y'])
                    if 'controls' in settings:
                        for control, keys in settings['controls'].iteritems():
                            self.controls[control] = [getattr(Keyboard, key) for key in keys if hasattr(Keyboard, key)]
                    self.apply(self.getResolutionText(resolution['x'], resolution['y']), fullscreen, masterVolume, musicVolume, sfxVolume)
            except ValueError:
                Log.info("Failed to read settings file. Deleting it.")
                os.remove(self.SETTINGS_PATH)

    @staticmethod
    def register():
        if not SettingsSystem._instance:
            SettingsSystem._instance = SettingsSystem()
        game.registerSystem(SettingsSystem.NAME, SettingsSystem._instance)
