#! /usr/bin/python
# coding: utf-8
"""
Download and parse XML from CentrBank
Generate folder with json for every currency with its rate
"""

import unittest
import sys
import os
import simplejson
from lxml import etree as ET


CENTRAL_BANK_RATES_URL = 'http://www.cbr.ru/scripts/XML_daily.asp'


def calc_cross_course(from_value, from_nominal,
                      to_value, to_nominal):
    """Считаем кросскурс одной валюты, относительно другой.
    На выходе tuple в виде [crosscurs, nominal]."""

    value = float(from_value) / to_value
    nominal = float(from_nominal) / to_nominal

    # возвращаем курс для 1 единицы исходной валюты
    return value / nominal


def find_cross_course(geodata, rates, from_geoid, to_geoid):
    """Ищем кросс курс для двух стран."""
    from_code = geodata.get(from_geoid)
    to_code = geodata.get(to_geoid)
    from_rate = rates.get(from_code)
    to_rate = rates.get(to_code)

    if from_rate and to_rate:
        cross_course = calc_cross_course(*(from_rate + to_rate))
        if cross_course >= 1:
            return [from_code, to_code, cross_course]
        else:
            return [to_code, from_code, calc_cross_course(*(to_rate + from_rate))]

    return [from_code, to_code, None]


def gen_crosscourses(geodata, rates):
    """Возвращает генератор кросскурсов для всех сочетаний geoid.
    """
    for geoid, value in geodata.iteritems():
        for other_geoid in geodata:
            if other_geoid != geoid:
                yield [geoid, other_geoid] + find_cross_course(geodata, rates, geoid, other_geoid)


def load_geodata(filename='geo.data'):
    """Загружает данные из файлика и возвращает словарь geoid -> 'код валюты'.
    """
    f = open(filename)
    try:
        data = (line.strip().split(None, 1) for line in f)
        data = ((int(key), value) for key, value in data)
        return dict(data)
    finally:
        f.close()


def get_rates():
    """Берет курсы из центробанка и возвращает словарик: 'код валюты' -> (курс, номинал)
    """
    data = ET.parse(CENTRAL_BANK_RATES_URL)

    data = data.iterfind('Valute')
    data = (
        (item.find('CharCode').text,
         (float(item.find('Value').text.replace(',', '.')),
          int(item.find('Nominal').text)))
        for item in data)
    data = dict(data)
    # в данных центробанка нет курса рубля к рублю
    data['RUB'] = (1.0, 1)
    return data


def dump_data(crosscourses, basedir):
    """Сохраняет данные в файлы, создавая следующую структуру, относительно basedir:
       basedir/from_geoid/to_geoid
    """
    for item in crosscourses:
        from_geoid, to_geoid = item[:2]
        data = item[2:]

        dirname = os.path.join(basedir, str(from_geoid))
        if not os.path.exists(dirname):
            os.makedirs(dirname)

        f = open(os.path.join(dirname, str(to_geoid)), 'w')
        try:
            f.write(simplejson.dumps(data))
        finally:
            f.close()


class TestCase(unittest.TestCase):
    geodata = {1: 'ONE',
               3: 'THR',
               5: 'FIV',
               7: 'ONE',}
    rates = {'ONE': (40, 1), 'THR': (15, 100)}

    def test_calc_cross_course(self):
        self.assertEqual(
            0.047846059118171735,
            calc_cross_course(
                36.2887, 10000,
                75.8447, 1000))
        self.assertEqual(
            0.024411082631514706,
            calc_cross_course(
                1.0, 1.0,
                40.9650, 1.0))

    def test_find_cross_course(self):
        self.assertEqual(['ONE', 'THR', 266.66666666666663],
                         find_cross_course(self.geodata, self.rates, 1, 3))
        self.assertEqual(['ONE', 'FIV', None],
                         find_cross_course(self.geodata, self.rates, 1, 5))
        self.assertEqual(['ONE', 'ONE', 1.0],
                         find_cross_course(self.geodata, self.rates, 1, 7))
        self.assertEqual(['ONE', None, None],
                         find_cross_course(self.geodata, self.rates, 1, 9))

        self.assertEqual(['ONE', 'THR', 266.66666666666663],
                         find_cross_course(self.geodata, self.rates, 3, 7))

    def test_gen_crosscourses(self):
        self.assertEqual(
            [[1, 3, 'ONE', 'THR', 266.66666666666663],
             [1, 5, 'ONE', 'FIV', None],
             [1, 7, 'ONE', 'ONE', 1.0],
             [3, 1, 'ONE', 'THR', 266.66666666666663],
             [3, 5, 'THR', 'FIV', None],
             [3, 7, 'ONE', 'THR', 266.66666666666663],
             [5, 1, 'FIV', 'ONE', None],
             [5, 3, 'FIV', 'THR', None],
             [5, 7, 'FIV', 'ONE', None],
             [7, 1, 'ONE', 'ONE', 1.0],
             [7, 3, 'ONE', 'THR', 266.66666666666663],
             [7, 5, 'ONE', 'FIV', None]],
            list(gen_crosscourses(self.geodata, self.rates)))

    def test_extrime_crosscourse(self):
        rates = self.rates.copy()
        rates['ONE'] = (40, 0.01)

        self.assertEqual(['ONE', 'ONE', 1.0],
                         find_cross_course(self.geodata, rates, 1, 7))

        crosscourse = find_cross_course(self.geodata, rates, 1, 3)
        crosscourse[2] = round(crosscourse[2], 3)
        self.assertEqual(['ONE', 'THR', round(26666.66666666, 3)], crosscourse)

        crosscourse = find_cross_course(self.geodata, rates, 3, 1)
        crosscourse[2] = round(crosscourse[2], 3)
        self.assertEqual(['ONE', 'THR', round(26666.66666666, 3)], crosscourse)


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print 'Usage: {0} <path-to-store-datafiles>'.format(sys.argv[0])
        sys.exit(1)

    if sys.argv[1] == 'selftest':
        unittest.main(argv=sys.argv[:1])
    else:
        geodata = load_geodata(os.path.join(os.path.dirname(__file__),
                                            'geo.data'))
        rates = get_rates()

        crosscourses = gen_crosscourses(geodata, rates)

        dump_data(crosscourses, sys.argv[1])

