# -*- coding: utf-8 -*-

import logging
import traceback
import hashlib
from google.protobuf.internal.encoder import _VarintBytes as Encoder

import rtyserver_pb2 as rtyserver
import dispatcher_feeder_pb2 as dispatcher
import errors
import utils

MESSAGE_ID = 1

log = logging.getLogger('message')


def encode_varint(output_data):
    return Encoder(output_data)


class Attribute(object):
    def __init__(self, name, value, type=None):
        '''
        Группировочные, поисковый, документый атрибуты.
        @name: unicode
        @value: unicode
        @type: utils.ATTRIBUTE_TYPES
        '''
        self.name = name
        self.value = value
        self.type = type

    def __unicode__(self):
        return u'Attribute(name=%s, value=%s, type=%s)' \
            % (self.name, self.value, self.type)

    def __repr__(self):
        return repr(self.__unicode__())

    def __hash__(self):
        return int(hashlib.md5('%s.%s.%s' %
            (self.name, self.value, self.type)).hexdigest(), 16)

    def __eq__(self, other):
        if isinstance(other, Attribute):
            if self.__hash__() == other.__hash__():
                return True
        return False


class Message(object):
    '''
    Сообщение для индексации

    @send_type: unicode, utils.SEND_TYPES; required - тип отправки сообщения на идексацию

    @url: unicode; required - произвольный уникальный строковой идентификатор с семантикой Url.
    Используется для отождествления документов при выполнении команд MODIFY_DOCUMENTS и DELETE_DOCUMENTS.
    Внутренние сервисные процедуры индексатора также испольуют факт уникальности Url

    @document: unicode; required - собственно документ

    @mime_type: unicode, utils.MIME_TYPES - тип содержимого документа отправленого в секции document

    @charset: unicode- кодировка документа

    @language: unicode - язык документа

    @language2: unicode - вторичный язык документа

    @modification_timestamp: int - время последней модификации документа

    @keyprefix: int - префикс всех ключей в индексе

    @search_attributes: list - список поисковых атрибутов
    [Attribute(name=some_name, value=some_value, type=some_type), ...]

    @grouping_attributes: list - список группировочных аттрибутов
    [Attribute(name=some_name, value=some_value, type=some_type), ...]

    @document_attributes: list - список документных аттрибутов
    [Attribute(name=some_name, value=some_value, type=None), ...]

    @factors: list - список факторов
    [Attribute(name=some_name, value=some_value, type=some_type), ...]
    '''
    def __init__(self,
        send_type,
        url,
        document,

        keyprefix,

        mime_type,
        charset,
        language,
        language2,

        modification_timestamp,

        search_attributes,
        grouping_attributes,
        document_attributes,
        factors):

        self.send_type = send_type
        self.url = url
        self.document = document

        self.keyprefix = keyprefix

        self.mime_type = mime_type
        self.charset = charset
        self.language = language
        self.language2 = language2

        self.modification_timestamp = modification_timestamp

        self.search_attributes = search_attributes
        self.grouping_attributes = grouping_attributes
        self.document_attributes = document_attributes
        self.factors = factors

    def __unicode__(self):
        return u'Message(url=%s, send_type=%s, keyprefix=%s)' \
            % (self.url, self.send_type, self.keyprefix)

    def __repr__(self):
        return repr(self.__unicode__())

    def __hash__(self):
        return int(hashlib.md5('.'.join(13 * ('%s',)) % \
            (self.send_type, self.url, self.document, self.keyprefix, self.mime_type,
            self.charset, self.language, self.language2, self.modification_timestamp,
            self.search_attributes, self.grouping_attributes, self.document_attributes,
            self.factors)).hexdigest(), 16)

    def __eq__(self, other):
        if isinstance(other, Message):
            if self.__hash__() == other.__hash__():
                return True
        return False

    def add_attrs(self, proto_document, type_attrs):
        if type_attrs == 'search':
            attrs = self.search_attributes
        if type_attrs == 'group':
            attrs = self.grouping_attributes
        if type_attrs == 'property':
            attrs = self.document_attributes
        if type_attrs == 'factor':
            attrs = self.factors

        for attr in attrs:
            if type_attrs == 'search':
                proto_attr = proto_document.SearchAttributes.add()
            if type_attrs == 'group':
                proto_attr = proto_document.GroupAttributes.add()
            if type_attrs == 'property':
                proto_attr = proto_document.DocumentProperties.add()
            if type_attrs == 'factor':
                proto_attr = proto_document.Factors.add()
            proto_attr.Name = attr.name
            proto_attr.Value = attr.value
            if type_attrs != 'property':
                proto_attr.Type = attr.type

    def _get_rtyserver_proto_message(self, proto_message, with_response=True):
        if with_response:
            proto_message.MessageId = MESSAGE_ID

        proto_message.MessageType = utils.SEND_TYPES.get(self.send_type)

        document = proto_message.Document
        document.Url = self.url

        document.Body = self.document

        if self.keyprefix:
            document.KeyPrefix = self.keyprefix
        else:
            document.KeyPrefix = 0

        if self.mime_type:
            document.MimeType = self.mime_type

        if self.charset:
            document.Charset = self.charset

        if self.language:
            document.Language = self.language

        if self.language2:
            document.Language2 = self.language2

        if self.modification_timestamp:
            document.ModificationTimestamp = self.modification_timestamp

        self.add_attrs(document, 'search')
        self.add_attrs(document, 'group')
        self.add_attrs(document, 'property')
        self.add_attrs(document, 'factor')

        return proto_message

    def get_dicpatcher_proto_message(self, service_name, with_response=True):
        try:
            disp_proto_message = dispatcher.TDispatchableMessage()

            self._get_rtyserver_proto_message(disp_proto_message.Message, with_response)

            disp_proto_message.Source = service_name
            return encode_varint(disp_proto_message.ByteSize()) + disp_proto_message.SerializeToString()
        except Exception, e:
            log.error(traceback.format_exc())
            raise errors.ProtobufError('Failed to create valid proto message with error: %s' % e)

    def get_rtyserver_proto_message(self, with_response=True):
        try:
            rtyserver_proto_message = self._get_rtyserver_proto_message(rtyserver.TMessage(), with_response)
            return encode_varint(rtyserver_proto_message.ByteSize()) + rtyserver_proto_message.SerializeToString()
        except Exception, e:
            log.error(traceback.format_exc())
            raise errors.ProtobufError('Failed to create valid proto message with error: %s' % e)
