#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
The BBMQ server is required to accept 2 connections. one from the producer and one from the consumer.

Each topic will have one queue. Topic is an abstraction basically for a queue. The name 'Topic' is inspired from apache kafka

Producer: Publisher of the messages
Consumer: Subscriber of the messages

Communication between the connection thread and the main thread. It uses a simple queue for communication purposes. Whenever a new connection is established, its details will be stored in the queue, for whatever number of new connections

"""

import socket
import logging, logging.config
import threading
import sys, os
import ast
import traceback
import Queue
import datetime
import signal

# --------------------------Custom imports------------------------------------------
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

import settings
from bbmq import BBMQ
from partition_messages import Message
from message import BaseMessage

USE_DB = settings.USE_DB

if USE_DB:
    import models
    from models import ModelManager
    from models import Queue as QueueModel
    from models import Message as MessageModel

LOG_FILEPATH = settings.LOG_FILEPATH
LOG_LEVEL = settings.LOG_LEVEL
SERVER_MAX_QUEUED_CON = settings.SERVER_MAX_QUEUED_CON
TOPICS = settings.TOPICS
CLIENT_PUBLISHER = settings.CLIENT_PUBLISHER
CLIENT_SUBSCRIBER = settings.CLIENT_SUBSCRIBER
MAX_MESSAGE_SIZE = settings.MAX_MESSAGE_SIZE
SERVER_ACKNOWLEDGEMENT = settings.SERVER_ACKNOWLEDGEMENT
CLIENT_SHUTDOWN_SIGNAL = settings.CLIENT_SHUTDOWN_SIGNAL
CONSUMER_REQUEST_WORD = settings.CONSUMER_REQUEST_WORD
INVALID_PROTOCOL = settings.INVALID_PROTOCOL
EMPTY_QUEUE_MESSAGE = settings.EMPTY_QUEUE_MESSAGE
PRODUCER_ACK_MESSAGE = settings.PRODUCER_ACK_MESSAGE
CLOSE_CONNECTION_SIGNAL = settings.CLOSE_CONNECTION_SIGNAL

HEAD = settings.HEAD
TAIL = settings.TAIL

PARTITION_SIZE = settings.PARTITION_SIZE

logging.config.dictConfig(settings.LOGGING)
logger = logging.getLogger("bbmq_server_module")


class ProducerThread(threading.Thread):
    """
    Connection thread will be waiting for connections from producers or consumers
    """
    def __init__(self, producer_socket, inbound_socket_address, queue, topic_name):
        """
        initialize the thread. During initialization of this thread, it must confirm to the
        producer that the producer can now start communication
        :param producer_socket:
        :param inbound_socket_address:
        :param queue:
        :param topic_name:
        """
        threading.Thread.__init__(self)
        self.logger = logging.getLogger("ProducerThread")
        self.logger.debug("Initializing Producer Thread for socket adddress: {}".format(
            inbound_socket_address))
        self.socket = producer_socket
        self.queue = queue
        self.topic_name = topic_name
        self.socket.send(SERVER_ACKNOWLEDGEMENT)
        if USE_DB:
            self.session = ModelManager.create_session(models.engine)
            self.queue_object = self.session.query(QueueModel).filter(QueueModel.name == topic_name).first()

    def run(self):
        """
        run the thread. called when the start() method of Thread super class is called
        :return:
        """
        msg = None
        msg_body = None
        try:
            while True:
                try:
                    # The Queue will only store the message and thats all.
                    msg = BaseMessage(message="")
                    msg_body = BaseMessage(message="")
                    while True:
                        part = self.socket.recv(PARTITION_SIZE)
                        msg.append(part)
                        self.logger.debug("message now: ")
                        self.logger.debug(msg)

                        has_tail, message_tail = msg.has_message_tail()
                        has_head, message_head = msg.has_message_head()

                        if has_tail:
                            self.logger.debug("TAIL received for message")