# coding: utf-8

from collections import namedtuple


class PartialTranslationError(Exception):
    pass


class _DbCategoryTranslator:
    def __init__(self, category_mapping={}, rel_role_updates={}):
        self.category_mapping = category_mapping
        self.rel_role_updates = rel_role_updates

    def convert(self, conn):
        """
        Осуществляет преобразование категоpий в данных TDS, доступных через
        соединение conn.
        """
        attr_cursor = conn.cursor()
        upd_cursor = conn.cursor()
        attr_cursor.execute(
            "SELECT id, hstore_to_matrix(contents) "
            "FROM revision.attributes")
        for attr_id, attr_contents in attr_cursor:
            attr_dict = dict(attr_contents)

            new_attr_contents = None
            if 'rel:role' in attr_dict:
                # this is a relation, map its attributes
                if attr_dict['rel:master'] in self.category_mapping:
                    new_master = self.category_mapping[attr_dict['rel:master']]
                    new_slave = self.category_mapping.get(
                        attr_dict['rel:slave'], attr_dict['rel:slave'])

                    new_attr_contents = [
                        ['rel:master', new_master],
                        ['rel:slave', new_slave],
                        ['rel:role', self.rel_role_updates.get(
                            (new_master, new_slave),
                            attr_dict['rel:role'])]
                    ]
                elif attr_dict['rel:slave'] in self.category_mapping:
                    raise PartialTranslationError(
                        'Conversion breaks relation: %s' % attr_contents)
            else:
                # this is an object, map its category
                for old_cat, new_cat in self.category_mapping.iteritems():
                    if 'cat:' + old_cat in attr_dict:
                        new_attr_contents = (
                            [['cat:' + new_cat, '1']]
                            + [[k.replace(old_cat, new_cat, 1), v]
                               for k, v in attr_dict.iteritems()
                               if not k.startswith('cat:')])

            if new_attr_contents is not None:
                upd_cursor.execute(
                    "UPDATE revision.attributes "
                    "SET contents = hstore(%(new_attr_contents)s) "
                    "WHERE id = %(attr_id)s;", locals())
        upd_cursor.execute(
            "DELETE FROM attributes_relations;"
            "INSERT INTO attributes_relations "
            "   SELECT * FROM attributes "
            "   WHERE id IN (SELECT attributes_id "
            "    FROM object_revision_relation);")

        conn.commit()


TopologyGroup = namedtuple('TopologyGroup',
                             'compound face element junction name')


RD_CLASS = TopologyGroup('rd', None, 'rd_el', 'rd_jc', 'rd_nm')
METRO_CLASS = TopologyGroup(
    'transport_metro_line', None,
    'transport_metro_el', 'transport_metro_jc', 'transport_metro_nm')
RAILWAY_CLASS = TopologyGroup(
    'transport_railway', None,
    'transport_railway_el', 'transport_railway_jc', 'transport_railway_nm')
TRAM_CLASS = TopologyGroup(
    'transport_tram_route', None,
    'transport_tram_el', 'transport_tram_jc', 'transport_tram_nm')
WATERWAY_CLASS = TopologyGroup(
    'transport_waterway_route', None,
    'transport_waterway_el', 'transport_waterway_jc', 'transport_waterway_nm')
HYDRO_LN_CLASS = TopologyGroup(
    'hydro_ln', None,
    'hydro_ln_el', 'hydro_ln_jc', 'hydro_nm')

AD_CLASS = TopologyGroup('ad', 'ad_fc', 'ad_el', 'ad_jc', 'ad_nm')
HYDRO_FC_CLASS = TopologyGroup(
    'hydro', 'hydro_fc',
    'hydro_fc_el', 'hydro_fc_jc', 'hydro_nm')
RELIEF_CLASS = TopologyGroup(
    'relief', 'relief_fc',
    'relief_el', 'relief_jc', 'relief_nm')
URBAN_CLASS = TopologyGroup(
    'urban', 'urban_fc',
    'urban_el', 'urban_jc', 'urban_nm')
URBAN_ROADNET_CLASS = TopologyGroup(
    'urban_roadnet', 'urban_roadnet_fc',
    'urban_roadnet_el', 'urban_roadnet_jc', 'urban_roadnet_nm')
VEGETATION_CLASS = TopologyGroup(
    'vegetation', 'vegetation_fc',
    'vegetation_el', 'vegetation_jc', 'vegetation_nm')
AD_NEUTRAL_CLASS = TopologyGroup(
    'ad_neutral', 'ad_neutral_fc',
    'ad_neutral_el', 'ad_neutral_jc', 'ad_nm')


def map_topology_categories(group_from, group_to):
    assert isinstance(group_from, TopologyGroup)
    assert isinstance(group_to, TopologyGroup)

    # filter out invalid mappings
    return dict(filter(all, zip(group_from, group_to)))

RD_TO_AD = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, AD_CLASS))
RD_TO_METRO = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, METRO_CLASS))
RD_TO_RAILWAY = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, RAILWAY_CLASS))
RD_TO_TRAM = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, TRAM_CLASS))
RD_TO_WATERWAY = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, WATERWAY_CLASS))
RD_TO_HYDRO_LN = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, HYDRO_LN_CLASS),
    {(HYDRO_LN_CLASS.compound, HYDRO_LN_CLASS.element): 'ln_part'})
RD_TO_HYDRO_FC = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, HYDRO_FC_CLASS))
RD_TO_RELIEF = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, RELIEF_CLASS))
RD_TO_URBAN = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, URBAN_CLASS))
RD_TO_URBAN_ROADNET = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, URBAN_ROADNET_CLASS))
RD_TO_VEGETATION = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, VEGETATION_CLASS))
RD_TO_AD_NEUTRAL = _DbCategoryTranslator(
    map_topology_categories(RD_CLASS, AD_NEUTRAL_CLASS))

AD_TO_HYDRO = _DbCategoryTranslator(
    map_topology_categories(AD_CLASS, HYDRO_FC_CLASS),
    {(HYDRO_FC_CLASS.compound, HYDRO_FC_CLASS.face): 'fc_part'})
AD_TO_RELIEF = _DbCategoryTranslator(
    map_topology_categories(AD_CLASS, RELIEF_CLASS))
AD_TO_URBAN = _DbCategoryTranslator(
    map_topology_categories(AD_CLASS, URBAN_CLASS))
AD_TO_URBAN_ROADNET = _DbCategoryTranslator(
    map_topology_categories(AD_CLASS, URBAN_ROADNET_CLASS))
AD_TO_VEGETATION = _DbCategoryTranslator(
    map_topology_categories(AD_CLASS, VEGETATION_CLASS))
AD_TO_AD_NEUTRAL = _DbCategoryTranslator(
    map_topology_categories(AD_CLASS, AD_NEUTRAL_CLASS))

URBAN_TO_HYDRO = _DbCategoryTranslator(
    map_topology_categories(URBAN_CLASS, HYDRO_FC_CLASS))
URBAN_TO_RELIEF = _DbCategoryTranslator(
    map_topology_categories(URBAN_CLASS, RELIEF_CLASS))
URBAN_TO_URBAN_ROADNET = _DbCategoryTranslator(
    map_topology_categories(URBAN_CLASS, URBAN_ROADNET_CLASS))
URBAN_TO_VEGETATION = _DbCategoryTranslator(
    map_topology_categories(URBAN_CLASS, VEGETATION_CLASS))

FEATURE_TO_BLD = _DbCategoryTranslator(
    {
        'polygon_feature': 'bld'
    }
)

FEATURE_TO_RELIEF = _DbCategoryTranslator(
    {
        'contour_feature': 'relief',
        'point_feature': 'relief_point',
        'face': 'relief_fc',
        'edge_fc': 'relief_el',
        'junction_fc': 'relief_jc',
        'feature_name': 'relief_nm'
    }
)

FEATURE_TO_TRANSPORT_AIRPORT = _DbCategoryTranslator(
    {
        'point_feature': 'transport_airport',
        'feature_name': 'transport_airport_nm'
    }
)

FEATURE_TO_TRANSPORT_AIRPORT_TERMINAL = _DbCategoryTranslator(
    {
        'point_feature': 'transport_airport_terminal',
        'feature_name': 'transport_airport_nm'
    }
)

FEATURE_TO_TRANSPORT_HELICOPTER = _DbCategoryTranslator(
    {
        'point_feature': 'transport_helicopter',
        'feature_name': 'transport_airport_nm'
    }
)

FEATURE_TO_TRANSPORT_STOP = _DbCategoryTranslator(
    {
        'point_feature': 'transport_stop',
        'feature_name': 'transport_nm'
    }
)

FEATURE_TO_TRANSPORT_TERMINAL = _DbCategoryTranslator(
    {
        'point_feature': 'transport_terminal',
        'feature_name': 'transport_nm'
    }
)

FEATURE_TO_TRANSPORT_TRAM = _DbCategoryTranslator(
    {
        'linear_feature': 'transport_tram_route',
        'edge_ln': 'transport_tram_el',
        'junction_ln': 'transport_tram_jc',
        'feature_name': 'transport_nm'
    }
)

FEATURE_TO_TRANSPORT_WATERWAY = _DbCategoryTranslator(
    {
        'point_feature': 'transport_waterway_stop',
        'linear_feature': 'transport_waterway_route',
        'edge_ln': 'transport_waterway_el',
        'junction_ln': 'transport_waterway_jc',
        'feature_name': 'transport_waterway_nm'
    }
)

FEATURE_TO_VEGETATION = _DbCategoryTranslator(
    {
        'contour_feature': 'vegetation',
        'face': 'vegetation_fc',
        'edge_fc': 'vegetation_el',
        'junction_fc': 'vegetation_jc',
        'feature_name': 'vegetation_nm'
    }
)

FEATURE_TO_TRANSPORT_RAILWAY = _DbCategoryTranslator(
    {
        'point_feature': 'transport_railway_station',
        'polygon_feature': 'transport_railway_platform',
        'linear_feature': 'transport_railway',
        'edge_ln': 'transport_railway_el',
        'junction_ln': 'transport_railway_jc',
        'feature_name': 'transport_railway_nm'
    }
)

FEATURE_TO_HYDRO = _DbCategoryTranslator(
    {
        'point_feature': 'hydro_point',
        'contour_feature': 'hydro',
        'face': 'hydro_fc',
        'edge_fc': 'hydro_fc_el',
        'junction_fc': 'hydro_ln_fc',
        'linear_feature': 'hydro_ln',
        'edge_ln': 'hydro_ln_el',
        'junction_ln': 'hydro_ln_jc',
        'feature_name': 'hydro_nm'
    }
)

FEATURE_TO_URBAN = _DbCategoryTranslator(
    {
        'contour_feature': 'urban',
        'polygon_feature': 'urban_areal',
        'face': 'urban_fc',
        'edge_fc': 'urban_el',
        'junction_fc': 'urban_jc',
        'feature_name': 'urban_nm'
    }
)

FEATURE_TO_URBAN_ROADNET = _DbCategoryTranslator(
    {
        'contour_feature': 'urban_roadnet',
        'polygon_feature': 'urban_roadnet_areal',
        'point_feature': 'urban_roadnet_parking_lot',
        'face': 'urban_roadnet_fc',
        'edge_fc': 'urban_roadnet_el',
        'junction_fc': 'urban_roadnet_jc',
        'feature_name': 'urban_roadnet_nm'
    }
)

FEATURE_TO_AD_NEUTRAL = _DbCategoryTranslator(
    {
        'contour_feature': 'ad_neutral',
        'face': 'ad_neutral_fc',
        'edge_fc': 'ad_neutral_el',
        'junction_fc': 'ad_neutral_jc',
        'feature_name': 'ad_nm'
    }
)

FEATURE_TO_REGION = _DbCategoryTranslator(
    {
        'point_feature': 'region',
        'feature_name': 'ad_nm'
    }
)

BUS_TO_TRAM = _DbCategoryTranslator(
    {
        'transport_operator': 'transport_operator',
        'transport_bus_route': 'transport_tram_route',
        'transport_bus_thread': 'transport_tram_thread',
        'rd_el': 'transport_tram_el',
        'rd_jc': 'transport_tram_jc',
    },
    {
        ('transport_tram_thread', 'transport_tram_el'): 'tram_part',
        ('transport_operator', 'transport_tram_route'): 'assigned_tram'
    }
)

BUS_TO_WATERWAY = _DbCategoryTranslator(
    {
        'transport_operator': 'transport_operator',
        'transport_bus_route': 'transport_waterway_route',
        'transport_bus_thread': 'transport_waterway_thread',
        'rd_el': 'transport_waterway_el',
        'rd_jc': 'transport_waterway_jc',
    },
    {
        ('transport_waterway_thread', 'transport_waterway_el'): 'waterway_part',
        ('transport_operator', 'transport_waterway_route'): 'assigned_waterway'
    }
)
