"""
Copyright (C) 2008  Matthew and Joey Marshall <joey@arcticpaint.com>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from __future__ import division
import sys, random, os
from gamelib import data
from gamelib.ui import UI
from gamelib.world import Map
from gamelib.menu import Menu


import rabbyt
import pyglet
from pyglet.window import Window, MouseCursor
from pyglet.clock import Clock
import snowui

from pyglet.media import Player, StaticSource, load
from itertools import cycle 

import time

pyglet.options['audio'] = ('openal', 'alsa', 'silent')

class MyCursor(MouseCursor):
    def __init__(self, name):
        self.sprite = rabbyt.Sprite(os.path.join("cursors", name+".png"))
        self.sprite.shape.top = 0
        self.sprite.shape.left = 0

    def draw(self, x, y):
        self.sprite.xy = (x,y)
        self.sprite.render()

cursors = {
    "default": MyCursor("default"),
    "connect": MyCursor("connect"),
    "build": MyCursor("build")
}

class Mixer(Player):
    def __init__(self, tracks):
        super(Mixer, self).__init__()

        random.shuffle(tracks)
        self.iter = cycle(tracks)
        self.queue_next()
        self.queue_next()

    def queue_next(self):
        self.queue(load(self.iter.next()))

    def on_music_skip(self):
        self.next()
        self.queue_next()

    def on_eos(self):
        self.queue_next()
player = Mixer(['data/music/Laid Back Swing.mp3', 'data/music/One For Luck.mp3'])
player.volume = .8

WAVES = pyglet.media.load('data/sounds/beach-waves.mp3', streaming=False)


class Profiler(object):
    def __init__(self, sections, enabled=False):
        self.enabled = enabled
        self.sections = list(sections)
        self.history = dict(((s, []) for s in self.sections))
        self.history_count = 0
        self.current = dict()
        self.times = {}

    def start_frame(self):
        self.times = {}

    def start(self, section):
        self.times[section] = time.time()
        if not section in self.sections:
            self.sections.append(section)
        return True

    def end(self, section):
        self.current[section] = self.current.get(section, 0) + \
                time.time() - self.times[section]
        return True

    def end_frame(self):
        self.history_count += 1
        for key, value in self.current.items():
            self.history.setdefault(key, []).append(value)
        self.current = {}
        if self.history_count >= 30:
            if self.enabled:
                self.report()
            self.history = dict(((s, []) for s in self.sections))
            self.history_count = 0


    def report(self):
        for s in self.sections:
            if not self.history.get(s, None):
                continue
            average = sum(self.history[s])/len(self.history[s]) 
            if average > 0.0001:
                print s+(" "*(20-len(s))), average
            else:
                print s
        print
        print len(pyglet.clock._default._schedule_items), len(pyglet.clock._default._schedule_interval_items)
        print

profiler = Profiler(("music", "sound", "clock", "world-clock", "step", "render", "world_render"),True)


class PositionalSoundManager(object):
    def __init__(self, view):
        self.view = view
        self.sound_counts = {}
        pyglet.clock.schedule_interval(self.dec_sound_counts, 1/.6)
        self.sources = {
                "splash":pyglet.media.load("data/sounds/splash.mp3", streaming=False),
                "squeak1":pyglet.media.load("data/sounds/squeak1.wav", streaming=False),
                "squeak2":pyglet.media.load("data/sounds/squeak2.wav", streaming=False),
                "squeak3":pyglet.media.load("data/sounds/squeak3.wav", streaming=False),
                "squeak4":pyglet.media.load("data/sounds/squeak4.wav", streaming=False),
                "connection":pyglet.media.load("data/sounds/connection.wav", streaming=False),
                }
        self.default_volumes = {
                "splash":.7}
        self.players = []

    def dec_sound_counts(self, dt):
        for key in self.sound_counts:
            self.sound_counts[key] = max(self.sound_counts[key] - 2, 0)

    def get_player(self):
        # Pyglet seems to leak positional players, so we recycle them.
        # (This also provides a hard limit to the number of sounds that will
        # be playing at a time.)
        max_players = 50
        if len(self.players) > max_players:
            p = self.players.pop(0)
            self.players.append(p)
            return p
        else:
            p = pyglet.media.Player()
            p.eos_action = pyglet.media.Player.EOS_PAUSE
            self.players.append(p)
            return p

    def dispatch_events(self):
        for p in self.players:
            p.dispatch_events()
    def play(self, source, xy, on_screen_only=True, random_pitch=0):
        assert profiler.start("sound")
        try:
            x,y = xy
            l,t,r,b = self.view.bounds
            in_view = (l<x<r and b<y<t)

            if on_screen_only and not in_view:
                return None

            count = self.sound_counts.get(source, 0)
            if count > 7 or (count > 3 and not in_view):
                return None
            self.sound_counts[source] = count + 1

            p = self.get_player()
            if not p:
                return None
            p.volume = self.default_volumes.get(source, 1.0)
            p.position = (xy[0], xy[1], 0)
            p.min_distance=self.view.height
            p.pitch = 1.0 + random_pitch*random.random() - random_pitch/2
            if p.source:
                p.queue(self.sources[source])
                p.next()
                p.play()
            else:
                p.queue(self.sources[source])
                p.play()

            if on_screen_only and not in_view:
                p.volume *= .5
        finally:
            assert profiler.end("sound")

        return p


def run_game(window, map):
    time = 0

    pyglet.clock.set_fps_limit(60)

    game_clock = Clock(time_function=lambda:time)
    rabbyt.set_time(time)

    m = Map(map, game_clock)

    view = snowui.View(window)

    profiler.enabled = False

    m.world.sound_manager = PositionalSoundManager(view)

    ui = UI(m, view, window)
    window.push_handlers(ui)
    ui.setup()
    ui.cursors = cursors

    w = pyglet.media.Player()
    w.queue(WAVES)
    w.volume = .6
    w.eos_action = pyglet.media.Player.EOS_LOOP
    w.play()

    player.volume = .8


    while True:
        p = profiler
        p.start_frame()

        pyglet.media.listener.position = (view.x, view.y, -500)
        w.position = pyglet.media.listener.position
        player.position = pyglet.media.listener.position

        p.start("music")
        if Menu.mute and player.playing:
            player.pause()
        elif not Menu.mute and not player.playing:
            player.play()

        w.dispatch_events()
        player.dispatch_events()
        p.end("music")

        if window.has_exit:
            sys.exit(0)

        p.start("sound")
        m.world.sound_manager.dispatch_events()
        p.end("sound")

        p.start("clock")
        dt = pyglet.clock.tick()
        p.end("clock")
        for i in range(int(ui.game_speed)):
            time += dt
            p.start("world-clock")
            m.world.clock.tick(poll=True)
            p.end("world-clock")
            p.start("step")
            m.world.step()
            p.end("step")


        rabbyt.set_time(time)
        p.start("render")
        window.dispatch_events()
        rabbyt.clear((.63,.79,1))
        ui.scroll_screen(dt)
        view.set_viewport()
        p.start("world_render")
        m.world.render()
        p.end("world_render")
        ui.render()
        window.flip()
        p.end("render")

        p.end_frame()

        if ui.quit_game:
            return


def do_menu(window):
    ui = Menu(window)
    window.push_handlers(ui)
    window.set_mouse_cursor(cursors["default"])
    ui.setup()
    ui.cursors = cursors

    clock = Clock()
    clock.set_fps_limit(60)
    time = 0
    rabbyt.set_time(time)

    ui.clock = clock
    ui.selected_plane = ui.main_plane

    while True:
        if Menu.mute and player.playing:
            player.pause()
        elif not Menu.mute and not player.playing:
            player.play()
        player.dispatch_events()
        if window.has_exit:
            sys.exit(0)

        time += clock.tick()
        window.dispatch_events()
        rabbyt.clear((0,0,0))
        rabbyt.set_time(time)
        ui.render()
        window.flip()

        if ui.do_play:
            ui.do_play = False
            run_game(window, ui.selected_map)
            return


def main(window):

    player.play()
    player.pause()

    # Preload all data
    for f in os.listdir("data"):
        if f[-4:].endswith(".png"):
            data.load_texture(f)

    window.set_mouse_cursor(cursors["default"])


    while not window.has_exit:
        do_menu(window)
