//
// LDASTools frameCPP - Frame File Information Analysis Classes
//
// Copyright (C) 2025 California Institute of Technology
//
// LDASTools frameCPP is free software; you may redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 (GPLv2) of the
// License or at your discretion, any later version.
//
// LDASTools frameCPP is distributed in the hope that it will be useful, but
// without any warranty or even the implied warranty of merchantability
// or fitness for a particular purpose. See the GNU General Public
// License (GPLv2) for more details.

#ifndef FRAMECPP_OOINTERFACE_FRAMEFILEINFO_HH
#define FRAMECPP_OOINTERFACE_FRAMEFILEINFO_HH

#include <filesystem>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include <set>
#include <ostream>
#include <iostream>
#include <tuple>

#include "ldastoolsal/SimpleLogger.hh"
#include "framecpp/Common/FrameCPPServices.hh"
#include "framecpp/Common/SequentialFrameReader.hh" 
#include "framecpp/Universal/FrHeader.hh"

// Forward declarations to reduce compilation dependencies
namespace FrameCPP::Common {
    class FrameCPPServices;
    class SequentialFrameReader;
}

namespace FrameCPP::Universal {
    class FrHeader;
}

namespace FrameCPP::OOInterface {

/// @brief Field selection using modern enum class with strong typing
enum class FrameField {
    All,
    Originator,
    FrameSpecVersion,
    LibraryRevision,
    LibraryName,
    ChecksumScheme,
    Endianness,
    DataSizes
};

/// @brief Display configuration using modern optional and variant types
struct DisplayConfig {
    /// @brief Display mode enumeration
    enum class Mode { 
        Verbose,  ///< Human-readable output with labels
        Terse     ///< Machine-parsable output
    };
    
    Mode mode = Mode::Verbose;                           ///< Default to verbose mode
    std::set<FrameField> fields = {FrameField::All};    ///< Fields to display (set for efficient lookup)
    bool show_filename = false;                          ///< Include filename in terse output
    std::optional<std::ostream*> output_stream = std::nullopt; ///< Output stream (defaults to std::cout)
    
    /// @brief Check if all fields should be displayed
    bool displayAll() const { 
        return fields.count(FrameField::All) > 0; 
    }
    
    /// @brief Check if a specific field should be displayed
    bool shouldDisplay(FrameField field) const {
        return displayAll() || fields.count(field) > 0;
    }
};

/// @brief Frame information container using C++17 structured data
struct FrameInfo {
    std::string originator;           ///< Frame originator string
    uint32_t frame_spec_version = 0;  ///< Frame specification version
    uint32_t library_revision = 0;    ///< Library revision number
    std::string library_name;         ///< Frame library name
    std::string checksum_scheme;      ///< Checksum scheme description
    std::string endianness;          ///< Byte order information
    std::string data_type_sizes;     ///< Data type size information
    bool is_valid = false;           ///< Whether frame file is valid
    
    /// @brief C++17 structured binding support
    auto tie() const { 
        return std::tie(originator, frame_spec_version, library_revision, 
                       library_name, checksum_scheme, endianness, data_type_sizes, is_valid); 
    }
    
    /// @brief Equality comparison for testing
    bool operator==(const FrameInfo& other) const {
        return tie() == other.tie();
    }
    
    /// @brief Inequality comparison
    bool operator!=(const FrameInfo& other) const {
        return !(*this == other);
    }
};

/// @brief Main frame analyzer class using RAII and modern C++17 patterns
class FrameFileAnalyzer {
private:
    const FrameCPP::Common::FrameCPPServices& services_;  ///< FrameCPP services for logging
    SimpleLogger* logger_;              ///< Logger instance
    
    /// @brief Calculate endianness string from Universal::FrHeader (extracted from original framecpp_info.cc)
    /// @param header Universal::FrHeader to analyze
    /// @return Human-readable endianness description
    static std::string calculateByteOrder(const Universal::FrHeader& header);
    
    /// @brief Read FrHeader from file using SequentialFrameReader for robust validation and extract info
    /// @param filepath Path to frame file
    /// @return Optional FrameInfo with extracted data (nullopt on error)
    std::optional<FrameInfo> readFrameHeader(const std::filesystem::path& filepath) const;
    
    /// @brief Extract structured information from Universal::FrHeader
    /// @param header Universal::FrHeader to analyze
    /// @return FrameInfo structure with extracted data
    static FrameInfo extractFrameInfo(const Universal::FrHeader& header);
    
public:
    /// @brief Constructor with dependency injection
    /// @param services FrameCPP services context
    explicit FrameFileAnalyzer(const FrameCPP::Common::FrameCPPServices& services);
    
    /// @brief Core analysis method - returns structured data instead of printing
    /// @param filepath Path to frame file to analyze
    /// @return Optional FrameInfo (nullopt on error)
    std::optional<FrameInfo> analyzeFile(const std::filesystem::path& filepath) const;
    
    /// @brief Batch processing with C++17 range support
    /// @param filepaths Vector of file paths to analyze
    /// @return Vector of path/result pairs
    std::vector<std::pair<std::filesystem::path, std::optional<FrameInfo>>>
    analyzeFiles(const std::vector<std::filesystem::path>& filepaths) const;
    
    /// @brief Static validation method for quick frame file checking
    /// @param filepath Path to check
    /// @return True if file exists and appears to be a valid frame file
    static bool isValidFrameFile(const std::filesystem::path& filepath);
};

/// @brief Separate formatter class following single responsibility principle
class FrameInfoFormatter {
private:
    DisplayConfig config_;    ///< Display configuration
    std::ostream& output_;   ///< Output stream
    
    /// @brief Format a single field with proper display mode handling
    /// @param label Field label for verbose mode
    /// @param value Field value
    /// @param field Field type for filtering
    /// @param filepath File path for terse mode filename display
    void formatField(std::string_view label, std::string_view value, 
                    FrameField field, const std::filesystem::path& filepath) const;
    
    /// @brief Format data type sizes with special handling
    /// @param info FrameInfo containing data type size information
    /// @param filepath File path for terse mode filename display
    void formatDataSizes(const FrameInfo& info, const std::filesystem::path& filepath) const;
    
    /// @brief Format verbose mode header
    /// @param filepath File path to display
    void formatVerboseHeader(const std::filesystem::path& filepath) const;
    
    /// @brief Format verbose mode footer
    void formatVerboseFooter() const;
    
public:
    /// @brief Constructor with display configuration
    /// @param config Display configuration
    /// @param output Output stream (defaults to std::cout)
    explicit FrameInfoFormatter(const DisplayConfig& config, 
                               std::ostream& output = std::cout);
    
    /// @brief Format single frame info result
    /// @param info Frame information to format
    /// @param filepath File path for context
    void format(const FrameInfo& info, const std::filesystem::path& filepath) const;
    
    /// @brief Format batch results with error handling
    /// @param results Vector of path/result pairs from batch analysis
    void formatBatch(const std::vector<std::pair<std::filesystem::path, 
                                               std::optional<FrameInfo>>>& results) const;
    
    /// @brief Format error message for failed analysis
    /// @param filepath File path that failed
    /// @param error_message Error description
    void formatError(const std::filesystem::path& filepath, std::string_view error_message) const;
};

/// @brief Utility functions for C++17 filesystem operations

/// @brief Check if path has .gwf extension (case-insensitive)
/// @param filepath Path to check
/// @return True if file has .gwf extension
bool isGwfFile(const std::filesystem::path& filepath);

/// @brief Get data type sizes as formatted string (matching original framecpp_info behavior)
/// @return Comma-separated size string
std::string getDataTypeSizes();

} // namespace FrameCPP::OOInterface

#endif // FRAMECPP_OOINTERFACE_FRAMEFILEINFO_HH