//
// LDASTools frameCPP - Utility to dump FrameL structure definitions
//
// 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.
//
// This utility queries FrameL library for version-specific structure definitions.
// It uses FrLibSetVersion() to set the target version, then calls the appropriate
// *Def() function to get the structure definition for that version.

#include <FrameL.h>
#include <boost/program_options.hpp>
#include <iostream>
#include <map>
#include <string>
#include <cstdlib>

namespace po = boost::program_options;

// Map structure names to their *Def() function pointers
typedef FrSH* (*DefFunction)();

std::map<std::string, DefFunction> getDefFunctions() {
    std::map<std::string, DefFunction> functions;
    functions["FrameH"] = FrameHDef;
    functions["FrAdcData"] = FrAdcDataDef;
    functions["FrDetector"] = FrDetectorDef;
    functions["FrEndOfFrame"] = FrEndOfFrameDef;
    functions["FrEndOfFile"] = FrEndOfFileDef;
    functions["FrEvent"] = FrEventDef;
    functions["FrHistory"] = FrHistoryDef;
    functions["FrMsg"] = FrMsgDef;
    functions["FrProcData"] = FrProcDataDef;
    functions["FrRawData"] = FrRawDataDef;
    functions["FrSE"] = FrSEDef;
    functions["FrSerData"] = FrSerDataDef;
    functions["FrSH"] = FrSHDef;
    functions["FrSimData"] = FrSimDataDef;
    functions["FrSimEvent"] = FrSimEventDef;
    functions["FrStatData"] = FrStatDataDef;
    functions["FrSummary"] = FrSummaryDef;
    functions["FrTable"] = FrTableDef;
    functions["FrTOC"] = FrTOCDef;
    functions["FrVect"] = FrVectDef;
    return functions;
}

void dumpDefinition(const std::string& objectName, int version) {
    // Set FrameL library version - this affects what the *Def() functions return
    FrLibSetVersion(version);

    // Get the definition function
    std::map<std::string, DefFunction> defFunctions = getDefFunctions();
    auto it = defFunctions.find(objectName);

    if (it == defFunctions.end()) {
        std::cerr << "Error: Unknown object type '" << objectName << "'" << std::endl;
        std::cerr << "Available types: ";
        for (const auto& pair : defFunctions) {
            std::cerr << pair.first << " ";
        }
        std::cerr << std::endl;
        std::exit(1);
    }

    // Call the *Def() function to get the structure definition
    FrSH* sh = it->second();

    if (!sh) {
        std::cerr << "Error: Failed to get definition for " << objectName << std::endl;
        std::exit(1);
    }

    // Print FrSH header
    std::cout << "FrSH: " << (sh->name ? sh->name : "(null)")
              << " (id=" << sh->id << ") "
              << "\"" << (sh->comment ? sh->comment : "") << "\"" << std::endl;

    // Walk the FrSE linked list and print each field
    FrSE* se = sh->firstE;
    while (se) {
        std::cout << "  FrSE: " << (se->name ? se->name : "(null)")
                  << " (" << (se->type ? se->type : "(null)") << ") "
                  << "\"" << (se->comment ? se->comment : "") << "\"" << std::endl;
        se = se->next;
    }
}

int main(int argc, char* argv[]) {
    try {
        int version;
        std::string objectName;

        // Define command line options
        po::options_description desc("Dump FrameL structure definitions");
        desc.add_options()
            ("help,h", "Show this help message")
            ("version,v", po::value<int>(&version)->required(),
             "Frame format version (3, 4, 6, 7, 8, 9)")
            ("object,o", po::value<std::string>(&objectName)->required(),
             "Object type name (e.g., FrDetector, FrVect)");

        po::variables_map vm;
        po::store(po::parse_command_line(argc, argv, desc), vm);

        if (vm.count("help")) {
            std::cout << desc << std::endl;
            std::cout << "\nExample usage:\n";
            std::cout << "  framel_dump_definition -v 3 -o FrDetector\n";
            std::cout << "\nThis tool queries the FrameL library for version-specific\n";
            std::cout << "structure definitions. It shows what FrameL uses when writing\n";
            std::cout << "frame files for the specified version.\n";
            return 0;
        }

        po::notify(vm); // Throws if required options are missing

        // Validate version
        if (version < 3 || version > 9 || version == 5) {
            std::cerr << "Error: Invalid version " << version
                      << " (must be 3, 4, 6, 7, 8, or 9)" << std::endl;
            return 1;
        }

        // Dump the definition
        dumpDefinition(objectName, version);

        return 0;

    } catch (const po::error& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        std::cerr << "Use --help for usage information" << std::endl;
        return 1;
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
}
