###############################################################################
# File:        guShipsView.py
# Appointment: Ships viewer
# Project:     GalaxyU
# Author:      AR
# History:
# 04.01.2019 Fixed launched info: don't show launched if destination is current star - confusing
# 11.10.2013 Propagationg ships FleetName and Efficiency to reports
# 15.01.2013 Created
###############################################################################
import wxTools
import guObjects
import guShipCalc
import guApp
import guListView
import zx

import wx
import copy


#===============================================================================
# Ship components list control
#===============================================================================
class CShipsViewCtrl(guListView.CListViewCtrlWithBuilder):
    #---------------------------------------------------------------------------
    def __init__(self, parent, aListCreator):
        guListView.CListViewCtrlWithBuilder.__init__(self, parent, aListCreator)

    #---------------------------------------------------------------------------
    # Redefined virtual
    def PrepareBuilder(self, aBuilder):
        # Add columns
        aBuilder.AddColumn("ID", lambda data: str(data.Id), wx.LIST_FORMAT_RIGHT, True)
        aBuilder.AddColumn("Name", lambda data: data.Name)
        aBuilder.AddColumn("Nation", lambda data: data.NationName)
        aBuilder.AddColumn("Type", lambda data: data.TypeName)
        aBuilder.AddColumn("Fleet Name", lambda data: data.FleetName)
        aBuilder.AddColumn("Default Fleet Name", lambda data: data.DefaultFleetName)
        aBuilder.AddColumn(\
            "Location", 
            lambda data: \
                ("in space: " + data.GetLocationStr()) \
                    if data.InSpace \
                    else \
                        ("?" if data.Star is None \
                            else (data.Star.GetFullName() + \
                                (" (launched to " + \
                                    guApp.Application.GetReport().GetStarName(data.GetDestination()) + ")" \
                                if data.IsLaunched() and data.Star.Id != data.GetDestination() else ""))))
        aBuilder.AddColumn("Mass", lambda data: "{0:.2f}".format(data.LoadedMass), wx.LIST_FORMAT_RIGHT, True)
        aBuilder.AddColumn("Speed", lambda data: "{0:.2f}".format(data.GetSpeed()), wx.LIST_FORMAT_RIGHT, True)
        aBuilder.AddColumn("Shield", lambda data: "{0:.2f}".format(data.GetShield()), wx.LIST_FORMAT_RIGHT, True)
        aBuilder.AddColumn("Bombing", lambda data: "{0:.2f}".format(data.GetBombing()), wx.LIST_FORMAT_RIGHT, True)
        aBuilder.AddColumn("Efficiency", lambda data: "{0:.2f}".format(data.Efficiency), wx.LIST_FORMAT_RIGHT, True)
        aBuilder.AddColumn("CF", lambda data: "{0:.2f}".format(data.CombatFactor), wx.LIST_FORMAT_RIGHT, True)
        aBuilder.AddColumn("Built", lambda data: str(data.BuildYear), wx.LIST_FORMAT_RIGHT, True)
        aBuilder.AddColumn("Travelled", lambda data: "{0:.2f}".format(data.TravelledDistance), wx.LIST_FORMAT_RIGHT, True)
        aBuilder.AddColumn("Been damaged", lambda data: str(data.DamagedCounter), wx.LIST_FORMAT_RIGHT, True)
        aBuilder.AddColumn("Destroyed ships", lambda data: str(data.DestroyedEnemyShipsCounter), wx.LIST_FORMAT_RIGHT, True)
        aBuilder.AddColumn("Destroyed mass", lambda data: str(data.DestroyedEnemyShipsMass), wx.LIST_FORMAT_RIGHT, True)
        def GetCargoStr(aShip):
            c = aShip.FindComponents(guObjects.CShipComponent.SHIP_COMPONENT_CARGO)
            # (It can be only one component cargo)
            return c[0].Cargo if len(c) > 0 else ""
        aBuilder.AddColumn("Cargo", lambda data: GetCargoStr(data))
        aBuilder.AddColumn("Upgrade Status", lambda data: data.UpgradeStatus)
        aBuilder.AddColumn(\
            "Assignment information",
            lambda data: ("ETA=" + str(data.ETA()) if data.ETA() > 0 else "") + \
                ("" if data.Assignment == "?" else ("; " if data.ETA() > 0 else "") + data.Assignment),
            wx.LIST_FORMAT_LEFT)


    #---------------------------------------------------------------------------
    # Redefined virtual
    def OnEdit(self, aPos):
        guApp.Application.WindowsCollection.RegisterAndShow(\
            lambda: guShipCalc.CShipDesignerFrame(\
                guApp.Application.GetMainWindow(), self.GetItems()[self.GetSelectedDataItem()]))
        

    #---------------------------------------------------------------------------
    # Redefined virtual
    def GetPopupMenu(self):
        menu = wx.Menu()
        mi = wx.MenuItem(menu, wx.NewId(), 'Locate the ship on the map')
        
        # Local helper - looks for where the ship is located - either on a star or in a space group
        def FindShipSpaceObjectId(aShip):
            if aShip.Star is not None:
                return aShip.Star.Id
            # The ship must be in space and belongs to a space group
            sg = zx.Find(guApp.Application.GetReport().SpaceGroups, lambda sg: sg.X == aShip.X and sg.Y == aShip.Y)
            return sg.Id if sg is not None else None
        
        self.Bind(wx.EVT_MENU,
            lambda event: guApp.Application.GetStarMap().ShowSpaceObject(\
                FindShipSpaceObjectId(self.GetItems()[self.GetSelectedDataItem()])))
        menu.AppendItem(mi)
        return menu
#-------------------------------------------------------------------------------


################################################################################
# List of ships
def ShipsList(aShipsListCreator, aTitle = "", aCfgPostfix = ""):
    return guApp.Application.WindowsCollection.RegisterAndShow(\
        lambda: guListView.CListViewFrame(\
            guApp.Application.GetMainWindow(), aShipsListCreator,
            lambda panel, aListCreator: CShipsViewCtrl(panel, aListCreator),
            "CShipsViewFrame" + aCfgPostfix, aTitle))


################################################################################
# List of all ships
def AllShipsList(aFunc = None, aTitle = "All visible ships", aCfgPostfix = ""):
    return ShipsList(\
        lambda: zx.GetSubListFor(\
            guApp.Application.GetReport().Ships,
            None if guApp.Application.GetReport().IsGM() else aFunc),
            aTitle, aCfgPostfix)

################################################################################
# List of own ships
def OwnShipsList():
    return AllShipsList(\
        lambda ship: ship.NationName == guApp.Application.GetReport().NationName,
        "Your ships", "Own")

################################################################################
# List of other ships
def OtherShipsList():
    return AllShipsList(\
        lambda ship: ship.NationName != guApp.Application.GetReport().NationName,
        "Other ships", "Other")


#===============================================================================
# Ship Types list control
#===============================================================================
class CShipTypesViewCtrl(guListView.CListViewCtrlWithBuilder):
    #---------------------------------------------------------------------------
    # aShips - list of ships
    def __init__(self, parent, aListCreator):
        guListView.CListViewCtrlWithBuilder.__init__(self, parent, aListCreator)
        

    #---------------------------------------------------------------------------
    # Redefined virtual
    def PrepareBuilder(self, aBuilder):
        # Cash with a list of ships for each type {name : ships}
        self.__shipsForType = {}

        # Add columns
        aBuilder.AddColumn("Nation", lambda data: data.NationName)
        aBuilder.AddColumn("Name", lambda data: data.Name)
        aBuilder.AddColumn(\
            "Ship Class",
            lambda data: "Combat" if data.ShipClass == 0 else ("Service" if data.ShipClass == 1 else "Unknown"))
        aBuilder.AddColumn(\
            "Code",
            lambda data: guObjects.CShipType.GetComponentsCommandString(data.Components))
        aBuilder.AddColumn(\
            "Number of Ships",
            lambda data: str(len(self.__GetShipsForType(data))), wx.LIST_FORMAT_RIGHT, True)

    #---------------------------------------------------------------------------
    # Helper - returns ships for type
    def __GetShipsForType(self, aShipType):     
        key = aShipType.NationName + aShipType.Name
        if key in self.__shipsForType:
            return self.__shipsForType[key]
        # Not created yet - populate:
        self.__shipsForType[key] = \
            zx.GetSubListFor(guApp.Application.GetReport().Ships,\
                lambda ship: aShipType.Name == ship.TypeName and aShipType.NationName == ship.NationName)
        return self.__shipsForType[key]

    
    #---------------------------------------------------------------------------
    # Redefined virtual
    def OnEdit(self, aPos):
        self.__ShowShipsFor(self.GetSelectedDataItem())
            

    #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -        
    # Helper: shows ships for item aIndex
    def __ShowShipsFor(self, aIndex):
        st = self.GetItems()[aIndex]
        if len(self.__GetShipsForType(st)) > 0:
            ShipsList(\
                lambda: self.__GetShipsForType(st),
                st.NationName + "'s ships of type '" + st.Name + "'")


    #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -        
    # Helper: calls ship type viewer
    def __ShowShipCalc(self, aIndex):
        # Create a dummy ship with selected type
        ship = guObjects.CShip(0, self.GetItems()[aIndex].NationName, self.GetItems()[aIndex].Name)
        ship.Components = copy.deepcopy(self.GetItems()[aIndex].Components)
        # Show the viewer
        guApp.Application.WindowsCollection.RegisterAndShow(\
            lambda: guShipCalc.CShipDesignerFrame(\
                guApp.Application.GetMainWindow(), ship, True))


    #---------------------------------------------------------------------------
    # Redefined virtual
    def GetPopupMenu(self):
        menu = wx.Menu()
        mi = wx.MenuItem(menu, wx.NewId(), 'Show ship type')
        self.Bind(wx.EVT_MENU, lambda event: self.__ShowShipCalc(self.GetSelectedDataItem()), mi)
        menu.AppendItem(mi)
        st = self.GetItems()[self.GetSelectedDataItem()]
        if len(self.__GetShipsForType(st)) > 0:
            mi = wx.MenuItem(menu, wx.NewId(), 'Show ships')
            self.Bind(wx.EVT_MENU, lambda event: self.__ShowShipsFor(self.GetSelectedDataItem()), mi)
            menu.AppendItem(mi)
        return menu
#-------------------------------------------------------------------------------

