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

See main.py for full notice.
"""
from __future__ import division
import random, math
import rabbyt

from gamelib.station import Producer, Station, StationPlaceholder, Consumer, Transformer
from gamelib.ships import Dock

import simplejson

class Map(object):
    def __init__(self, mapname, clock):
        self.mapname = mapname

        fp = open("data/maps/%s.json"%mapname)
        self.data = data = simplejson.load(fp)

        self.world = World()
        self.world.clock = clock

        l, t, r, b = 0,0,0,0
        margin = 200
        for d in data["islands"]:
            i = Island(self.world, d["xy"], d["id"])
            l = min(l, i.left - margin)
            t = max(t, i.top + margin)
            r = max(r, i.right + margin)
            b = min(b, i.bottom - margin)
            if "rot" in d:
                i.rot = int(d["rot"])
        self.scroll_bounds = l,t,r,b

        for i in data["station_placeholders"]:
            sp = StationPlaceholder(self.world, i["xy"])

        for i in data["stations"]:
            sp = self.world.station_placeholders[i["placeholder_index"]]
            s = Station(self.world, sp)

            if i["type"] == "producer":
                p = Producer(self.world, s)
                if i.has_key("figures"):
                    p.figures = list(i["figures"])
                if i.has_key("colors"):
                    p.colors = list(i["colors"])
            elif i["type"] == "consumer":
                c = Consumer(self.world, s, match_figure=i["match_figure"],
                        match_color=i["match_color"])
            elif i["type"] == "transformer":
                Transformer(self.world, s, 
                        input_figure=i.get("input_figure", None),
                        output_figure=i.get("output_figure", None),
                        input_color=i.get("input_color", None),
                        output_color=i.get("output_color", None))
            elif i["type"] == "dock":
                d = Dock(self.world, s, 
                        input_figure=i.get("input_figure", None),
                        output_figure=i.get("output_figure", None),
                        input_color=i.get("input_color", None),
                        output_color=i.get("output_color", None))


        for i in data["decors"]:
            self.add_decor(i)


        for i in range(20):
            b = Bird(clock)
            self.world.renderables["birds_flying"].append(b)

    def add_decor(self, decor):
        sizes = {"bird":2, "buoy":8}
        s = rabbyt.Sprite("decors/"+decor["name"]+".png", xy=decor["xy"])
        self.world.decors.append(s)
        self.world.renderables["decors"].append(s)
        if decor["name"][:-1] in "buoy bird".split():
            d = sizes[decor["name"][:-1]]
            s.scale = rabbyt.ease_in(start=.9, end=1, dt=2, extend="reverse")
            for ix in range(2):
                b = rabbyt.Sprite("wake.png",
                        xy=s.attrgetter('xy'), rot=s.attrgetter('rot'))
                b.alpha = rabbyt.ease_out(start=.5, end=1,
                        dt=random.random()*1+.5, extend="reverse")
                b.scale = rabbyt.ease_out(start=1,
                        end=random.randrange(d-1, d+1)/10.0,
                        dt=random.random()*.5+1.5, extend="reverse")
                self.world.renderables["floats"].append(b)


class Layered(object):
    def __init__(self, layers):
        self.layers = layers
        self._dict = dict([(l, []) for l in layers])

    def __getitem__(self, key):
        return self._dict[key]

    def items(self):
        for layer in self.layers:
            yield layer, self._dict[layer]

    def values(self):
        for layer in self.layers:
            yield self._dict[layer]

class World(object):
    def __init__(self):
        self.islands = []
        self.station_placeholders = []
        self.stations = []
        self.producers = []
        self.cables = []
        self.decors = []
        self.filters = []

        self.renderables = Layered(["floats", "islands",  "decors", "ships", "buildings",
            "platforms", "cables", "stations", "sorters", "other", "birds_flying", "hud"])
        self.stepables = []

    def render(self, cull_quad=None):
        if cull_quad is None:
            if hasattr(self, 'ui'):
                cull_quad = rabbyt.Quad(self.ui.view.bounds)
        from gamelib.main import profiler
        cull_layers = ("cables", "ships", "sorters", "platforms")
        for layer, renderables in self.renderables.items():
            profiler.start("render-"+layer)
            if cull_quad and layer in cull_layers:
                renderables = rabbyt.collisions.aabb_collide_single(
                        rabbyt.Quad(self.ui.view.bounds),
                        renderables)
            rabbyt.render_unsorted(renderables)
            profiler.end("render-"+layer)

    def step(self):
        for s in self.stepables:
            s.step()


class Island(rabbyt.Sprite):
    def __init__(self, world, xy, id=None):
        if id is None:
            id = random.choice([1,2,3,4,5])
        rabbyt.Sprite.__init__(self, "island%i.png"%id, xy=xy)

        self.id = id

        self.world = world
        self.world.islands.append(self)
        self.world.renderables["islands"].append(self)

        self.borders = []
        for i in range(3):
            b = rabbyt.Sprite("island%i_border.png"%id,
                    xy=self.attrgetter('xy'), rot=self.attrgetter('rot'))
            b.alpha = rabbyt.ease_out(start=.5, end=1,
                    dt=random.random()*1+.5, extend="reverse")
            b.scale = rabbyt.ease_out(start=1, end=random.random()*.2+1,
                    dt=random.random()*.5+1.5, extend="reverse")
            self.borders.append(b)

        # HACK to get smoke for volcano
        if self.id == 5:
            s = rabbyt.Sprite("smoke.png", x=self.attrgetter('x'),
                y=self.attrgetter('y')+30)
            for i in range(3):
                s.rot = rabbyt.lerp(start=0, end=359, dt=random.choice((7,6)))
                s.alpha = rabbyt.ease_out(start=.2, end=.4,
                        dt=random.random()*1+.7, extend="reverse")
                s.scale = rabbyt.ease_out(start=1, end=random.random()*.2+1,
                        dt=random.random()*.5+1.5, extend="reverse")
                self.world.renderables["islands"].append(s)

    def render(self):
        rabbyt.Sprite.render(self)
        rabbyt.render_unsorted(self.borders)


class Bird(rabbyt.Sprite):
    speed = rabbyt.anim_slot()

    def __init__(self, clock):
        rabbyt.Sprite.__init__(self, "bird_flying.png", xy=(0,0))
        self.clock = clock

        clock.schedule(self.move)

        self.scale = .4
        self.x = random.randint(-100,100)
        self.y = random.randint(-100,100)
        self.rot = random.randint(0,360)

        self.new_path()

    def move(self, dt):
        speed = self.scale * 70
        self.x += math.cos(math.radians(self.rot))*dt*speed
        self.y += math.sin(math.radians(self.rot))*dt*speed

    def new_path(self, *args):
        #TODO: Make them so they won't fly off the edge of the map for very long
        dt = random.choice([self.soar, self.spiral])()
        self.clock.schedule_once(self.new_path, dt)

    def soar(self):
        dt = random.randint(6,20)
        self.rot = rabbyt.ease_in(end=self.rot+random.randint(-10,10), dt=dt)
        self.scale = rabbyt.lerp(end=self.scale+random.randint(1,3)/10.0, dt=dt)
        return dt

    def spiral(self):
        dt = random.randint(9,20)
        if self.scale >= .5:
            self.rot = rabbyt.ease(end=self.rot+random.randint(50,200), dt=dt)
            self.scale = rabbyt.lerp(end=random.randint(3,6)/10.0, dt=dt)
        else:
            self.rot = rabbyt.ease(end=self.rot-random.randint(50,200), dt=dt)
            self.scale = rabbyt.lerp(end=random.randint(6,10)/10.0, dt=dt)
        return dt
