"""Test the media accessors.

If run directly from the command line, also tests the accessors and the names
of all the media by getting just about everything in a naive brute-force way.
This, of course, takes a lot of time to run.
"""

import os
import re

from nose.tools import *
from nose.plugins.skip import SkipTest
import nose
import pkg_resources

from pokedex.db import tables, connect
from pokedex.util import media

session = connect()
basedir = pkg_resources.resource_filename('pokedex', 'data/media')

path_re = re.compile('^[-a-z0-9./]*$')

def test_totodile():
    """Totodile's female sprite -- same as male"""
    totodile = session.query(tables.Pokemon).filter_by(identifier=u'totodile').one()
    accessor = media.PokemonMedia(totodile)
    assert accessor.sprite() == accessor.sprite(female=True)

def test_chimecho():
    """Chimecho's Platinum female backsprite -- diffeent from male"""
    chimecho = session.query(tables.Pokemon).filter_by(identifier=u'chimecho').one()
    accessor = media.PokemonMedia(chimecho)
    male = accessor.sprite('platinum', back=True, frame=2)
    female = accessor.sprite('platinum', back=True, female=True, frame=2)
    assert male != female

def test_venonat():
    """Venonat's shiny Yellow sprite -- same as non-shiny"""
    venonat = session.query(tables.Pokemon).filter_by(identifier=u'venonat').one()
    accessor = media.PokemonMedia(venonat)
    assert accessor.sprite('yellow') == accessor.sprite('yellow', shiny=True)

def test_arceus_icon():
    """Arceus fire-form icon -- same as base icon"""
    arceus = session.query(tables.Pokemon).filter_by(identifier=u'arceus').one()
    accessor = media.PokemonMedia(arceus)
    fire_arceus = [f for f in arceus.forms if f.identifier == 'fire'][0]
    fire_accessor = media.PokemonFormMedia(fire_arceus)
    assert accessor.icon() == fire_accessor.icon()

@raises(ValueError)
def test_strict_castform():
    """Castform rainy form overworld with strict -- unavailable"""
    castform = session.query(tables.Pokemon).filter_by(identifier=u'castform').first()
    rainy_castform = [f for f in castform.forms if f.identifier == 'rainy'][0]
    rainy_castform = media.PokemonFormMedia(rainy_castform)
    rainy_castform.overworld('up', strict=True)

@raises(ValueError)
def test_strict_exeggcute():
    """Exeggcutes's female backsprite, with strict -- unavailable"""
    exeggcute = session.query(tables.Pokemon).filter_by(identifier=u'exeggcute').one()
    accessor = media.PokemonMedia(exeggcute)
    accessor.sprite(female=True, strict=True)



def get_all_filenames():
    print 'Reading all filenames...'

    all_filenames = set()

    for dirpath, dirnames, filenames in os.walk(basedir):
        for filename in filenames:
            path = os.path.join(dirpath, filename)
            assert path_re.match(path), path
            all_filenames.add(path)

    return all_filenames

def hit(filenames, method, *args, **kwargs):
    """
    Run the given accessor method with args & kwargs; if found remove the
    result path from filenames and return True, else return False.
    """
    try:
        medium = method(*args, **kwargs)
        #print 'Hit', medium.relative_path
        assert medium.exists
    except ValueError, e:
        #print 'DNF', e
        return False
    except:
        print 'Error while processing', method, args, kwargs
        raise
    try:
        filenames.remove(medium.path)
    except KeyError:
        pass
    return True

def check_get_everything():
    """
    For every the accessor method, loop over the Cartesian products of all
    possible values for its arguments.
    Make sure we get every file in the repo, and that we get a file whenever
    we should.

    Well, there are exceptions of course.
    """

    versions = list(session.query(tables.Version).all())
    versions.append('red-green')

    black = session.query(tables.Version).filter_by(identifier=u'black').one()

    filenames = get_all_filenames()

    # Some small stuff first

    for damage_class in session.query(tables.MoveDamageClass).all():
        assert hit(filenames, media.DamageClassMedia(damage_class).icon)

    for habitat in session.query(tables.PokemonHabitat).all():
        assert hit(filenames, media.HabitatMedia(habitat).icon)

    for shape in session.query(tables.PokemonShape).all():
        assert hit(filenames, media.ShapeMedia(shape).icon)

    for item_pocket in session.query(tables.ItemPocket).all():
        assert hit(filenames, media.ItemPocketMedia(item_pocket).icon)
        assert hit(filenames, media.ItemPocketMedia(item_pocket).icon, selected=True)

    for contest_type in session.query(tables.ContestType).all():
        assert hit(filenames, media.ContestTypeMedia(contest_type).icon)

    for elemental_type in session.query(tables.Type).all():
        assert hit(filenames, media.TypeMedia(elemental_type).icon)

    # Items
    versions_for_items = [
            None,
            session.query(tables.Version).filter_by(identifier='emerald').one(),
        ]

    for item in session.query(tables.Item).all():
        accessor = media.ItemMedia(item)
        assert hit(filenames, accessor.berry_image) or not item.berry
        for rotation in (0, 90, 180, 270):
            assert hit(filenames, accessor.underground, rotation=rotation) or (
                    not item.appears_underground or rotation)
        for version in versions_for_items:
            success = hit(filenames, accessor.sprite, version=version)
            if version is None:
                assert success

    for color in 'red green blue pale prism'.split():
        for big in (True, False):
            accessor = media.UndergroundSphereMedia(color=color, big=big)
            assert hit(filenames, accessor.underground)

    for rock_type in 'i ii o o-big s t z'.split():
        accessor = media.UndergroundRockMedia(rock_type)
        for rotation in (0, 90, 180, 270):
            success = hit(filenames, accessor.underground, rotation=rotation)
            assert success or rotation

    # Pokemon!
    accessors = []

    accessors.append(media.UnknownPokemonMedia())
    accessors.append(media.EggMedia())
    manaphy = session.query(tables.Pokemon).filter_by(identifier=u'manaphy').one()
    accessors.append(media.EggMedia(manaphy))
    accessors.append(media.SubstituteMedia())

    print 'Loading pokemon'

    for form in session.query(tables.PokemonForm).filter(tables.PokemonForm.identifier != '').all():
        accessors.append(media.PokemonFormMedia(form))

    for pokemon in session.query(tables.Pokemon).all():
        accessors.append(media.PokemonMedia(pokemon))

    for accessor in accessors:
        assert hit(filenames, accessor.footprint) or not accessor.form
        assert hit(filenames, accessor.trozei) or not accessor.form or (
                accessor.form.pokemon.generation.id > 3)
        assert hit(filenames, accessor.cry) or not accessor.form
        assert hit(filenames, accessor.cropped_sprite) or not accessor.form
        for female in (True, False):
            assert hit(filenames, accessor.icon, female=female) or not accessor.form
            assert hit(filenames, accessor.sugimori, female=female) or (
                    not accessor.form or accessor.form.pokemon.id >= 647)
            for shiny in (True, False):
                for frame in (1, 2):
                    for direction in 'up down left right'.split():
                        assert hit(filenames, accessor.overworld,
                                direction=direction,
                                shiny=shiny,
                                female=female,
                                frame=frame,
                            ) or not accessor.form or (
                                    accessor.form.pokemon.generation.id > 4)
                    for version in versions:
                        for animated in (True, False):
                            for back in (True, False):
                                for color in (None, 'gray', 'gbc'):
                                    success = hit(filenames,
                                            accessor.sprite,
                                            version,
                                            animated=animated,
                                            back=back,
                                            color=color,
                                            shiny=shiny,
                                            female=female,
                                            frame=frame,
                                        )
                                    if (version == black and not animated
                                        and not back and not color and not
                                        shiny and not female and
                                        frame == 1):
                                        # All pokemon are in Black
                                        assert success or not accessor.form
                                    if (str(accessor.pokemon_id) == '1'
                                        and not animated and not color and
                                        frame == 1):
                                        # Bulbasaur is in all versions
                                        assert success

    # Remove exceptions
    exceptions = [os.path.join(basedir, dirname) for dirname in
            'chrome fonts ribbons'.split()]
    exceptions.append(os.path.join(basedir, 'items', 'hm-'))
    exceptions = tuple(exceptions)

    for filename in tuple(filenames):
        if filename.startswith(exceptions):
            filenames.remove(filename)

    if len(filenames):
        print
        print '-----------------'
        print 'Unaccessed stuff:'
        for filename in sorted(filenames):
            print filename
        print len(filenames), 'unaccessed files :('

    return (not filenames)

if __name__ == '__main__':
    result = nose.run(defaultTest=__file__)
    result = result and check_get_everything()
    exit(not result)