import freeorion as fo

import natives
import planets
import universe_tables
from common.print_utils import Number, Sequence, Table, Text

species_summary = {species: 0 for species in fo.get_native_species()}
empire_species = {species: 0 for species in fo.get_playable_species()}
potential_native_planet_summary = {planet_type: 0 for planet_type in planets.planet_types}
settled_native_planet_summary = {planet_type: 0 for planet_type in planets.planet_types}
monsters_summary = []
tracked_monsters_chance = {}
tracked_monsters_tries = {}
tracked_monsters_summary = {}
tracked_monsters_location_summary = {}
tracked_nest_location_summary = {}
specials_summary = {special: 0 for special in fo.get_all_specials()}
specials_repeat_dist = {count: 0 for count in [0, 1, 2, 3, 4]}


def log_planet_count_dist(sys_list):
    planet_count_dist = {}
    planet_size_dist = {size: 0 for size in planets.planet_sizes}
    for system in sys_list:
        planet_count = 0
        for planet in fo.sys_get_planets(system):
            this_size = fo.planet_get_size(planet)
            if this_size in planets.planet_sizes:
                planet_count += 1
                planet_size_dist[this_size] += 1
        planet_count_dist.setdefault(planet_count, [0])[0] += 1
    planet_tally = sum(planet_size_dist.values())

    count_distribution_table = Table(
        Text("planets in system"),
        Text("num of systems"),
        Number("% of systems", precession=1),
        table_name="Planet Count Distribution",
    )
    for planet_count, sys_count in planet_count_dist.items():
        count_distribution_table.add_row(
            planet_count,
            sys_count[0],
            100.0 * sys_count[0] / len(sys_list),
        )
    print(count_distribution_table)
    print()

    size_distribution = Table(
        Text("size"),
        Text("count"),
        Number("% of planets", precession=1),
        table_name="Planet Size Distribution",
    )
    for planet_size, planet_count in sorted(planet_size_dist.items()):
        size_distribution.add_row(
            planet_size,
            planet_count,
            100.0 * planet_count / planet_tally,
        )
    print(size_distribution)
    print()


def log_planet_type_summary(sys_list):
    planet_type_summary_table = {k: 0 for k in planets.planet_types}
    for system in sys_list:
        for planet in fo.sys_get_planets(system):
            planet_type_summary_table[fo.planet_get_type(planet)] += 1
    planet_total = sum(planet_type_summary_table.values())

    type_summary_table = Table(
        Text("planet type", align="<"),
        Number("% of planets", precession=1),
        table_name="Planet Type Summary for a total of %d placed planets" % planet_total,
    )

    for planet_type, planet_count in sorted(planet_type_summary_table.items()):
        type_summary_table.add_row(
            planet_type.name,
            100.0 * planet_count / planet_total,
        )
    print(type_summary_table)
    print()


def log_species_summary(native_freq):
    num_empires = sum(empire_species.values())
    num_species = len(fo.get_playable_species())
    exp_count = num_empires // num_species

    species_summary_table = Table(
        Text("species"),
        Text("count"),
        Number("%", precession=1),
        table_name=(
            "Empire Starting Species Summary\n"
            "Approximately %d to %d empires expected per species\n"
            "%d Empires and %d playable species"
        )
        % (max(0, exp_count - 1), exp_count + 1, num_empires, num_species),
    )

    for species, count in sorted(empire_species.items()):
        species_summary_table.add_row(
            species,
            count,
            100.0 * count / num_empires,
        )
    print(species_summary_table)
    print()

    native_chance = universe_tables.NATIVE_FREQUENCY[native_freq]
    # as the value in the universe table is higher for a lower frequency, we have to invert it
    # a value of 0 means no natives, in this case return immediately
    if native_chance <= 0:
        return
    native_table = Table(
        Text("settled natives"),
        Text("on planets"),
        Number("total", precession=1),
        Number("actual", precession=1),
        Number("expected", precession=1),
        Sequence("planet types"),
        table_name="Natives Placement Summary (native frequency: %.1f%%)" % (100 * native_chance),
    )

    native_potential_planet_total = sum(potential_native_planet_summary.values())
    for species in sorted(species_summary):
        if species_summary[species] > 0:
            settleable_planets = 0
            expectation_tally = 0.0
            for p_type in natives.planet_types_for_natives[species]:
                settleable_planets += potential_native_planet_summary[p_type]
                expectation_tally += (
                    native_chance
                    * 100.0
                    * potential_native_planet_summary[p_type]
                    / (1e-10 + len(natives.natives_for_planet_type[p_type]))
                )
            expectation = expectation_tally / (1e-10 + settleable_planets)
            native_table.add_row(
                species,
                species_summary[species],
                100.0 * species_summary[species] / (1e-10 + native_potential_planet_total),
                100.0 * species_summary[species] / (1e-10 + settleable_planets),
                expectation,
                [str(p_t) for p_t in natives.planet_types_for_natives[species]],
            )

    print(native_table)
    print()

    native_settled_planet_total = sum(settled_native_planet_summary.values())
    type_summary_table = Table(
        Text("planet type"),
        Number("potential (% of tot)", precession=1),
        Number("settled (% of potential)", precession=1),
        table_name=(
            "Planet Type Summary for Native Planets (native frequency: {:.1f}%)\n"
            "Totals: native_potential_planet_total: {}; native_settled_planet_total {}"
        ).format(100 * native_chance, native_potential_planet_total, native_settled_planet_total),
    )

    for planet_type, planet_count in sorted(potential_native_planet_summary.items()):
        settled_planet_count = settled_native_planet_summary.get(planet_type, 0)
        potential_percent = 100.0 * planet_count / (1e-10 + native_potential_planet_total)
        settled_percent = 100.0 * settled_planet_count / (1e-10 + planet_count)
        type_summary_table.add_row(
            planet_type.name,
            potential_percent,
            settled_percent,
        )
    print(type_summary_table)
    print()


def log_monsters_summary(monster_freq):
    monster_place_table = Table(
        Text("monster"),
        Text("count"),
        table_name="Monster placement",
    )
    for monster, counter in sorted(monsters_summary):
        monster_place_table.add_row(monster, counter)
    print(monster_place_table)
    print()

    monster_chance = universe_tables.MONSTER_FREQUENCY[monster_freq]
    monster_table = Table(
        Text("monster"),
        Number("chance"),
        Text("attempts"),
        Text("number placed"),
        Text("number valid sys locs"),
        Text("number valid nest planet locs"),
        table_name=(
            "Space Monsters Placement Summary\n"
            "Tracked Monster and Nest Summary (base monster freq: %4.1f%%)" % (100 * monster_chance)
        ),
    )
    for monster in sorted(tracked_monsters_summary):
        monster_table.add_row(
            monster,
            tracked_monsters_chance[monster],
            tracked_monsters_tries[monster],
            tracked_monsters_summary[monster],
            tracked_monsters_location_summary[monster],
            tracked_nest_location_summary[monster],
        )
    print(monster_table)
    print()


def log_specials_summary():
    special_placement_count_table = Table(
        Text("special"),
        Text("times placed"),
        table_name="Special Placement Summary",
    )
    for special in sorted(specials_summary):
        special_placement_count_table.add_row(special, specials_summary[special])
    print(special_placement_count_table)
    print()

    special_placement = Table(
        Text("count"),
        Text("tally"),
        Number("% of objects", precession=1),
        table_name="Specials Count(Repeat) Distribution",
    )
    objects_tally = sum(specials_repeat_dist.values())
    for number, tally in specials_repeat_dist.items():
        special_placement.add_row(
            number,
            tally,
            100.0 * tally / (1e-10 + objects_tally),
        )
    print(special_placement)
    print()


def log_systems():
    universe = fo.get_universe()

    systems_table = Table(
        Text("id"),
        Text("name"),
        Sequence("planets"),
        Sequence("connections"),
        Text("star"),
        table_name="System summary",
    )
    for sid in fo.get_systems():
        system = universe.getSystem(sid)
        systems_table.add_row(
            sid,
            system.name,
            fo.sys_get_planets(sid),
            fo.sys_get_starlanes(sid),
            system.starType.name,
        )
    print(systems_table)


def log_planets():
    universe = fo.get_universe()
    planets_table = Table(
        Text("id"),
        Text("name"),
        Text("system"),
        Text("type"),
        Sequence("specials"),
        Text("species"),
        Sequence("buildings"),
        table_name="Planets summary",
    )
    # group planets by system
    for sid in fo.get_systems():
        for pid in fo.sys_get_planets(sid):
            planet = universe.getPlanet(pid)

            planet_type = fo.planet_get_type(pid).name
            planet_size = fo.planet_get_size(pid).name
            if planet_type != planet_size:
                planet_type = f"{planet_type} {planet_size}"

            buildings = [universe.getBuilding(x).name for x in planet.buildingIDs]
            planets_table.add_row(
                pid,
                planet.name,
                planet.systemID,
                planet_type,
                list(planet.specials),
                planet.speciesName,
                buildings,
            )

    print(planets_table)
