###############################################################################
# File:        guPlanetView.py
# Appointment: Star map view support
# Project:     GalaxyU
# Author:      AR
# Created:     25.09.2012
# Last change: 18.10.2016
###############################################################################
import zx
import wxTools
import pgTools
import guTools
import guReport
import pgWxDisplay
import guApp
import guStarDust

import math
import wx
import random
import pygame.gfxdraw

#===============================================================================
# Map view
#===============================================================================
class CPlanetViewFrame(guTools.CCfgFrame):
    #---------------------------------------------------------------------------
    def __init__(self, parent, aStarId, aPlanetImage, aStarImage):
        guTools.CCfgFrame.__init__(self, parent, guApp.Application.Config, "GUI", "CPlanetViewFrame")
        self.__planetImage = aPlanetImage
        self.__starImage = aStarImage
        self.__statusBar = self.CreateStatusBar()
        self.__tagsInitialized = False

        area = zx.CArea(0, 0, 1300, 800);
        self.__display = pgWxDisplay.CPygameDisplay(self, -1, self.PygameDraw, area, area)
        
        # Display events
        self.__display.OnKeyEvent = self.OnKeyEvent
        self.__display.OnMouseMoveEvent = self.OnMouseMoveEvent
        self.__display.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRightDownEvent)

        # Disable scrolling and rescale
        self.__display.EnableScrolling(False, False)
        self.__display.CanRescale = False

        # Star dust
        self.__starId = aStarId
        random.seed(self.__starId)
        self._StarDust = guStarDust.Generate(1000, self.__display._totalArea)
        
        # Prepare ship groups
        self.__RefreshShips()

        # Min window size
        self.SetSizeHints(area.GetWidth() + 20, area.GetHeight() + 30, area.GetWidth() + 20, area.GetHeight() + 30)
        self.__display.SetFocus()
        
        # Fonts
        self.__headerFont = pgTools.LoadFont("Fonts/SelfDestructButtonBB_reg.ttf", 45)
        self.__lastReportedFont = pgTools.LoadFont("Fonts/SelfDestructButtonBB_reg.ttf", 40)
        self.__lastReportedFont.set_italic(True)
        self.__infoFont = pgTools.LoadFont("Fonts/SelfDestructButtonBB_reg.ttf", 30)
        self.__shipsGroupFont = pgTools.LoadFont("Fonts/freesansbold.ttf", 15)
        self.__bombingFont = pgTools.LoadFont("Fonts/SaucerBB.ttf", 25)

        # Currently highlighted star
        self.__highlightedSpaceObject = None
    
        # Subscribe for refresh message
        wxTools.PublisherSubscribe(self.__MessageRefreshListener, guApp.MESSAGE_REFRESH_DATA)

    #---------------------------------------------------------------------------
    def __del__(self):
        wxTools.PublisherUnsubscribe(self.__MessageRefreshListener, guApp.MESSAGE_REFRESH_DATA)

    #---------------------------------------------------------------------------
    def __MessageRefreshListener(self, arg = ""):
        self.__RefreshShips()
        self.__display.RePaint()


    #---------------------------------------------------------------------------
    def __RefreshShips(self):
        # Prepare ship groups
        class CShipTag:
            pass
        self.__shipGroups = []
        for sg in guApp.Application.GetReport().GetShipGroups():
            if sg.StarId == self.__starId:
                tag = CShipTag()
                tag.sg = sg
                self.__shipGroups.append(tag)


    #---------------------------------------------------------------------------
    def PygameDraw(self, aScreen):
        # Clear screen
        aScreen.fill((0,0,0))

        guStarDust.Draw(self._StarDust, self.__display._screen)
        
        # Draw star
        image = self.__starImage
        starRect = image.get_rect()
        starRect.centerx = self.GetClientSize().GetWidth() - starRect.width/2
        starRect.centery = starRect.height/2
        aScreen.blit(image, starRect)

        # Main header
        star = guApp.Application.GetReport().FindStar(self.__starId)
        stateColour = guApp.Application.GetReport().GetStateColour(star)
        self.SetTitle("Planet in star system " + star.GetFullName())
        owner = star.GetOwner()
        font = self.__headerFont
        ownership = star.GetOwnership()
        font.set_bold(ownership.IsOwn or ownership.IsActualKnown)
        headerRect = pgTools.DrawText(
            aScreen,
            ('Your' if ownership.IsOwn else (owner + "'s" if owner != "" else '')) + ' ' + \
                ('colony' if owner != "" else 'Uninhabited planet' + \
                    ((" (former '" + star.BombRecords[0].Owner + "' colony)")\
                        if star.HasBombing() else "")) + ' ' + \
                            star.GetFullName() if star.IsInvestigated() else \
                                "Unknown star system " + star.GetFullName(),
            self.GetClientSize().GetWidth()/2, 0, font, stateColour, True)
        
        if not star.IsInvestigated():
            return # nothing to do more - not investigated yet

        # Draw the planet
        planetRect = self.__planetImage.get_rect();
        planetRect.centerx = planetRect.width/2
        planetRect.centery = self.GetClientSize().GetHeight() - planetRect.height/2
        aScreen.blit(self.__planetImage, planetRect)

        # Draw flag
        guTools.DrawNationFlag(\
            aScreen, owner, planetRect.centerx,
            planetRect.centery - planetRect.height/2, stateColour)
        
        # Last reported
        if ownership.IsKnown and not ownership.IsActualKnown:
            headerRect = pgTools.DrawText(aScreen, 
                             "[Last reported at Year " + str(star.LastKnownAt) + \
                                       " (" + str(star.CalcLastReportedAgo()) + " years ago)]",
                             headerRect.centerx, headerRect.height, self.__lastReportedFont, stateColour, True)

        # Live conditions
        oldRect = headerRect
        oldRect = pgTools.DrawText(\
            aScreen, 
            "Live conditions: " + "{0:.2f}".format(star.LiveConditions),
            oldRect.centerx, oldRect.bottom + 10, self.__infoFont, stateColour, True)
         
        # Resources
        oldRect = pgTools.DrawText(\
            aScreen, 
            "Resource Factor: " + "{0:.2f}".format(star.ResourceFactor),
            oldRect.centerx, oldRect.bottom, self.__infoFont, stateColour, True)

        # Materials
        oldRect = pgTools.DrawText(\
            aScreen, 
            "Materials: " + "{0:.2f}".format(star.Materials),
            oldRect.centerx, oldRect.bottom, self.__infoFont, stateColour, True)

        # Size
        oldRect = pgTools.DrawText(\
            aScreen, 
            "Size: " + str(int(star.Size)),
            oldRect.centerx, oldRect.bottom, self.__infoFont, stateColour, True)
        
        # Industry
        oldRect = pgTools.DrawText(\
            aScreen, 
            "Industry: " + "{0:.4f}".format(star.Industry) + \
                (' (MAX reached)' if star.Industry >= star.Size else ''),
            oldRect.centerx, oldRect.bottom, self.__infoFont, stateColour, True)

        # Population
        oldRect = pgTools.DrawText(\
            aScreen, 
            "Population: " + "{0:.4f}".format(star.CalcActivePopulation()) + \
                (' (MAX reached)' if star.Population >= star.Size else ''),
            oldRect.centerx, oldRect.bottom, self.__infoFont, stateColour, True)

        # Col
        oldRect = pgTools.DrawText(\
            aScreen, 
            " Colonists: " + "{0:.2f}".format(star.CalcCol()),
            oldRect.centerx, oldRect.bottom, self.__infoFont, stateColour, True)
                                   
        # Bombing info
        if star.HasBombing():
            # Calculate all damage and attackers
            attackers = ""
            destroyedPOP = 0.0
            destroyedCOL = 0.0
            destroyedIND = 0.0
            for br in star.BombRecords:
                attackers += ("" if attackers == "" else ", ") + br.Attacker
                destroyedPOP += br.Pop
                destroyedCOL += br.Col
                destroyedIND += br.Ind

            # Draw some explosions
            n = int(pow(destroyedPOP + destroyedCOL + destroyedIND, 0.5)*1.5)
            minN = 3
            if n < minN:
                n = minN
            r = planetRect.width/2 - 10
            count = 0
            for i in range(1000):
                x = planetRect.left + random.random()*planetRect.width
                y = planetRect.top + random.random()*planetRect.height
                if math.pow(math.pow(x - planetRect.centerx, 2) + math.pow(y - planetRect.centery, 2), 0.5) > r:
                    continue
                
                ind = int(random.random()*len(guApp.Application.ExplosionImages.Images))
                rect = guApp.Application.ExplosionImages.Images[ind].get_rect()
                rect.centerx = int(x)
                rect.centery = int(y)
                aScreen.blit(guApp.Application.ExplosionImages.Images[ind], rect)
                count += 1
                if (count == n):
                    break

            # Display data
            c = (255, 0, 0)
            oldRect = pgTools.DrawText(\
                aScreen, 
                "The Colony is under attack",
                planetRect.centerx, planetRect.centery, self.__bombingFont, c, True)
            oldRect = pgTools.DrawText(\
                aScreen, 
                "by " + attackers,
                oldRect.centerx, oldRect.bottom, self.__bombingFont, c, True)
            if destroyedPOP > 0:                                   
                oldRect = pgTools.DrawText(\
                    aScreen, 
                    "Killed population: " + "{0:.2f}".format(destroyedPOP),
                    oldRect.centerx, oldRect.bottom, self.__bombingFont, c, True)
            if destroyedCOL > 0:                                   
                oldRect = pgTools.DrawText(\
                    aScreen, 
                    "Killed colonists: " + \
                        "{0:.2f}".format(destroyedCOL/guApp.Application.API.guColFactor()),
                    oldRect.centerx, oldRect.bottom, self.__bombingFont, c, True)
            if destroyedIND > 0:                                   
                oldRect = pgTools.DrawText(\
                    aScreen, 
                    "Destroyed industry: " + "{0:.2f}".format(destroyedIND),
                    oldRect.centerx, oldRect.bottom, self.__bombingFont, c, True)

        # Calculate koefs to fit all ships on the screen
        ky = 30
        dsize = 20
        while True:
            y = 0
            d = 0
            for tag in self.__shipGroups:
                newY = (tag.sg.Y - star.Y)*ky + planetRect.height
                if newY > y:
                    y = newY
                    d = tag.sg.CalcOnScreenDiameter(lambda aKoef, aMinSize: aKoef*dsize)
            if y + d/2 <= self.__display._totalArea.GetHeight():
                break;
            ky -= 1
            dsize -= 0.7
            if dsize <= 1:
                dsize = 1
                break

        # Ship groups
        for tag in reversed(self.__shipGroups):
            if not hasattr(tag, 'screenX'):
                # Tag not initialized yet
                tag.screenX, tag.screenY = \
                    (tag.sg.X - star.X)*30 + planetRect.width*1.2, \
                    (tag.sg.Y - star.Y)*ky + int(planetRect.height/1.2)
                
                tag.shipColour = \
                    guApp.Application.GetReport().GetNationColour(tag.sg.Ships[0].NationName) 
                tag.shipImage = \
                    guApp.Application.GetReport().GetShipImage(\
                        tag.sg.Ships[0].Components, tag.sg.Ships[0].NationName,
                        tag.sg.Ships[0].TypeName, tag.shipColour)
                tag.screenDiameter = tag.sg.CalcOnScreenDiameter(lambda aKoef, aMinSize: aKoef*dsize)

            k = tag.shipImage.get_width()/tag.shipImage.get_height()
            shipImage = pygame.transform.scale(\
                tag.shipImage, (int(tag.screenDiameter), int(tag.screenDiameter)/k))
            if not tag.sg.IsFriendly():
                shipImage = pygame.transform.rotate(shipImage, 180)

            shipRect = shipImage.get_rect();
            shipRect.centerx = tag.screenX
            shipRect.centery = tag.screenY
            aScreen.blit(shipImage, shipRect)
            
            # Display a number of ships
            if not tag.sg.SuppressLabel:            
                label = (str(len(tag.sg.Ships)) if len(tag.sg.Ships) > 1 else \
                    tag.sg.Ships[0].GetFullShipName()) + \
                    " (" + tag.sg.Ships[0].NationName + ")" +\
                    ((" (fleet: " + tag.sg.Ships[0].FleetName + ")") \
                        if tag.sg.Ships[0].FleetName != "" else "")
            else:
                label = ("X " + str(len(tag.sg.Ships))) if len(tag.sg.Ships) > 1 else ""

            if len(label) > 0:                
                text = self.__shipsGroupFont.render(label, True, tag.shipColour)

                textRect = text.get_rect()
                textRect.centery = shipRect.centery
                if tag.sg.IsFriendly():
                    textRect.right = shipRect.left - 3
                else:
                    textRect.left = shipRect.right + 3
                aScreen.blit(text, textRect)
        
        # Mark as initialized: all tags should be fine now    
        self.__tagsInitialized = True
        
    #---------------------------------------------------------------------------
    def OnKeyEvent(self, aEvent, aKey):
        if aKey == wx.WXK_ESCAPE:
            self.Close()
        return False

    #---------------------------------------------------------------------------
    def OnMouseMoveEvent(self, event):
        if not self.__tagsInitialized:
            return # not ready yet
            
        # Find a ship group, pointed by mouse
        found = False
        for tag in self.__shipGroups:
            r = tag.screenDiameter/2
            if event.GetX() >= tag.screenX - r and event.GetX() <= tag.screenX + r and \
               event.GetY() >= tag.screenY - r and event.GetY() <= tag.screenY + r:
                found = True
                if self.__highlightedSpaceObject is None or tag.sg.Ships[0].Id != self.__highlightedSpaceObject.SpaceObject.Ships[0].Id:
                    self.__highlightedSpaceObject = guTools.CHighlightedSpaceObject(self.__display, 
                                                                                    tag.screenX, 
                                                                                    tag.screenY, 
                                                                                    tag.sg, 
                                                                                    tag.screenDiameter,
                                                                                    lambda txt: self.__statusBar.SetStatusText(txt))
                    self.__highlightedSpaceObject.Draw()
                    break
        
        if not found and self.__highlightedSpaceObject is not None:
            self.__highlightedSpaceObject = None
            

    #---------------------------------------------------------------------------
    def OnMouseRightDownEvent(self, event):
        if self.__highlightedSpaceObject is not None:
            menu = self.__highlightedSpaceObject.SpaceObject.GetPopupMenu(self)
            if menu is not None:
                self.PopupMenu(menu, event.GetPosition())

        event.Skip()

#-------------------------------------------------------------------------------


#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# Shows planet view
#>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
def ShowPlanet(aStarId, aPlanetImage = None, aStarImage = None):
    if aPlanetImage is None or aStarImage is None:
        starMap = guApp.Application.GetStarMap()
        aPlanetImage = starMap.GetPlanetImage(aStarId)
        aStarImage = starMap.GetStarImage(aStarId)

    guApp.Application.WindowsCollection.RegisterAndShow(\
        lambda: CPlanetViewFrame(\
            guApp.Application.GetMainWindow(), aStarId, aPlanetImage, aStarImage))
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
