"""Runnable sample script. Demonstrates realtime ticks request with further updates.
"""

import logging
import threading

from tabulate import tabulate
from win32com.client import Dispatch, constants

import CELEnvironment

# region Settings

# Replace it with enabled symbol
SYMBOL = "CLE"

# Logging settings: specify log level and log format here.
# Set log level to DEBUG to see more internal messages.
# See more information about logging lib usage: https://docs.python.org/3/howto/logging-cookbook.html
CELEnvironment.LOG_LEVEL = logging.INFO
# CELEnvironment.LOG_FORMAT = ...

# CELEnvironment.SinkInternal uses own LOG_LEVEL (which is inherited from the global one) and own LOG_FORMAT.
# You can redefine them as follows:
# CELEnvironment.SinkInternal.LOG_LEVEL = logging.DEBUG
# CELEnvironment.SinkInternal.LOG_FORMAT = ...

LOG = CELEnvironment.GetLogger()

# endregion


class TicksRequestSample(CELEnvironment.CELSinkBase):
    def __init__(self):
        self.eventDone = threading.Event()
        self.celEnvironment = None
        self.ticks = None

    def Init(self, celEnvironment):
        self.celEnvironment = celEnvironment

    def Start(self):
        request = Dispatch(self.celEnvironment.cqgCEL.CreateTicksRequest())
        request.Symbol = SYMBOL
        request.Type = constants.trtCurrentNotify
        request.TickFilter = constants.tfAll
        request.SessionsFilter = 31
        request.Limit = 10
        LOG.info("Limit: %d", request.Limit)

        self.ticks = self.celEnvironment.cqgCEL.RequestTicks(request)
        self.eventDone.wait(10)

        LOG.info("Done!")

    def OnDataError(self, cqgError, errorDescription):
        if cqgError is not None:
            error = Dispatch(cqgError)
            LOG.error("OnDataError: Code: %d. Description: '%s'", error.Code, error.Description)

        self.eventDone.set()

    def OnTicksResolved(self, cqgTicks, cqgError):
        if cqgError is not None:
            error = Dispatch(cqgError)
            LOG.error("OnTicksResolved: Code: %d. Description: '%s'", error.Code, error.Description)
            self.eventDone.set()
            return

        dispatchedCQGTicks = Dispatch(cqgTicks)
        LOG.info(
            "OnTicksResolved: Ticks count: %d. Ticks:\n%s",
            dispatchedCQGTicks.Count,
            self.formatTicks(dispatchedCQGTicks),
        )

    def OnTicksAdded(self, cqgTicks, addedTicksCount):
        dispatchedCQGTicks = Dispatch(cqgTicks)
        LOG.info(
            "OnTicksAdded: Ticks added count: %d. Total ticks count: %d. Ticks:\n%s",
            addedTicksCount,
            dispatchedCQGTicks.Count,
            self.formatTicks(dispatchedCQGTicks),
        )

    def OnTicksRemoved(self, cqgTicks, removedTickIndex):
        dispatchedCQGTicks = Dispatch(cqgTicks)
        LOG.info(
            "OnTicksRemoved: Removed tick index: %d. Total ticks count: %d. Ticks:\n%s",
            removedTickIndex,
            dispatchedCQGTicks.Count,
            self.formatTicks(dispatchedCQGTicks),
        )

    # region Utility methods

    def validateValue(self, value):
        """Returns value if it is valid, otherwise "N/A" string.
        WARN: should be called only from callbacks.

        Args:
            value (datetime | float | int): Value to check.
        """
        return value if self.celEnvironment.cqgCELForCallback.IsValid(value) else "N/A"

    def getTickRow(self, tick, index: int) -> list:
        """Returns CQGTick values as list.
        WARN: should be called only from callbacks.

        Args:
            tick (CQGTick): Dispatched CQGTick object.
            index (int): Tick index.
        """
        res = [index, tick.Timestamp, tick.VolumeFractional, tick.Price, tick.PriceType, tick.SalesConditionLabel]
        return [self.validateValue(value) for value in res]

    def formatTicks(self, ticks) -> str:
        """Formats ticks into string as table.
        WARN: should be called only from callbacks.

        Args:
            ticks (CQGTicks): Dispatched CQG ticks collection.

        Returns:
            str: Formatted string.
        """
        return tabulate(
            [self.getTickRow(tick, index) for tick, index in zip(ticks, range(ticks.Count))],
            headers=["Index", "Timestamp", "Vol", "Price", "PriceType", "SalesCondition"],
        )

    # endregion


if __name__ == "__main__":
    CELEnvironment.Start(TicksRequestSample)
