class MainWindowController(QObject):
    '''
    
    '''

    def __init__(self, zeromq_context, window):
        '''
        Constructor
        '''
        self.zeromq_context = zeromq_context
        self.window = window
        self.timeSeries = TimeSeries()
        self.window.view.setData(self.timeSeries)
        self.presentation = None
        
        
    def incomingBar(self, ticker, bar):
        self.timeSeries.addPoint(bar.timestamp, bar)
        self.window.view.update()
        
    def incomingTick(self, ticker, tick):
        self.timeSeries.addPoint(tick.timestamp.toDatetime(), tick)
        
    def incomingEvent(self):
        self.window.view.update()
        
    def startStream(self, streamParameters):
        if streamParameters['type'] == 'attach':
            self.client = QuotesourceClient(self.zeromq_context, streamParameters['control-ep'])
            self.client.setSummaryCallback(self.incomingBar)
            self.client.setTickCallback(self.incomingTick)
            self.client.setEventCallback(self.incomingEvent)
            try:
                from_time = streamParameters['from']
            except KeyError:
                from_time = None    
                
            try:
                to_time = streamParameters['to']
            except KeyError:
                to_time = None
            
            if self.presentation is None:
                self.presentation = ClusterChartPresentation(streamParameters['cluster-chart/cluster-size'], streamParameters['cluster-chart/bar-length'] * 60)
                self.window.view.setPresentation(self.presentation)
            
            self.client.startStream(streamParameters['source'], streamParameters['ticker-selectors'], from_time, to_time, streamParameters['delay'])
            
            
    def stopAllStreams(self):
        self.client.stopStream()
    
        
class ClusterChartPresentation():
    '''
    
    '''


    def __init__(self, cluster_size, bar_length):
        '''
        Constructor
        '''
        
        self.offset = 0
        self.candles_on_screen = 40
        self.volume_chart_height = 100
        self.cluster_size = cluster_size
        self.bar_length = bar_length
        self.y_min = 0
        self.y_max = 0
        self.cached_data = None
        
    def getMinMax(self):
        return (self.y_min, self.y_max)
    
    def getNumberOfItems(self):
        if self.cached_data is not None:
            return len(self.cached_data)
        else:
            return 0
        
    def getNumberOfVisibleItems(self):
        return self.candles_on_screen
        
    def setOffset(self, offset):
        self.offset = offset
        
    def setData(self, data):
        self.data = data
        self.cached_data = TimeSeries()
        self.current_bar = ClusterBar(self.cluster_size, self.bar_length)
        self.current_bar_number = 0
        self.bar_end_time = None
        self.data.registerNewPointListener(self.incomingTick)
        
    def incomingTick(self, time, data):
        if self.current_bar_number == 0:
            self.current_bar_number = int(time.timestamp() // self.bar_length)
            self.bar_end_time = datetime.datetime.fromtimestamp((self.current_bar_number + 1) * self.bar_length)
            self.current_bar = ClusterBar(self.cluster_size, self.bar_length)
            self.cached_data.addPoint(time, self.current_bar)
            
        bar_number = int(time.timestamp() // self.bar_length)
        if bar_number != self.current_bar_number:
            self.cached_data.updateLastPointTime(self.bar_end_time)
            
            self.current_bar = ClusterBar(self.cluster_size, self.bar_length)
            self.current_bar_number = bar_number
            self.bar_end_time = datetime.datetime.fromtimestamp((self.current_bar_number + 1) * self.bar_length)
            
            self.cached_data.addPoint(time, self.current_bar)
            
        self.current_bar.addPoint(float(data.value), data.volume)
        self.cached_data.updateLastPointTime(time)
        
        
    def paint(self, painter, rect):
        if len(self.cached_data) == 0:
            return
        
        print("paint start", len(self.data))
        painter.setFont(QtGui.QFont("serif", 6))
        
        start_index = max(0, len(self.cached_data) - self.offset - self.candles_on_screen - 1)
        end_index = max(0, len(self.cached_data) - self.offset)
        (abs_min, abs_max, min_volume, max_volume, min_bar_volume, max_bar_volume) = self._get_minmax(self.cached_data, start_index, end_index)
        self.y_min = abs_min
        self.y_max = abs_max
        
        cluster_chart_rect = rect.adjusted(0, 0, 0, -self.volume_chart_height - 20)
        volume_chart_rect = rect.adjusted(0, cluster_chart_rect.height() + 20, 0, 0)
        
        if abs_max > abs_min:
            y_scale = cluster_chart_rect.height() / (abs_max - abs_min)
            y_offset = abs_min
        else:
            y_scale = 0
            y_offset = 0
            
        bar_width = cluster_chart_rect.width() / (self.candles_on_screen + 1)
        cluster_height = cluster_chart_rect.height() / self.cluster_size
        bar_x = 1
        
        painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 0)))
        for i in range(start_index, end_index):
            bar = self.cached_data[i]
            self._paint_bar(painter, cluster_chart_rect, bar, bar_x, bar_width, cluster_height, y_scale, y_offset, min_volume, max_volume)
            self._paint_bar_volume(painter, volume_chart_rect, bar, bar_x, bar_width, min_bar_volume, max_bar_volume)
            bar_x += bar_width
            
        print("paint end")
            
    def _paint_bar(self, painter, rect, bar, bar_x, bar_width, cluster_height, y_scale, y_offset, min_volume, max_volume):
        cluster_start = bar.start_price
        cluster_end = bar.start_price + bar.cluster_size
                
        for cluster in bar.clusters:
            if max_volume > min_volume:
                relative_volume = (cluster.total() - min_volume) / (max_volume - min_volume)
            else:
                relative_volume = 0
            painter.setPen(QtGui.QColor(relative_volume * 255, 0, (1 - relative_volume) * 255))
            painter.setBrush(QtGui.QColor(255, 255, 255))
            low_y = (cluster_start - y_offset) * y_scale
            high_y = (cluster_end  - y_offset) * y_scale
            cluster_rect = QtCore.QRect(bar_x, rect.height() - high_y, bar_width, high_y - low_y)
            painter.drawRect(cluster_rect)
            if cluster.minus() == 0:
                painter.drawText(cluster_rect, QtCore.Qt.AlignCenter, str(cluster.total()))
            else:
                cluster_rect.adjust(0, 0, 0, -cluster_rect.height() / 2)
                painter.drawText(cluster_rect, QtCore.Qt.AlignCenter, str(cluster.total()))
                cluster_rect.translate(0, cluster_rect.height())
                painter.drawText(cluster_rect, QtCore.Qt.AlignCenter, "+" + str(cluster.plus()) + "\n-" + str(cluster.minus()))
                
                delta_bar_rect = cluster_rect.adjusted(0, cluster_rect.height() * 0.8, 0, 0)
                plus_bar_rect = QtCore.QRect(delta_bar_rect.left(), delta_bar_rect.top(),
                                             delta_bar_rect.width() * (cluster.plus() / cluster.total()), delta_bar_rect.height())
                
                minus_bar_rect = delta_bar_rect.adjusted(plus_bar_rect.width(), 0, 0, 0)
                
                painter.setBrush(QtGui.QColor(0, 192, 0))
                painter.drawRect(plus_bar_rect)
                painter.setBrush(QtGui.QColor(192, 0, 0))
                painter.drawRect(minus_bar_rect)
                
            cluster_start = cluster_end
            cluster_end = cluster_start + bar.cluster_size
        
        first_price_y = rect.height() - (bar.first_price - y_offset) * y_scale
        last_price_y = rect.height() - (bar.last_price - y_offset) * y_scale
        painter.setPen(QtGui.QColor(0, 0, 0))
        painter.setBrush(QtGui.QColor(0, 0, 0))
        painter.drawLine(bar_x, first_price_y, bar_x + 5, first_price_y)
        painter.drawLine(bar_x + bar_width, last_price_y, bar_x + bar_width - 5, last_price_y)
        
    def _paint_bar_volume(self, painter, volume_chart_rect, bar, bar_x, bar_width, min_bar_volume, max_bar_volume):
        total_minus = 0
        total_plus = 0
        for cluster in bar.clusters:
            total_minus += cluster.minus()
            total_plus += cluster.plus()
            
        delta_num_height = 20
            
        if max_bar_volume > 0:
            y_scale = (volume_chart_rect.height() - delta_num_height) / max_bar_volume
        else:
            y_scale = 0
        
        painter.setPen(QtGui.QColor(0, 0, 0))
        
        minus_bar_height = y_scale * total_minus
        plus_bar_height = y_scale * total_plus
        
        bar_rect = QtCore.QRect(bar_x, volume_chart_rect.top(), bar_width, volume_chart_rect.height() - delta_num_height)
        
        painter.setBrush(QtGui.QColor(0, 192, 0))
        plus_bar_rect = bar_rect.adjusted(0, (volume_chart_rect.height() - delta_num_height - plus_bar_height - minus_bar_height), 0, -minus_bar_height)
        painter.drawRect(plus_bar_rect)
        
        painter.setBrush(QtGui.QColor(192, 0, 0))
        minus_bar_rect = bar_rect.adjusted(0, (volume_chart_rect.height() - delta_num_height - minus_bar_height), 0, 0)
        painter.drawRect(minus_bar_rect)
        
        delta_num_rect = QtCore.QRect(bar_x, volume_chart_rect.bottom() - delta_num_height, bar_width, delta_num_height)
        
        delta = total_plus - total_minus
        
        if total_plus + total_minus == 0:
            painter.setPen(QtGui.QColor(128, 128, 128))
        else:
            if delta / (total_plus + total_minus) > 0.1:
                painter.setPen(QtGui.QColor(0, 192, 0))
            elif delta / (total_plus + total_minus) < -0.1:
                painter.setPen(QtGui.QColor(192, 0, 0))
            else:
                painter.setPen(QtGui.QColor(128, 128, 128))
                
        painter.setBrush(QtGui.QColor(255, 255, 255))
        painter.drawRect(delta_num_rect)
        painter.drawText(delta_num_rect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, str(delta))
        

    def _get_minmax(self, data, start_index, end_index):
        abs_min = float(data[start_index].start_price)
        abs_max = float(data[start_index].start_price)
        min_volume = data[start_index].clusters[0].total()
        max_volume = data[start_index].clusters[0].total()
        min_bar_volume = data[start_index].clusters[0].total()
        max_bar_volume = data[start_index].clusters[0].total()
        
        for i in range(start_index, end_index):
            bar = data[i]
            if abs_min > bar.start_price:
                abs_min = bar.start_price
                
            if abs_max < bar.start_price + bar.cluster_size * len(bar.clusters):
                abs_max = bar.start_price + bar.cluster_size * len(bar.clusters)
            
            bar_volume = 0
                
            for cluster in bar.clusters:
                    
                if min_volume > cluster.total():
                    min_volume = cluster.total()
                    
                if max_volume < cluster.total():
                    max_volume = cluster.total()
                    
                bar_volume += cluster.total()
                
            if min_bar_volume > bar_volume:
                min_bar_volume = bar_volume
            
            if max_bar_volume < bar_volume:
                max_bar_volume = bar_volume
                    
        return (abs_min, abs_max, min_volume, max_volume, min_bar_volume, max_bar_volume)