# -*-coding:UTF-8 -*
from sqlite3 import IntegrityError
import pika
import json
from dbutils import list_searches
from utils import logit


class SearchesPub(object):
    EXCHANGE = "spidernest"
    EXCHANGE_TYPE = "direct"
    PUBLISH_INTERVAL = 3600
    QUEUE = 'searches'
    ROUTING_KEY = 'spidernest.searches'


    def __init__(self, amqp_url):
        self._connection = None
        self._channel = None
        self._deliveries = []
        self._acked = 0
        self._nacked = 0
        self._message_number = 0
        self._stopping = False
        self._url = amqp_url
        self._closing = False

    def connect(self):
        logit('Connecting to {}'.format(self._url))
        return  pika.SelectConnection(pika.URLParameters(self._url),\
            self.on_connection_open, stop_ioloop_on_close=False)

    def on_connection_open(self,unused_connection):
        logit('Connection opened')
        self.add_on_connection_close_callback()
        self.open_channel()

    def add_on_connection_close_callback(self):
        logit("adding connection close callback")
        self._connection.add_on_close_callback(self.on_connection_closed)

    def on_connection_closed(self,connection,reply_code, reply_text):
        self._channel = None
        if(self._closing):
            self._connection.ioloop.stop()
        else:
            logit("Connection closed, reopening in five "+\
                "seconds:{}\n{}\n".format(reply_code,reply_text))
            self._connection.add_timeout(5,self.reconnect)

    def reconnect(self):
        self._deliveries = []
        self.acked = 0
        self._nacked = 0
        self._message_number = 0
        self._connection.ioloop.stop()
        self._connection = self.connect()
        self._connection.ioloop.start()

    def open_channel(self):
        logit("creating new channel")
        self._connection.channel(on_open_callback=self.on_channel_open)

    def on_channel_open(self,channel):
        logit("channel opened")
        self._channel =  channel
        self.add_on_channel_close_callback()
        self.setup_exchange(self.EXCHANGE)

    def add_on_channel_close_callback(self):
        logit("adding channel close cb")
        self._channel.add_on_close_callback(self.on_channel_closed)

    def on_channel_closed(self,channel,reply_code,reply_text):
        logit("channel was closed :{}\n{}\n".format(reply_code,reply_text),\
           30)
        
        if not self._closing:
            self._connection.close()

    def setup_exchange(self,exchange_name):
        logit("Declaring exchange {}".format(exchange_name))
        self._channel.exchange_declare(self.on_exchange_declareok,\
            exchange_name, self.EXCHANGE_TYPE)

    def on_exchange_declareok(self,unused_frame):
        logit("Exchange Declared")
        self.setup_queue(self.QUEUE)

    def setup_queue(self,queue_name):
        logit("Declaring queue {}".format(queue_name))
        self._channel.queue_declare(self.on_queue_declareok,queue_name)

    def on_queue_declareok(self,method_frame):
        logit("Binding {} to {} with {}".format(self.EXCHANGE,self.QUEUE,\
            self.ROUTING_KEY))
        self._channel.queue_bind(self.on_bindok, self.QUEUE, self.EXCHANGE,\
            self.ROUTING_KEY)

    def on_bindok(self,unused_frame):
        logit("Queue bound")
        self.start_publishing()

    def start_publishing(self):
        logit("Issuing consumer related RPC commands")
        self.enable_delivery_confirmations()
        self.schedule_next_message()

    def enable_delivery_confirmations(self):
        logit("issuing confirm select rpc")
        self._channel.confirm_delivery(self.on_delivery_confirmation)

    def on_delivery_confirmation(self,method_frame):
        confirmation_type = method_frame.method.NAME.split('.')[1].lower()
        logit("received {} for tag {}".format(confirmation_type,\
            method_frame.method.delivery_tag))

        if confirmation_type == 'ack':
            self._acked += 1
        elif confirmation_type == 'nack':
            self._nacked += 1
        self._deliveries.remove(method_frame.method.delivery_tag)
        logit("Published {} messages, {} have yet to be confirmed,".format(\
            self._message_number, len(self._deliveries)) +\
            " {} were acked and {} were nacked".format(self._acked,
            self._nacked))

    def schedule_next_message(self):
        if(self._stopping):
            return
        logit("scheduling next message")
        self._connection.add_timeout(self.PUBLISH_INTERVAL,self.publish_message)

    def publish_message(self):
        if self._stopping:
            return


        messages = list_searches()

        for m in messages:

            properties = pika.BasicProperties(app_id='spidernest',
                content_type='application/json')

            self._channel.basic_publish(self.EXCHANGE,self.ROUTING_KEY,
                json.dumps({m:messages[m]}),
                properties)

            self._message_number += 1
            self._deliveries.append(self._message_number)
            logit('Published message {}'.format(self._message_number),30)

        self.schedule_next_message()
    
    def close_channel(self):
        logit('closing channel')
        if(self._channel):
            self._channel.close()

    def run(self):
        self._connection = self.connect()
        self._connection.ioloop.start()

    def stop(self):
        logit("stopping")
        self._stopping = True
        self.close_channel()
        self.close_connection()
        self._connection.ioloop.start()
        logit("stopped")

    def close_connection(self):
        logit("closing connection")
        self.closing = True
        self._connection.close()
