#!/usr/bin/python

from __future__ import print_function

import argparse
import json
import math
import sys
import urllib

import yt.yson as yson
import yandex.maps.geolib3 as geolib


def view(view_func):
    def decorated(obj):
        return yson.dumps(yson.to_yson_type(view_func(obj)))

    return decorated


def create_point(obj):
    coords = obj['geometry']['coordinates']

    return {'lon': coords[0], 'lat': coords[1]}


@view
def hypothesis_view(hypothesis):
    view = create_point(hypothesis)
    view['_pointstyle'] = 'rgba(100,0,0,150):rgba(100,0,0,150):32'

    for key, value in hypothesis.iteritems():
        if key in ('id', 'geometry'):
            continue

        if key == 'objectId':
            view[key] = prepare_object_url(value)
        else:
            view[key] = value

    return view


FEATURE_URL_PATTERN = '<a target=\'_blank\' href=\'{url}?boxes={boxes}\'>{feature_id}</a>'


def prepare_feature_url(feature_id, url, boxes):
    boxes_str = ';'.join(
        ','.join(map(str, box)) for box in boxes
    )

    return FEATURE_URL_PATTERN.format(
        feature_id=feature_id,
        url=url,
        boxes=urllib.quote(boxes_str)
    )

OBJECT_URL_PATTERN = '<a target=\'_blank\' href=\'https://n.maps.yandex.ru/#!/objects/{object_id}\'>{object_id}</a>'


def prepare_object_url(object_id):
    return OBJECT_URL_PATTERN.format(
        object_id=object_id
    )


@view
def feature_view(feature):
    view = create_point(feature)

    view['_pointstyle'] = 'black:black:3'
    view['id'] = prepare_feature_url(feature['id'], feature['url'], feature['boxes'])
    view['timestamp'] = short_timestamp(feature['timestamp'])

    return view


def short_timestamp(timestamp):
    return timestamp.split('.')[0]


def prepare_features(signs):
    id_to_feature = {}

    for sign in signs:
        features = sign['features']

        for feature in features:
            feature_id = feature['id']
            if feature_id not in id_to_feature:
                id_to_feature[feature_id] = {
                    'id': feature_id,
                    'geometry': feature['geometry'],
                    'url': feature['imageFull']['url'],
                    'timestamp': feature['timestamp'],
                    'boxes': [],
                }

            id_to_feature[feature_id]['boxes'].append(
                feature['box']
            )

    return id_to_feature.values()


@view
def sign_view(sign):
    view = create_point(sign)
    view['_pointstyle'] = 'white:red:8'

    for key, value in sign.iteritems():
        if key in ('geometry', 'features'):
            continue

        view[key] = value

    return view


@view
def sign_head_view(sign):
    coords = sign['geometry']['coordinates']
    start = geolib.Point2(coords[0], coords[1])

    end = geolib.geo_to_mercator(start)

    angle = math.pi / 2 - sign['heading'] / 180 * math.pi
    end = geolib.Point2(end.x + 7 * math.cos(angle), end.y + 7 * math.sin(angle))

    end = geolib.mercator_to_geo(end)

    return {
        'start_lon': start.x,
        'start_lat': start.y,
        'end_lon': end.x,
        'end_lat': end.y,
        '_linestyle': 'blue:4'
    }


def prepare_lines(signs):
    lines = []

    for sign in signs:
        sign_coords = sign['geometry']['coordinates']

        for feature in sign['features']:
            lines.append(
                {
                    'start': sign_coords,
                    'end': feature['geometry']['coordinates'],
                }
            )

    return lines


@view
def line_view(line):
    return {
        'start_lon': line['start'][0],
        'start_lat': line['start'][1],
        'end_lon': line['end'][0],
        'end_lat': line['end'][1],
        '_linestyle': 'black:1'
    }


def mercator_distance_ratio(geopoint):
    return 1.0 / math.cos(math.pi * (geopoint.y / 180.0))


@view
def box_view(sign):

    center = sign['geometry']['coordinates']

    def move(geo_point, dx, dy):
        geo_point = geolib.Point2(geo_point[0], geo_point[1])
        ratio = mercator_distance_ratio(geo_point)

        mercator_point = geolib.geo_to_mercator(geo_point)
        mercator_point = geolib.Point2(
            mercator_point.x + ratio * dx,
            mercator_point.y + ratio * dy
        )

        geo_point = geolib.mercator_to_geo(mercator_point)

        return [geo_point.x, geo_point.y]

    d = 30

    return {
        'polyline': [
            move(center, d, d),
            move(center, -d, d),
            move(center, -d, -d),
            move(center, d, -d),
            move(center, d, d),
        ],
        '_linestyle': 'black:3'
    }


def generate(lines):
    dump = json.loads(lines.read())

    for hypothesis in dump["hypotheses"]:
        print(hypothesis_view(hypothesis))

    for sign in dump['signs']:
        print(box_view(sign))

    for line in prepare_lines(dump['signs']):  # from feature to sign
        print(line_view(line))

    for feature in prepare_features(dump['signs']):
        print(feature_view(feature))

    for sign in dump['signs']:
        print(sign_head_view(sign))

    for sign in dump['signs']:
        print(sign_view(sign))


def parse_args():
    parser = argparse.ArgumentParser(
        description='Generate view of hypotheses from json dump',
    )

    parser.add_argument(
        '-I', '--input',
        type=argparse.FileType('r'),
        default=sys.stdin,
        help='input json dump file'
    )

    parser.add_argument(
        '-O', '--output',
        type=argparse.FileType('w'),
        default=sys.stdout,
        help='output .yson file'
    )

    return parser.parse_args()


def main():
    args = parse_args()

    sys.stdout = args.output
    generate(args.input)

if __name__ == "__main__":
    main()
