// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// gpu_backend.hpp
//
// GPUバックエンド実装
//
// このファイルでは、GPU（CUDA/HIP）を使用した線形代数演算のバックエンド実装のための
// 基本的なインターフェースを提供します。現在は将来の拡張のための枠組みのみを含みます。
//
// 主な機能:
// - GPU検出と初期化
// - GPU BLASルーチンへのラッパー関数
// - CPU-GPU間のデータ転送ユーティリティ

#ifndef CALX_GPU_BACKEND_HPP
#define CALX_GPU_BACKEND_HPP

#include "math/core/common.hpp"
#include "math/core/traits.hpp"

#include <cstddef>
#include <memory>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>

// GPUが利用可能かどうかを確認
#if defined(CALX_HAS_CUDA)
#include <cuda_runtime.h>
#include <cublas_v2.h>
#elif defined(CALX_HAS_HIP)
#include <hip/hip_runtime.h>
#include <hipblas.h>
#endif

namespace calx {
namespace computation {
namespace gpu {

// GPUサポートの有無を表す定数
#if defined(CALX_HAS_CUDA) || defined(CALX_HAS_HIP)
constexpr bool has_gpu_support = true;
#else
constexpr bool has_gpu_support = false;
#endif

// GPUデバイス情報を表す構造体
struct GpuDeviceInfo {
    int device_id;
    std::string name;
    std::size_t total_memory;
    int compute_capability_major;
    int compute_capability_minor;
    
    GpuDeviceInfo() : device_id(-1), total_memory(0), 
                     compute_capability_major(0), 
                     compute_capability_minor(0) {}
};

// GPUコンテキストを管理するクラス
class GpuContext {
public:
    // シングルトンインスタンスの取得
    static GpuContext& instance() {
        static GpuContext instance;
        return instance;
    }
    
    // GPUの初期化
    bool initialize() {
#if defined(CALX_HAS_CUDA)
        // CUDAの初期化
        cudaError_t cuda_error = cudaSetDevice(0);
        if (cuda_error != cudaSuccess) {
            last_error_ = cudaGetErrorString(cuda_error);
            return false;
        }
        
        // CUBLASの初期化
        cublasStatus_t cublas_error = cublasCreate(&cublas_handle_);
        if (cublas_error != CUBLAS_STATUS_SUCCESS) {
            last_error_ = "Failed to create CUBLAS handle";
            return false;
        }
        
        // デバイス情報の取得
        cudaDeviceProp device_prop;
        cuda_error = cudaGetDeviceProperties(&device_prop, 0);
        if (cuda_error != cudaSuccess) {
            last_error_ = cudaGetErrorString(cuda_error);
            return false;
        }
        
        device_info_.device_id = 0;
        device_info_.name = device_prop.name;
        device_info_.total_memory = device_prop.totalGlobalMem;
        device_info_.compute_capability_major = device_prop.major;
        device_info_.compute_capability_minor = device_prop.minor;
        
        initialized_ = true;
        return true;
#elif defined(CALX_HAS_HIP)
        // HIPの初期化
        hipError_t hip_error = hipSetDevice(0);
        if (hip_error != hipSuccess) {
            char const* error_str;
            hipGetErrorString(hip_error, &error_str);
            last_error_ = error_str;
            return false;
        }
        
        // hipBLASの初期化
        hipblasStatus_t hipblas_error = hipblasCreate(&hipblas_handle_);
        if (hipblas_error != HIPBLAS_STATUS_SUCCESS) {
            last_error_ = "Failed to create hipBLAS handle";
            return false;
        }
        
        // デバイス情報の取得
        hipDeviceProp_t device_prop;
        hip_error = hipGetDeviceProperties(&device_prop, 0);
        if (hip_error != hipSuccess) {
            char const* error_str;
            hipGetErrorString(hip_error, &error_str);
            last_error_ = error_str;
            return false;
        }
        
        device_info_.device_id = 0;
        device_info_.name = device_prop.name;
        device_info_.total_memory = device_prop.totalGlobalMem;
        device_info_.compute_capability_major = device_prop.major;
        device_info_.compute_capability_minor = device_prop.minor;
        
        initialized_ = true;
        return true;
#else
        last_error_ = "GPU support not available";
        return false;
#endif
    }
    
    // GPU処理の終了処理
    void finalize() {
#if defined(CALX_HAS_CUDA)
        if (cublas_handle_) {
            cublasDestroy(cublas_handle_);
            cublas_handle_ = nullptr;
        }
#elif defined(CALX_HAS_HIP)
        if (hipblas_handle_) {
            hipblasDestroy(hipblas_handle_);
            hipblas_handle_ = nullptr;
        }
#endif
        initialized_ = false;
    }
    
    // GPUが初期化されているかどうかの確認
    bool is_initialized() const {
        return initialized_;
    }
    
    // 最後のエラーメッセージの取得
    const std::string& get_last_error() const {
        return last_error_;
    }
    
    // デバイス情報の取得
    const GpuDeviceInfo& get_device_info() const {
        return device_info_;
    }
    
    // BLASハンドルの取得
#if defined(CALX_HAS_CUDA)
    cublasHandle_t get_cublas_handle() const {
        return cublas_handle_;
    }
#elif defined(CALX_HAS_HIP)
    hipblasHandle_t get_hipblas_handle() const {
        return hipblas_handle_;
    }
#endif
    
private:
    // コンストラクタ
    GpuContext() : initialized_(false) {}
    
    // デストラクタ
    ~GpuContext() {
        finalize();
    }
    
    // コピー禁止
    GpuContext(const GpuContext&) = delete;
    GpuContext& operator=(const GpuContext&) = delete;
    
    bool initialized_;
    std::string last_error_;
    GpuDeviceInfo device_info_;
    
#if defined(CALX_HAS_CUDA)
    cublasHandle_t cublas_handle_ = nullptr;
#elif defined(CALX_HAS_HIP)
    hipblasHandle_t hipblas_handle_ = nullptr;
#endif
};

// GPUメモリ管理クラス
template<typename T>
class GpuArray {
public:
    // デフォルトコンストラクタ
    GpuArray() : data_(nullptr), size_(0) {}
    
    // サイズ指定コンストラクタ
    explicit GpuArray(std::size_t size) : data_(nullptr), size_(0) {
        resize(size);
    }
    
    // コピーコンストラクタ（禁止）
    GpuArray(const GpuArray&) = delete;
    
    // ムーブコンストラクタ
    GpuArray(GpuArray&& other) noexcept : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
    }
    
    // コピー代入演算子（禁止）
    GpuArray& operator=(const GpuArray&) = delete;
    
    // ムーブ代入演算子
    GpuArray& operator=(GpuArray&& other) noexcept {
        if (this != &other) {
            free();
            data_ = other.data_;
            size_ = other.size_;
            other.data_ = nullptr;
            other.size_ = 0;
        }
        return *this;
    }
    
    // デストラクタ
    ~GpuArray() {
        free();
    }
    
    // サイズの取得
    std::size_t size() const {
        return size_;
    }
    
    // データポインタの取得
    T* data() {
        return data_;
    }
    
    // データポインタの取得（const版）
    const T* data() const {
        return data_;
    }
    
    // サイズ変更
    void resize(std::size_t new_size) {
        if (new_size == size_) {
            return;
        }
        
        free();
        
        if (new_size > 0) {
#if defined(CALX_HAS_CUDA)
            cudaError_t error = cudaMalloc(&data_, new_size * sizeof(T));
            if (error != cudaSuccess) {
                throw std::runtime_error(cudaGetErrorString(error));
            }
#elif defined(CALX_HAS_HIP)
            hipError_t error = hipMalloc(&data_, new_size * sizeof(T));
            if (error != hipSuccess) {
                char const* error_str;
                hipGetErrorString(error, &error_str);
                throw std::runtime_error(error_str);
            }
#else
            throw std::runtime_error("GPU support not available");
#endif
            size_ = new_size;
        }
    }
    
    // メモリ解放
    void free() {
        if (data_) {
#if defined(CALX_HAS_CUDA)
            cudaFree(data_);
#elif defined(CALX_HAS_HIP)
            hipFree(data_);
#endif
            data_ = nullptr;
        }
        size_ = 0;
    }
    
    // ホストからデバイスへのデータコピー
    void copy_from_host(const T* host_data, std::size_t count) {
        if (count > size_) {
            throw std::out_of_range("Copy count exceeds array size");
        }
        
#if defined(CALX_HAS_CUDA)
        cudaError_t error = cudaMemcpy(data_, host_data, count * sizeof(T), cudaMemcpyHostToDevice);
        if (error != cudaSuccess) {
            throw std::runtime_error(cudaGetErrorString(error));
        }
#elif defined(CALX_HAS_HIP)
        hipError_t error = hipMemcpy(data_, host_data, count * sizeof(T), hipMemcpyHostToDevice);
        if (error != hipSuccess) {
            char const* error_str;
            hipGetErrorString(error, &error_str);
            throw std::runtime_error(error_str);
        }
#else
        throw std::runtime_error("GPU support not available");
#endif
    }
    
    // デバイスからホストへのデータコピー
    void copy_to_host(T* host_data, std::size_t count) const {
        if (count > size_) {
            throw std::out_of_range("Copy count exceeds array size");
        }
        
#if defined(CALX_HAS_CUDA)
        cudaError_t error = cudaMemcpy(host_data, data_, count * sizeof(T), cudaMemcpyDeviceToHost);
        if (error != cudaSuccess) {
            throw std::runtime_error(cudaGetErrorString(error));
        }
#elif defined(CALX_HAS_HIP)
        hipError_t error = hipMemcpy(host_data, data_, count * sizeof(T), hipMemcpyDeviceToHost);
        if (error != hipSuccess) {
            char const* error_str;
            hipGetErrorString(error, &error_str);
            throw std::runtime_error(error_str);
        }
#else
        throw std::runtime_error("GPU support not available");
#endif
    }
    
    // ベクトルからデバイスへのデータコピー
    void copy_from_vector(const std::vector<T>& host_vector) {
        copy_from_host(host_vector.data(), host_vector.size());
    }
    
    // デバイスからベクトルへのデータコピー
    void copy_to_vector(std::vector<T>& host_vector) const {
        host_vector.resize(size_);
        copy_to_host(host_vector.data(), size_);
    }
    
private:
    T* data_;
    std::size_t size_;
};

//-----------------------------------------------------------------------------
// GPU BLAS関数 (将来的な実装)
//-----------------------------------------------------------------------------

// 内積計算（GPU実装）
template<typename T>
T dot_product(const T* x_gpu, const T* y_gpu, std::size_t size) {
#if defined(CALX_HAS_CUDA) || defined(CALX_HAS_HIP)
    // この関数は将来的に実装される予定です
    // CUBLASやhipBLASを使用した実装
    throw std::runtime_error("GPU dot_product not implemented yet");
#else
    throw std::runtime_error("GPU support not available");
#endif
}

// ベクトルのユークリッドノルム（GPU実装）
template<typename T>
T norm2(const T* x_gpu, std::size_t size) {
#if defined(CALX_HAS_CUDA) || defined(CALX_HAS_HIP)
    // この関数は将来的に実装される予定です
    // CUBLASやhipBLASを使用した実装
    throw std::runtime_error("GPU norm2 not implemented yet");
#else
    throw std::runtime_error("GPU support not available");
#endif
}

// axpy演算（GPU実装）
template<typename T, typename Alpha>
void axpy(T* y_gpu, Alpha alpha, const T* x_gpu, std::size_t size) {
#if defined(CALX_HAS_CUDA) || defined(CALX_HAS_HIP)
    // この関数は将来的に実装される予定です
    // CUBLASやhipBLASを使用した実装
    throw std::runtime_error("GPU axpy not implemented yet");
#else
    throw std::runtime_error("GPU support not available");
#endif
}

// スケーリング（GPU実装）
template<typename T, typename Alpha>
void scale(T* x_gpu, Alpha alpha, std::size_t size) {
#if defined(CALX_HAS_CUDA) || defined(CALX_HAS_HIP)
    // この関数は将来的に実装される予定です
    // CUBLASやhipBLASを使用した実装
    throw std::runtime_error("GPU scale not implemented yet");
#else
    throw std::runtime_error("GPU support not available");
#endif
}

// ベクトル加算（GPU実装）
template<typename T>
void add(const T* x_gpu, const T* y_gpu, T* z_gpu, std::size_t size) {
#if defined(CALX_HAS_CUDA) || defined(CALX_HAS_HIP)
    // この関数は将来的に実装される予定です
    // CUBLASやhipBLASを使用した実装
    throw std::runtime_error("GPU add not implemented yet");
#else
    throw std::runtime_error("GPU support not available");
#endif
}

// ベクトル減算（GPU実装）
template<typename T>
void subtract(const T* x_gpu, const T* y_gpu, T* z_gpu, std::size_t size) {
#if defined(CALX_HAS_CUDA) || defined(CALX_HAS_HIP)
    // この関数は将来的に実装される予定です
    // CUBLASやhipBLASを使用した実装
    throw std::runtime_error("GPU subtract not implemented yet");
#else
    throw std::runtime_error("GPU support not available");
#endif
}

// 行列-ベクトル積（GPU実装）
template<typename T, typename Alpha, typename Beta>
void gemv(bool trans, std::size_t m, std::size_t n, Alpha alpha, 
         const T* a_gpu, std::size_t lda, const T* x_gpu, std::size_t incx,
         Beta beta, T* y_gpu, std::size_t incy) {
#if defined(CALX_HAS_CUDA) || defined(CALX_HAS_HIP)
    // この関数は将来的に実装される予定です
    // CUBLASやhipBLASを使用した実装
    throw std::runtime_error("GPU gemv not implemented yet");
#else
    throw std::runtime_error("GPU support not available");
#endif
}

// 行列-行列積（GPU実装）
template<typename T, typename Alpha, typename Beta>
void gemm(bool transa, bool transb, std::size_t m, std::size_t n, std::size_t k,
         Alpha alpha, const T* a_gpu, std::size_t lda, const T* b_gpu, std::size_t ldb,
         Beta beta, T* c_gpu, std::size_t ldc) {
#if defined(CALX_HAS_CUDA) || defined(CALX_HAS_HIP)
    // この関数は将来的に実装される予定です
    // CUBLASやhipBLASを使用した実装
    throw std::runtime_error("GPU gemm not implemented yet");
#else
    throw std::runtime_error("GPU support not available");
#endif
}

// その他のGPU BLAS関数も将来的に実装予定...

} // namespace gpu
} // namespace computation
} // namespace calx

#endif // CALX_GPU_BACKEND_HPP