import pygame
import os.path
import util
import random
import xml.dom.minidom
import shipsettings

###########################################################################
# Sundog: Resurrection
# code by Patrick Devine (patrick@cpan.org)
#
# 
#

from pygame.locals import *

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
DIST = 8192

# XXX - this should go in ship.xml
MAXFUEL = 1000.0

class Cockpit:
    def __init__(self, screen, file="ship.xml"):
        '''Initialize the cockpit for any given space ship.'''

        self.screen = screen
        self.parseConfigFile(screen, file)

        self.starfield = StarField(self.screen)

    def parseConfigFile(self, screen, file):
        '''Parse the xml config file to and create objects inside of the
           cockpit.

           NOTE:  This method could use a whole hell of a lot better
                  error checking, but I'm feeling lazy right now.

           See ship.xml for the format of the config file.

        '''

        doc = xml.dom.minidom.parse(file)

        self.gauges = {}
        self.buttons = {}
        self.indicators = {}
        self.statuslights = {}
        self.meters = {}
        self.sliders = {}

        for node in doc.getElementsByTagName("cockpit"):
            image = util.fetchTextFromElementByTagName(node, "image")
            self.width = util.fetchTextFromElementByTagName(node, "width")
            self.height = util.fetchTextFromElementByTagName(node, "height")
            self.focalpoint_height = \
                util.fetchTextFromElementByTagName(node, "focalpoint_height")

            for gauge in node.getElementsByTagName("gauge"):
                name = gauge.getAttribute("name")
                pos = util.fetchTextFromElementByTagName(gauge, "position")
                width = util.fetchTextFromElementByTagName(gauge, "width")
                height = util.fetchTextFromElementByTagName(gauge, "height")
                color = util.fetchTextFromElementByTagName(gauge, "color")

                self.gauges[name] = \
                    Gauge(screen, eval(pos), eval(color), int(height),
                          int(width))

            for button in node.getElementsByTagName("button"):
                name = button.getAttribute("name")
                pos = util.fetchTextFromElementByTagName(button, "position")
                active = util.fetchTextFromElementByTagName(button, "active")
                imageOn = \
                    util.fetchTextFromElementByTagName(button, "image_active")
                imageOff = \
                    util.fetchTextFromElementByTagName(button, "image_inactive")

                self.buttons[name] = \
                    Button(screen, imageOn, imageOff, eval(pos), eval(active))

            for indicator in node.getElementsByTagName("indicator"):
                name = indicator.getAttribute("name")
                text = util.fetchTextFromElementByTagName(indicator, "text")
                pos = util.fetchTextFromElementByTagName(indicator, "position")
                color = util.fetchTextFromElementByTagName(indicator, "color")
                size = util.fetchTextFromElementByTagName(indicator, "size")

                self.indicators[name] = \
                    Indicator(screen, text, eval(pos), eval(color), int(size),
                              False)

            for statuslight in node.getElementsByTagName("statuslight"):
                name = statuslight.getAttribute("name")
                type = util.fetchTextFromElementByTagName(statuslight, "type")
                pos = \
                    util.fetchTextFromElementByTagName(statuslight, "position")

                self.statuslights[name] = \
                    StatusLight(screen, eval(pos), None, type)

            for meter in node.getElementsByTagName("meter"):
                name = meter.getAttribute("name")
                size = util.fetchTextFromElementByTagName(meter, "size")
                pos = util.fetchTextFromElementByTagName(meter, "position")
                type = util.fetchTextFromElementByTagName(meter, "type")

                self.meters[name] = Meter(screen, eval(pos), int(size), type)

            for slider in node.getElementsByTagName("slider"):
                name = slider.getAttribute("slider")
                pos = util.fetchTextFromElementByTagName(slider, "position")
                height = util.fetchTextFromElementByTagName(slider, "height")
                sliderImage = \
                    util.fetchTextFromElementByTagName(slider, "slider_image")

                self.sliders[name] = \
                    Slider(screen, sliderImage, eval(pos), int(height))

        (self.cockpit_img, self.cockpit_rect) = util.load_image(image, None)

    def checkButtonPressed(self, pos):
        '''Check through the button dictionary and return a button object
           if one was pressed.
        '''
        for button in self.buttons.keys():
            if self.buttons[button].button_rect.collidepoint(pos):
                return button
        return None

    def checkSliderGrabbed(self, pos):
        for slider in self.sliders.keys():
            if self.sliders[slider].sliderRect.collidepoint(pos):
                return button
        return None

    def checkShipStatus(self):
        ship = shipsettings.Ship()

        self.indicators['Cloaked'].active = ship.cloaked

        self.gauges['Fuel'].setValue(ship.fuel / MAXFUEL * 100)
        if ship.fuel < 200:
            self.indicators['Fuel Low'].active = True

        for shipSystem in [(ship.warp, "Warp"), (ship.subc, "Sub-C"),
                           (ship.guns, "Guns"), (ship.tac, "Tac"),
                           (ship.shields, "Shields"), (ship.pilot, "Pilot")]:

            setStatusLight(shipSystem[0], shipSystem[1], self.statuslights,
                           self.indicators)

    def handleEvents(self, event):
        if event.type == KEYUP:
            if event.key == K_w:
                print "warp speed"
                self.starfield.resetFocalpoint()
                self.starfield.setSpeed(500)
            elif event.key == K_i:
                self.starfield.setSpeed(20)
                print "impulse speed"
            elif event.key == K_s:
                self.starfield.setSpeed(0)
                print "stopped"
        elif event.type == MOUSEBUTTONUP:
            print "pos = %d,%d" % (pygame.mouse.get_pos())
            button = self.checkButtonPressed(pygame.mouse.get_pos())
            if button:
                self.buttons[button].active = \
                    not self.buttons[button].active
                print button
            if event.button == 3 and self.buttons['Master Arm'].active:
                self.buttons['Master Arm'].active = False
                self.starfield.freeview = False

            if self.buttons['Master Arm'].active:
                self.starfield.freeview = True

    def draw(self):
        # get the current status of the ship before we draw
        self.checkShipStatus()

        self.screen.fill((0, 0, 0))
        self.starfield.draw()

        self.screen.blit(self.cockpit_img, (0, 0))

        for button in self.buttons.keys():
            self.buttons[button].draw()

        for gauge in self.gauges.keys():
            self.gauges[gauge].draw()

        for indicator in self.indicators.keys():
            self.indicators[indicator].draw()

        for statuslight in self.statuslights.keys():
            self.statuslights[statuslight].draw()

        for meter in self.meters.keys():
            self.meters[meter].draw()

        for slider in self.sliders.keys():
            self.sliders[slider].draw()

class Slider:
    def __init__(self, screen, image, position, max_height):
        self.screen = screen
        self.position = position
        self.max_height = max_height

        self.setValue(100)

        (self.sliderImg, self.sliderRect) = util.load_image(image, -1)

    def setValue(self, value):
        self.value = value
        self.height = int(self.max_height * (value / 100.0))

        # XXX - FIXME put the image size in here
        (x, y) = self.position
        self.sliderRect = pygame.Rect((x, y-self.height), (7, 12))

    def draw(self):
        (x, y) = self.position
        self.screen.blit(self.sliderImg, (x, y-self.height))

class Button:
    def __init__(self, screen, imageOn, imageOff, position,
                 active=False):
        self.screen = screen
        self.position = position
        self.active = active

        (self.buttonOnImg, self.buttonOnRect) = util.load_image(imageOn, -1)
        (self.buttonOffImg, self.buttonOffRect) = util.load_image(imageOff, -1)

    def draw(self):
        if self.active:
            self.button_img = self.buttonOnImg
        else:
            self.button_img = self.buttonOffImg

        # XXX - fixme
        self.button_rect = pygame.Rect(self.position, (16, 14))
        self.screen.blit(self.button_img, self.position)

class Indicator:
    def __init__(self, screen, text, position, color, size, active):
        self.screen = screen
        self.position = position
        self.active = active

        font = util.load_font("CharcoalCY.ttf", size)
        self.text = font.render(text, 1, color)

    def draw(self):
        if self.active:
            self.screen.blit(self.text, self.position)

class Meter:
    def __init__(self, screen, position, size, type):
        self.screen = screen
        self.position = position
        self.value = 0
        self.color = (255, 255, 255)
        self.type = type

        self.font = util.load_font("CharcoalCY.ttf", size)

    def draw(self):
        if self.type == "percentage":
            value = "%d%%" % (self.value)
        else:
            value = "%d" % (self.value)

        text = self.font.render(value, 1, self.color)
        self.screen.blit(text, self.position)

class Gauge:
    def __init__(self, screen, position, color, max_height, width):
        self.screen = screen
        self.width = width
        self.color = color
        self.position = position 
        self.max_height = max_height
        self.value = 0
        self.height = 0

    def setValue(self, value):
        self.value = value
        self.height = int(self.max_height * (value / 100.0))

    def draw(self):
        (x, y) = self.position
        pygame.draw.line(self.screen, self.color, self.position, 
                         (x, y-self.height), self.width)

class StatusLight:
    def __init__(self, screen, position, state, type):
        self.screen = screen
        self.position = position
        self.type = type
        self.active = True

        self.state = "red"

    def draw(self):
        if self.type == "toggle" and self.active:
            pygame.draw.circle(self.screen, (0, 255, 0), self.position, 3)
        elif self.type == "multi" and self.state:
            # we should probably put some constants in for these
            if self.state == "red":
                color = (255, 0, 0)
            elif self.state == "yellow":
                color = (255, 228, 0)
            elif self.state == "green":
                color = (0, 255, 0)
            (x, y) = self.position
            pygame.draw.line(self.screen, color, self.position, (x+4, y), 3)

class StarField:
    def __init__(self, screen):
        self.stars = []
        self.quantity = 2000
        self.span = 40 
        self.current_speed = 0
        self.desired_speed = self.current_speed
        self.reset()
        self.screen = screen
        self.x_focalpoint = SCREEN_WIDTH / 2
        self.y_focalpoint = 150
        self.freeview = False

        self.current_x_focalpoint = SCREEN_WIDTH / 2
        self.current_y_focalpoint = 150

        self.resetFocalpoint()

    def resetFocalpoint(self):
        self.current_x_focalpoint = SCREEN_WIDTH / 2
        self.current_y_focalpoint = 150

    def setSpeed(self, speed):
        if speed != self.current_speed:
            self.desired_speed = speed

    def moveFocalpoint(self, location):
        (x, y) = location
        self.desired_x_focalpoint += x
        self.desired_y_focalpoint += y

    def reset(self):
        self.stars = []
        for value in range(self.quantity):
            star_values = \
                [random.randrange(-SCREEN_WIDTH * self.span, 
                              SCREEN_WIDTH * self.span),
                 random.randrange(-SCREEN_HEIGHT * self.span,
                              SCREEN_HEIGHT * self.span),
                 random.randrange(1, DIST)]
            self.stars.append(star_values)

    def draw(self):

        if self.current_speed < self.desired_speed:
            self.current_speed += 5
        elif self.current_speed > self.desired_speed:
            self.current_speed -= 8

        if self.freeview:
            mouse_pos = pygame.mouse.get_pos()

            # yaw left
            if mouse_pos[0] < (self.x_focalpoint / 3):
                self.current_x_focalpoint += 4
            elif mouse_pos[0] < (self.x_focalpoint / 3 * 2):
                self.current_x_focalpoint += 2
            elif mouse_pos[0] < self.x_focalpoint:
                self.current_x_focalpoint += 1

            # yaw right
            if mouse_pos[0] > (self.x_focalpoint / 3) * 2 + self.x_focalpoint:
                self.current_x_focalpoint -= 4
            elif mouse_pos[0] > (self.x_focalpoint / 3) + self.x_focalpoint:
                self.current_x_focalpoint -= 2
            elif mouse_pos[0] > self.x_focalpoint:
                self.current_x_focalpoint -= 1

            # pitch up
            if mouse_pos[1] < self.y_focalpoint - (self.y_focalpoint / 2):
                self.current_y_focalpoint += 3
            elif mouse_pos[1] < self.y_focalpoint:
                self.current_y_focalpoint += 1

            # pitch down
            if mouse_pos[1] > self.y_focalpoint + (self.y_focalpoint / 2):
                self.current_y_focalpoint -= 3
            elif mouse_pos[1] > self.y_focalpoint:
                self.current_y_focalpoint -= 1



        colors = []
        for value in range(256, 0, -1):
            color_value = 255 * value / 256
            colors.append((color_value, color_value, color_value))

        count = 0

        for width, height, depth in self.stars:

            if self.current_speed >= 100:
                w_depth = max(1, depth + self.current_speed * 2)
                w_width = width * 256 / w_depth
                w_height = height * 256 / w_depth

                w_draw_x = w_width + self.current_x_focalpoint
                w_draw_y = w_height + self.current_y_focalpoint

            width = width * 256 / depth
            height = height * 256 / depth

            color = min(255, depth * 255 / DIST)

            draw_x = width + self.current_x_focalpoint
            draw_y = height + self.current_y_focalpoint

            if self.current_speed >= 100:
                pygame.draw.line(self.screen, colors[color],
                                 (draw_x, draw_y), (w_draw_x, w_draw_y), 1)

            pygame.draw.circle(self.screen, colors[color], (draw_x, draw_y), 1)

            ch = False
            depth -= self.current_speed
            if depth <= 0:
                ch = True
                depth += DIST
            if depth > DIST:
                ch = True
                depth -= DIST
            if ch:
                self.stars[count][0] = \
                    random.randrange(-SCREEN_WIDTH * self.span,
                                     SCREEN_WIDTH * self.span)
                self.stars[count][1] = \
                    random.randrange(-SCREEN_HEIGHT * self.span,
                                     SCREEN_HEIGHT * self.span)
            self.stars[count][2] = depth
            count += 1

def setStatusLight(bay, name, statusLights, indicatorLights):
    """Walk through each of the rows in a given system bay and
       determine the current status.

       This is a helper function for determining the current
       status of a given system bay.  If a shunt has been added,
       mark the system as "yellow", and if more than one has been
       added, mark the system as "red".

    """

    count = 1
    systemonline = len(bay)

    for row in bay:
        shuntCount = 0
        partStatus = "green"
        statusLightName = "%s %d" % (name, count)

        for part in row:
            if part in (shipsettings.PART_MISSING, shipsettings.PART_BROKEN):
                partStatus = None
                systemonline -= 1     
            elif part == shipsettings.PART_SHUNT:
                shuntCount += 1

        if partStatus:
            if shuntCount == 1:
                partStatus = "yellow"
            elif shuntCount > 1:
                partStatus = "red"

        statusLights[statusLightName].state = partStatus
        count += 1

    if not systemonline:
        indicatorLights[name].active = True
    else:
        indicatorLights[name].active = False


if __name__ == "__main__":

    pygame.init()
    window = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption('SunDog: Python Resurrection')

    screen = pygame.display.get_surface()

    cockpit = Cockpit(screen)
    cockpit.draw()

    pygame.display.flip()

    clock = pygame.time.Clock()

    while True:
        # XXX - 30 fps for now
        clock.tick(60)

        for event in pygame.event.get():
            if event.type == MOUSEBUTTONDOWN:
                slider = cockpit.checkSliderGrabbed(pygame.mouse.get_pos())
                if slider:
                    print "Slider grabbed"
            if event.type == MOUSEBUTTONUP:
                print "pos = %d,%d" % (pygame.mouse.get_pos())
                button = cockpit.checkButtonPressed(pygame.mouse.get_pos())
                if button:
                    cockpit.buttons[button].active = \
                        not cockpit.buttons[button].active
                    print button
                if event.button == 3 and cockpit.buttons['Master Arm'].active:
                    cockpit.buttons['Master Arm'].active = False
                    cockpit.starfield.freeview = False

                if cockpit.buttons['Master Arm'].active:
                    cockpit.starfield.freeview = True

            elif event.type == KEYUP:
                if event.key == K_w:
                    print "warp speed"
                    cockpit.starfield.resetFocalpoint()
                    cockpit.starfield.setSpeed(500)
                elif event.key == K_i:
                    cockpit.starfield.setSpeed(20)
                    print "impulse speed"
                elif event.key == K_s:
                    cockpit.starfield.setSpeed(0)
                    print "stopped"

                #cockpit.starfield.resetFocalpoint()

        cockpit.draw()
        pygame.display.flip()


