from collections import OrderedDict
import csv
from sys import platform
from functools import partial
import time
if __debug__:
    from traceback import print_exc

import Tkinter as tk
import ttk
import myNotebook as nb

import companion
from companion import ship_map
from l10n import Locale
from monitor import monitor
import prefs

if platform=='win32':
    import ctypes
    from ctypes.wintypes import *
    try:
        CalculatePopupWindowPosition = ctypes.windll.user32.CalculatePopupWindowPosition
        CalculatePopupWindowPosition.argtypes = [ctypes.POINTER(POINT), ctypes.POINTER(SIZE), UINT, ctypes.POINTER(RECT), ctypes.POINTER(RECT)]
        GetParent = ctypes.windll.user32.GetParent
        GetParent.argtypes = [HWND]
        GetWindowRect = ctypes.windll.user32.GetWindowRect
        GetWindowRect.argtypes = [HWND, ctypes.POINTER(RECT)]
    except:	# Not supported under Wine 4.0
        CalculatePopupWindowPosition = None

def status(data):

    # StatsResults assumes these three things are first
    res = [ [_('Cmdr'),    data['commander']['name']],
            [_('Balance'), str(data['commander'].get('credits', 0))],	# Cmdr stats
            [_('Loan'),    str(data['commander'].get('debt', 0))],	# Cmdr stats
    ]

    RANKS = [	# in output order
        (_('Combat')     , 'combat'),		# Ranking
        (_('Trade')      , 'trade'),		# Ranking
        (_('Explorer')   , 'explore'),		# Ranking
        (_('CQC')        , 'cqc'),		# Ranking
        (_('Federation') , 'federation'),	# Ranking
        (_('Empire')     , 'empire'),		# Ranking
        (_('Powerplay')  , 'power'),		# Ranking
        # ???            , 'crime'),		# Ranking
        # ???            , 'service'),		# Ranking
    ]

    RANK_NAMES = {

        # http://elite-dangerous.wikia.com/wiki/Pilots_Federation#Ranks
        'combat'     : [_('Harmless'),		# Combat rank
                        _('Mostly Harmless'),	# Combat rank
                        _('Novice'),		# Combat rank
                        _('Competent'),		# Combat rank
                        _('Expert'),		# Combat rank
                        _('Master'),		# Combat rank
                        _('Dangerous'),		# Combat rank
                        _('Deadly'),		# Combat rank
                        _('Elite')],		# Top rank
        'trade'      : [_('Penniless'),		# Trade rank
                        _('Mostly Penniless'),	# Trade rank
                        _('Peddler'),		# Trade rank
                        _('Dealer'),		# Trade rank
                        _('Merchant'),		# Trade rank
                        _('Broker'),		# Trade rank
                        _('Entrepreneur'),	# Trade rank
                        _('Tycoon'),		# Trade rank
                        _('Elite')],		# Top rank
        'explore'    : [_('Aimless'),		# Explorer rank
                        _('Mostly Aimless'),	# Explorer rank
                        _('Scout'),		# Explorer rank
                        _('Surveyor'),		# Explorer rank
                        _('Trailblazer'),	# Explorer rank
                        _('Pathfinder'),	# Explorer rank
                        _('Ranger'),		# Explorer rank
                        _('Pioneer'),		# Explorer rank
                        _('Elite')],		# Top rank
        'cqc'        : [_('Helpless'),		# CQC rank
                        _('Mostly Helpless'),	# CQC rank
                        _('Amateur'),		# CQC rank
                        _('Semi Professional'),	# CQC rank
                        _('Professional'),	# CQC rank
                        _('Champion'),		# CQC rank
                        _('Hero'),		# CQC rank
                        _('Gladiator'),		# CQC rank
                        _('Elite')],		# Top rank

        # http://elite-dangerous.wikia.com/wiki/Federation#Ranks
        'federation' : [_('None'),		# No rank
                        _('Recruit'),		# Federation rank
                        _('Cadet'),		# Federation rank
                        _('Midshipman'),	# Federation rank
                        _('Petty Officer'),	# Federation rank
                        _('Chief Petty Officer'),	# Federation rank
                        _('Warrant Officer'),	# Federation rank
                        _('Ensign'),		# Federation rank
                        _('Lieutenant'),	# Federation rank
                        _('Lieutenant Commander'),	# Federation rank
                        _('Post Commander'),	# Federation rank
                        _('Post Captain'),	# Federation rank
                        _('Rear Admiral'),	# Federation rank
                        _('Vice Admiral'),	# Federation rank
                        _('Admiral')],		# Federation rank

        # http://elite-dangerous.wikia.com/wiki/Empire#Ranks
        'empire'     : [_('None'),		# No rank
                        _('Outsider'),		# Empire rank
                        _('Serf'),		# Empire rank
                        _('Master'),		# Empire rank
                        _('Squire'),		# Empire rank
                        _('Knight'),		# Empire rank
                        _('Lord'),		# Empire rank
                        _('Baron'),		# Empire rank
                        _('Viscount'),		# Empire rank
                        _('Count'),		# Empire rank
                        _('Earl'),		# Empire rank
                        _('Marquis'),		# Empire rank
                        _('Duke'),		# Empire rank
                        _('Prince'),		# Empire rank
                        _('King')],		# Empire rank

        # http://elite-dangerous.wikia.com/wiki/Ratings
        'power'      : [_('None'),		# No rank
                        _('Rating 1'),		# Power rank
                        _('Rating 2'),		# Power rank
                        _('Rating 3'),		# Power rank
                        _('Rating 4'),		# Power rank
                        _('Rating 5')],		# Power rank
    }

    ranks = data['commander'].get('rank', {})
    for title, thing in RANKS:
        rank = ranks.get(thing)
        names = RANK_NAMES[thing]
        if isinstance(rank, int):
            res.append([title, rank < len(names) and names[rank] or ('Rank %d' % rank)])
        else:
            res.append([title, _('None')])	# No rank

    return res


def export_status(data, filename):
    h = csv.writer(open(filename, 'wb'))
    h.writerow(['Category', 'Value'])
    for thing in status(data):
        h.writerow([x.encode('utf-8') for x in thing])


# Returns id,name,shipName,system,station,value
def ships(data):

    ships = companion.listify(data.get('ships'))
    current = data['commander'].get('currentShipId')

    if isinstance(current, int) and current < len(ships) and ships[current]:
        ships.insert(0, ships.pop(current))	# Put current ship first

        if not data['commander'].get('docked'):
            # Set current system, not last docked
            return ([ (str(ships[0]['id']), ship_map.get(ships[0]['name'].lower(), ships[0]['name']), ships[0].get('shipName', ''), data['lastSystem']['name'], '', str(ships[0]['value']['total'])) ] +
                    [ (str(ship['id']), ship_map.get(ship['name'].lower(), ship['name']), ship.get('shipName', ''), ship['starsystem']['name'], ship['station']['name'], str(ship['value']['total'])) for ship in ships[1:] if ship])

    return [ (str(ship['id']), ship_map.get(ship['name'].lower(), ship['name']), ship.get('shipName', ''), ship['starsystem']['name'], ship['station']['name'], str(ship['value']['total'])) for ship in ships if ship]

def export_ships(data, filename):
    h = csv.writer(open(filename, 'wb'))
    h.writerow(['Id', 'Ship', 'Name', 'System', 'Station', 'Value'])
    for thing in ships(data):
        h.writerow([x.encode('utf-8') for x in thing])


class StatsDialog():

    def __init__(self, app):
        self.parent = app.w
        self.status = app.status
        self.showstats()

    def showstats(self):
        if not monitor.cmdr:
            return

        self.status['text'] = _('Fetching data...')
        self.parent.update_idletasks()

        try:
            data = companion.session.profile()
        except companion.ServerError as e:
            self.status['text'] = str(e)
            return
        except Exception as e:
            if __debug__: print_exc()
            self.status['text'] = str(e)
            return

        if not data.get('commander') or not data['commander'].get('name','').strip():
            self.status['text'] = _("Who are you?!")		# Shouldn't happen
        elif not data.get('lastSystem') or not data['lastSystem'].get('name','').strip() or not data.get('lastStarport') or not data['lastStarport'].get('name','').strip():
            self.status['text'] = _("Where are you?!")		# Shouldn't happen
        elif not data.get('ship') or not data['ship'].get('modules') or not data['ship'].get('name','').strip():
            self.status['text'] = _("What are you flying?!")	# Shouldn't happen
        else:
            self.status['text'] = ''
            StatsResults(self.parent, data)


class StatsResults(tk.Toplevel):

    def __init__(self, parent, data):
        tk.Toplevel.__init__(self, parent)

        self.parent = parent

        stats = status(data)
        self.title(' '.join(stats[0]))	# assumes first thing is player name

        if parent.winfo_viewable():
            self.transient(parent)

        # position over parent
        if platform!='darwin' or parent.winfo_rooty()>0:	# http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
            self.geometry("+%d+%d" % (parent.winfo_rootx(), parent.winfo_rooty()))

        # remove decoration
        self.resizable(tk.FALSE, tk.FALSE)
        if platform=='win32':
            self.attributes('-toolwindow', tk.TRUE)
        elif platform=='darwin':
            # http://wiki.tcl.tk/13428
            parent.call('tk::unsupported::MacWindowStyle', 'style', self, 'utility')

        frame = ttk.Frame(self)
        frame.grid(sticky=tk.NSEW)

        notebook = nb.Notebook(frame)

        page = self.addpage(notebook)
        for thing in stats[1:3]:
            self.addpagerow(page, [thing[0], self.credits(int(thing[1]))])	# assumes things two and three are money
        for thing in stats[3:]:
            self.addpagerow(page, thing)
        ttk.Frame(page).grid(pady=5)			# bottom spacer
        notebook.add(page, text=_('Status'))		# Status dialog title

        page = self.addpage(notebook, [
            _('Ship'),		# Status dialog subtitle
            '',
            _('System'),	# Main window
            _('Station'),	# Status dialog subtitle
            _('Value'),		# Status dialog subtitle - CR value of ship
        ])
        shiplist = ships(data)
        for thing in shiplist:
            self.addpagerow(page, list(thing[1:-1]) + [self.credits(int(thing[-1]))])	# skip id, last item is money
        ttk.Frame(page).grid(pady=5)			# bottom spacer
        notebook.add(page, text=_('Ships'))		# Status dialog title

        if platform!='darwin':
            buttonframe = ttk.Frame(frame)
            buttonframe.grid(padx=10, pady=(0,10), sticky=tk.NSEW)
            buttonframe.columnconfigure(0, weight=1)
            ttk.Label(buttonframe).grid(row=0, column=0)	# spacer
            ttk.Button(buttonframe, text='OK', command=self.destroy).grid(row=0, column=1, sticky=tk.E)

        # wait for window to appear on screen before calling grab_set
        self.wait_visibility()
        self.grab_set()

        # Ensure fully on-screen
        if platform == 'win32' and CalculatePopupWindowPosition:
            position = RECT()
            GetWindowRect(GetParent(self.winfo_id()), position)
            if CalculatePopupWindowPosition(POINT(parent.winfo_rootx(), parent.winfo_rooty()),
                                            SIZE(position.right - position.left, position.bottom - position.top),
                                            0x10000, None, position):
                self.geometry("+%d+%d" % (position.left, position.top))

    def addpage(self, parent, header=[], align=None):
        page = nb.Frame(parent)
        page.grid(pady=10, sticky=tk.NSEW)
        page.columnconfigure(0, weight=1)
        if header:
            self.addpageheader(page, header, align=align)
        return page

    def addpageheader(self, parent, header, align=None):
        self.addpagerow(parent, header, align=align)
        ttk.Separator(parent, orient=tk.HORIZONTAL).grid(columnspan=len(header), padx=10, pady=2, sticky=tk.EW)

    def addpagespacer(self, parent):
        self.addpagerow(parent, [''])

    def addpagerow(self, parent, content, align=None):
        for i in range(len(content)):
            label = nb.Label(parent, text=content[i])
            if i == 0:
                label.grid(padx=10, sticky=tk.W)
                row = parent.grid_size()[1]-1
            elif align is None and i == len(content) - 1:	# Assumes last column right justified if unspecified
                label.grid(row=row, column=i, padx=10, sticky=tk.E)
            else:
                label.grid(row=row, column=i, padx=10, sticky=align or tk.W)

    def credits(self, value):
        return Locale.stringFromNumber(value, 0) + ' Cr'