import ctypes
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import load_model
import os
import sys
from datetime import datetime

# Глобальные переменные для модели
ml_model = None
lookback_period = 0
features_count = 0
last_error = "No error"

# Логирование ошибок
def log_error(message):
    global last_error
    error_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    last_error = f"[{error_time}] {message}"
    print(last_error, file=sys.stderr)

# Инициализация ML модели
def initialize_ml(model_path, lb, fc):
    global ml_model, lookback_period, features_count
    
    try:
        if not os.path.exists(model_path):
            log_error(f"Model file not found: {model_path}")
            return False
        
        # Загружаем модель
        ml_model = load_model(model_path)
        
        # Проверяем архитектуру модели
        if len(ml_model.input_shape) != 2 or ml_model.input_shape[1] != fc:
            log_error(f"Model input shape {ml_model.input_shape} doesn't match features count {fc}")
            ml_model = None
            return False
        
        lookback_period = lb
        features_count = fc
        
        # Тестовый прогноз для проверки работы модели
        test_input = np.zeros((1, features_count))
        try:
            ml_model.predict(test_input)
        except Exception as e:
            log_error(f"Model test prediction failed: {str(e)}")
            ml_model = None
            return False
            
        return True
    except Exception as e:
        log_error(f"Error initializing model: {str(e)}")
        return False

# Получение предсказания от модели
def predict_market_direction(features_array):
    global ml_model
    
    try:
        if ml_model is None:
            log_error("Model not initialized")
            return False, 0.0, 0.0
        
        # Преобразуем входные данные
        features = np.array(features_array).reshape(1, -1)
        
        # Проверяем размерность
        if features.shape[1] != features_count:
            log_error(f"Input features count {features.shape[1]} doesn't match expected {features_count}")
            return False, 0.0, 0.0
        
        # Получаем предсказание
        prediction = ml_model.predict(features)
        
        # Проверяем выход модели
        if prediction.shape[1] < 2:
            log_error(f"Model output shape {prediction.shape} doesn't match expected (?, 2)")
            return False, 0.0, 0.0
        
        buy_prob = float(prediction[0][0])
        sell_prob = float(prediction[0][1])
        
        return True, buy_prob, sell_prob
    except Exception as e:
        log_error(f"Prediction error: {str(e)}")
        return False, 0.0, 0.0

# Завершение работы
def shutdown_ml():
    global ml_model
    ml_model = None
    return True

# Получение последнего сообщения об ошибке
def get_last_error_message():
    global last_error
    return last_error

# Функции для экспорта в DLL
def InitializePythonML(model_path, lookback, features):
    try:
        return initialize_ml(model_path.decode('utf-8'), lookback, features)
    except Exception as e:
        log_error(f"InitializePythonML error: {str(e)}")
        return False

def PredictMarketDirection(features_ptr, buy_prob_ptr, sell_prob_ptr):
    try:
        # Преобразуем указатель C в массив Python
        features = (ctypes.c_double * features_count).from_address(ctypes.addressof(features_ptr.contents))
        
        success, buy_prob, sell_prob = predict_market_direction(list(features))
        
        if success:
            buy_prob_ptr[0] = buy_prob
            sell_prob_ptr[0] = sell_prob
            return True
        return False
    except Exception as e:
        log_error(f"PredictMarketDirection error: {str(e)}")
        return False

def ShutdownPythonML():
    try:
        return shutdown_ml()
    except Exception as e:
        log_error(f"ShutdownPythonML error: {str(e)}")
        return False

def GetLastErrorMessage():
    try:
        return get_last_error_message().encode('utf-8')
    except Exception as e:
        return f"Error getting error message: {str(e)}".encode('utf-8')

# Экспортируемые функции
functions = [
    ('InitializePythonML', ctypes.c_bool, [ctypes.c_char_p, ctypes.c_int, ctypes.c_int]),
    ('PredictMarketDirection', ctypes.c_bool, [ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double)]),
    ('ShutdownPythonML', None, []),
    ('GetLastErrorMessage', ctypes.c_char_p, [])
]

# Создание точки входа для DLL
if __name__ == "__main__":
    pass