
#include <sstream>
#include <iostream>
#include <fstream>
#include <libgen.h>
#include <sys/stat.h>

#include "PrintFileContents13.hh"
#include "FieldDescription.hh"
#include "ClassValues.hh"
#include "EnumValues.hh"

PrintFileContents13::PrintFileContents13() {}

/**
 */
void PrintFileContents13::printIOHeader(std::ofstream & outfile , std::string header_file_name) {
    if ( ! header_file_name.compare("S_source.hh") ) {
        header_file_name = "../S_source.hh" ;
    }
    outfile << "\n"
                "/*\n"
                " * This file was automatically generated by the ICG based on the file:\n"
                " * " << header_file_name << "\n"
                " */\n"
                "#include \"" << header_file_name << "\"\n"
                "#include <stddef.h>\n"                           // for offsetof()
                "#include \"TypeDictionary.hh\"\n"
                "#include \"EnumDataType.hh\"\n"
                "#include \"CompositeDataType.hh\"\n"
                "\n";
}

std::string PrintFileContents13::enumeration_identifier( EnumValues * e ) {

    std::ostringstream oss;
    EnumValues::NamespaceIterator nsi ;
    for ( nsi = e->namespace_begin() ; nsi != e->namespace_end() ; nsi++ ) {
        oss << *nsi << "__" ;
    }
    EnumValues::ContainerClassIterator ci ;
    for ( ci = e->container_class_begin() ; ci != e->container_class_end() ; ci++ ) {
        oss << *ci << "__" ;
    }
    oss << e->getName();
    return oss.str();
}

/** Prints enumeration attributes */
void PrintFileContents13::print_enum_attr(std::ofstream & outfile , EnumValues * e ) {
    EnumValues::NameValueIterator nvit ;

    outfile << "class type_" << enumeration_identifier(e) << " {\n"
             << "public:\n"
             << "    type_" << enumeration_identifier(e) << "() {\n"
             << "        TypeDictionary * type_dict = TypeDictionary::getInstance();\n"
             << "        EnumDataType * dataType = new EnumDataType( sizeof(enum " << e->getName() << "));\n"
             << "        try {\n"
             ;

    for ( nvit = e->begin() ; nvit != e->end() ; nvit++ ) {
        outfile << "            dataType->addEnumerator(\"" << (*nvit).first << "\" , " << (*nvit).second << ");\n" ;
    }

    outfile << "            typeDictionary->addTypeDefinition( \"" << e->getName() << "\", dataType );\n"
             << "        } catch ( std::logic_error e ) {\n"
             << "            std::cerr << e.what();\n"
             << "        }\n"
             << "    }\n"
             << "};\n"
             << "type_" << enumeration_identifier(e) << " __" << enumeration_identifier(e) << ";\n"
             << "\n"
             ;
}

/** */
std::string PrintFileContents13::class_identifier( ClassValues * c ) {

    std::ostringstream oss;
    ClassValues::NamespaceIterator nsi ;
    for ( nsi = c->namespace_begin() ; nsi != c->namespace_end() ; nsi++ ) {
        oss << *nsi << "__" ;
    }
    ClassValues::ContainerClassIterator ci ;
    for ( ci = c->container_class_begin() ; ci != c->container_class_end() ; ci++ ) {
        oss << *ci << "__" ;
    }
    oss << c->getName();
    return oss.str();
}

std::string PrintFileContents13::bit_field_getter_name(ClassValues * c, FieldDescription * f) {
    std::ostringstream oss;
    oss << " get_" << class_identifier(c) << "__" << f->getName();
    return oss.str();
}

std::string PrintFileContents13::bit_field_setter_name(ClassValues * c, FieldDescription * f) {
    std::ostringstream oss;
    oss << " set_" << class_identifier(c) << "__" << f->getName();
    return oss.str();
}

/** Prints attributes for a field */
void PrintFileContents13::print_field_attr(std::ofstream & outfile , ClassValues * c, FieldDescription * fv ) {

   if (fv->isBitField()) {
       outfile << "            " ;
       outfile << "dataType->addBitFieldMember(\"" << fv->getName() << "\", "
                << bit_field_getter_name(c,fv) << ", "
                << bit_field_setter_name(c,fv) << ");\n"
               ;
   } else {
       int ndims = fv->getNumDims();

       // For arrays and/or pointers, generate a dimension-size array.
       if (ndims) {
           outfile << "            " ;
           outfile << "unsigned int dim_" << fv->getName() << "[] = {";
           for ( unsigned int ii = 0 ; ii < ndims ; ii++ ) {
               if (ii) {
                   outfile << ",";
               }
               outfile << fv->getArrayDim(ii) ;
           }
           outfile << "};\n";
       }

       outfile << "            " ;
       if (fv->isStatic()) {
           outfile << "dataType->addStaticMember( \"";
           outfile << fv->getName()
                    << "\", &" << c->getName() << "::" << fv->getName() << ", \"" << fv->getTypeName() << "\", ";
       } else {
           outfile << "dataType->addRegularMember( \"";
           outfile << fv->getName() << "\", offsetof(" << c->getName() << ", " << fv->getName() << "), \"" << fv->getTypeName() << "\", ";
       }


       if (ndims) {
           outfile << ndims << ", " << "dim_" << fv->getName() ;
       } else {
           outfile << "0, NULL" ;
       }
       outfile <<");\n" ;

   }
}

/** Prints class attributes */
void PrintFileContents13::print_class_attr(std::ofstream & outfile , ClassValues * c ) {

    ClassValues::FieldIterator fit ;

    // Create setters and getters for any bitfields we may have.
    for ( fit = c->field_begin() ; fit != c->field_end() ; fit++ ) {

        FieldDescription * f = *fit;

        if ( f->isBitField()) {

            // Create bitfield getter.
            outfile << "static " << f->getTypeName() << " " << bit_field_getter_name(c,f) << "()(void* addr) {\n"
                     << "    return ((" << class_identifier(c) << "*)addr)->" << f->getName() << ";\n"
                     << "}\n"
                     ;

            // Create bitfield setter.
            outfile << "static void " << bit_field_setter_name(c,f) << "(void* addr, "<< f->getTypeName() << " v) {\n"
                     << "    ((" << class_identifier(c) << "*)addr)->" << f->getName() << " = v;\n"
                     << "}\n"
                     ;
        }
    }

    outfile << "class type_" << class_identifier(c) << " {\n"
             << "public:\n"
             << "    type_" << class_identifier(c) << "() {\n"
             << "        TypeDictionary * type_dict = TypeDictionary::getInstance();\n"
             << "        CompositeDataType * dataType = new CompositeDataType( sizeof( " << c->getName() << "));\n"
             << "        try {\n"
             ;

    for ( fit = c->field_begin() ; fit != c->field_end() ; fit++ ) {
        FieldDescription * field = *fit;
        if ( determinePrintAttr(c , field) ) {
            print_field_attr( outfile, c, field) ;
        }
    }

    outfile << "            typeDictionary->addTypeDefinition( \"" << c->getName() << "\", dataType );\n"
             << "        } catch ( std::logic_error e ) {\n"
             << "            std::cerr << e.what();\n"
             << "        }\n"
             << "    }\n"
             << "};\n"
             << "type_" << class_identifier(c) << " __" << class_identifier(c) << ";\n\n"
             ;
}


/** Prints io_src for a class */
void PrintFileContents13::printClass( std::ofstream & outfile , ClassValues * cv ) {
    print_class_attr(outfile, cv) ;
}

/** Prints io_src for an enum */
void PrintFileContents13::printEnum( std::ofstream & outfile , EnumValues * ev ) {
    print_enum_attr(outfile, ev) ;
}

