import numpy
from nose.tools import eq_

from helix.db.sql_manager import SQLManager
from helix.models.sql.inverter_brands import InverterBrand
from helix.models.sql.inverters import Inverter
from helix.models.sql.power_monitors import PowerMonitor
from helix.models.sql.power_stations import PowerStation
from helix.models.sql.sites import Site
from helix.models.sql.standalone_inverters import StandaloneInverter
from helix.models.sql.users import User


def test_db_session():
    session = SQLManager.get_sql_session_maker('postgres://pivotal:@localhost/test', cache=False)
    reset_db_session(session)
    return session


def reset_db_session(db_session):
    db_session.rollback()
    db_session.query(Inverter).delete()
    db_session.query(PowerMonitor).delete()
    db_session.query(StandaloneInverter).delete()
    db_session.query(InverterBrand).delete()
    db_session.query(PowerStation).delete()
    db_session.query(Site).delete()
    db_session.query(User).delete()
    db_session.commit()


def assert_dictionary_equal(received, expected):
    assert_array_equal_debug(received.keys(), expected.keys())

    for (key, value) in received.items():
        eq_(value, expected[key], "received %s for key %s, expected %s" % (str(value),
                                                                           str(key),
                                                                           str(expected[key])))


def assert_array_is_close(a, b, decimal):
    try:
        for (idx, val) in enumerate(a):
            other = b[idx]
            assert val.almost_equal(other, decimal=decimal), "Expected %s to be close to %s" % (str(val), str(other))
    except AttributeError:
        numpy.testing.assert_array_almost_equal(a, b, decimal=decimal)


def assert_array_equal_debug(received, expected):
    received = sorted(received, key=lambda x: str(x))
    expected = sorted(expected, key=lambda x: str(x))
    eq_(len(received), len(expected),
        "Arrays do not have the same amount of elements - \nexpected %s\nreceived %s" % (expected, received))
    for index, received_item in enumerate(received):
        expected_item = expected[index]
        eq_(received_item, expected_item, "Items at index %d are not equal. Expected %s, received %s" % (index,
                                                                                                         expected_item,
                                                                                                         received_item))


def assert_2d_array_equal_debug(received, expected):
    eq_(len(received), len(expected), "Arrays do not have same amount of rows")
    for row_index, received_row in enumerate(received):
        expected_row = expected[row_index]
        eq_(len(received_row), len(expected_row), "Arrays do not have same dimensions")
        for item_index, received_item in enumerate(received_row):
            expected_item = expected_row[item_index]
            eq_(expected_item, received_item, "Items at [%d, %d] are not equal. Expected %s, received %s" % (row_index,
                                                                                                             item_index,
                                                                                                             str(
                                                                                                                 expected_item),
                                                                                                             str(
                                                                                                                 received_item)))


def assert_error(browser, input_name, error_message):
    eq_(browser.find_by_css(
        '#' + input_name + '_row .error_message').first.value, error_message)


def assert_no_error(browser, input_name):
    errors = browser.find_by_css('#' + input_name + '_row .error_message')
    assert len(errors) == 0, "expected no errors, got %d errors" % len(errors)


def assert_image_equal(image_1, image_2, error=5e-2):
    # direct image comparison.
    # This is slow :/

    # first, let's get the easy ones out of the way.
    assert image_1.size == image_2.size, "Images are not the same size, got %s, expected %s" % (image_1.size, image_2.size)
    # make sure they're the same format (E.G. both have R, G, B, and A bands).
    assert image_1.getbands() == image_2.getbands(), "Images do not have the same bands, got %s, expected %s" % (image_1.getbands(), image_2.getbands())

    total_error = 0
    pixels = image_1.size[0] * image_1.size[1] * len(image_1.getbands())
    for x in range(image_1.size[0]):
        for y in range(image_1.size[1]):
            pixel_1 = image_1.getpixel((x, y))
            pixel_2 = image_2.getpixel((x, y))
            color_diffs = [(color_1 - pixel_2[idx]) for idx, color_1 in enumerate(pixel_1)]
            average_error = sum(color_diffs) / len(color_diffs)
            total_error += average_error

    average_error = total_error / pixels
    assert average_error <= error, "Images are not equal to within %f error (got %f)" % (error, average_error)
