#include "Modules.hxx"
#include <netinet/in.h>
#include <iostream>
#include <string>

namespace __gnu_cxx {
  template<> struct hash<std::string> {
    size_t operator()(std::string const &x) const {
      return (size_t)x.size();
    }
  };
}

/******************************
 *   Modules                  *
 ******************************/

namespace StateFile {

  using namespace __gnu_cxx;

  __gnu_cxx::hash_map<std::string, Module*> Module::singleInstance_;
  
  uint8 string8_byteLength( std::string str ) {
    return 1 + ( (str.size()-1) & 0xff );
  }


  Module::Module( std::string name, bool skip ) :
    version_(0), versionRange_(0),
    name_(name), contentProfile_(""), contentVarNames_(""), extra_(NULL),
    content_(NULL),
    skip_(skip), read_(false), written_(false) {
  }


  Module* Module::getInstance( std::string name, bool skip ) {

    if STATEFILE_MODULE_DEBUG
      std::cout << "Module::getInstance: Name = " << name << std::endl;

    if STATEFILE_MODULE_DEBUG
      std::cout << "Module::getInstance: Creating temp pair holder." << std::endl;

    if STATEFILE_MODULE_DEBUG
      std::pair<std::string,Module*> p;

    if STATEFILE_MODULE_DEBUG
      std::cout << "Module::getInstance: Creating hash_map iterator." << std::endl;

    hash_map<std::string, Module*>::iterator i;

    if STATEFILE_MODULE_DEBUG
      std::cout << "Module::getInstance: Running singleInstance_.find( " << name << " )" << std::endl;
    
    i = singleInstance_.find(name);

    if( i != singleInstance_.end() ) {
      if ( (*i).second != NULL ) {

	if STATEFILE_MODULE_DEBUG
	  std::cout << "Module::getInstance: *i.second found to be valid value." << std::endl;

	return (*i).second;
      }
    }
    if STATEFILE_MODULE_DEBUG
      std::cout << "Module::getInstance: not found.  Creating new module..." << std::endl;

    Module* M = new Module(name, skip);
    
    if STATEFILE_MODULE_DEBUG
      std::cout << "Module::getInstance: Inserting pointer to module into singleInstance_." << std::endl;

    singleInstance_[ name ] = M;

    if STATEFILE_MODULE_DEBUG
      std::cout << "Module::getInstance: Returning pointer to module." << std::endl;
    
    return M;
  }

  Module::~Module( void ) {
    // FIXME:
    //   slight memory leak by not deleting 
    //    'content_' and 'extra_'
    //   but without a smarter system, blanket deleting
    //   them causes double free/segfault issues.
  }
  
  uint32 Module::byteLength_content() {
    if (skip_ || !content_) 
      if (written_)
	return 4; // 32-bit representation of 0
      else
	return 0; // we skip this module entirely
  
    return content_->byteLength();
  }

  uint32 Module::byteLength_header() {

    uint32 result = 0;

    if (skip_) 
      return result;

    // length A (8-bit)
    // string A (8-bit string of length A)
    result += string8_byteLength( name_ ) + 1;
    // version number (32-bit)
    result += 4; 
    // length B (8-bit)
    // string B (8-bit string of length B)
    result += string8_byteLength( contentProfile_ ) + 1;
    // length C (8-bit)
    // string C (8-bit string of length C)
    result += string8_byteLength( contentVarNames_ ) + 1; 
    // length D (8-bit)
    // string D (8-bit string of length D)
    if (extra_)  result += extra_->byteLength();
    else         result += 1;
    
    return result;
  }

  bool Module::versionCompare( uint32 other ) {
    return ( other <= version_ && other + versionRange_ >= version_ );  
  }


  ByteData32* Module::getContent( void ) {
    if( content_ ) 
      return content_;

    content_ = new ByteData32();
    return content_;
  }

  uint8* Module::getContentData( void ) {
    return getContent()->getData();
  }

  bool Module::fileWrite_Content( std::fstream *file ) {
    if (!file) return false;

    if ( skip_ || !content_ ) {
      if (written_) { // header in file, so field expected
	uint32 zero = 0;
	file->write( (char*)&zero, 4 );
      }
      return true;
    }

    return content_->fileWrite( file );
  }

  bool Module::fileWrite_Header( std::fstream *file ) {
    uint8 strLen = 0, zero = 0;

    if (!file ) return false;

    if (skip_) return true;

    written_ = true;

    // length A (8-bit)
    strLen = string8_byteLength( name_ );
    file->write( (char*)&strLen, 1 );
    // string A (8-bit string of length A)
    file->write( name_.c_str(), strLen );

    uint32 netVersion = htonl( version_ );
    // version number (32-bit)
    file->write( (char*)&netVersion, 4 );
    
    // the rest are "optional" (still requires a '0' per field)

    if (contentProfile_.size()) {
      // length B (8-bit)
      strLen = string8_byteLength( contentProfile_ );
      file->write( (char*)&strLen, 1 );
      // string B (8-bit string of length B)
      file->write( contentProfile_.c_str(), strLen );
    } else
      file->write( (char*)&zero, 1 );

    if (contentVarNames_.size()) {
      // length C (8-bit)
      strLen = string8_byteLength( contentVarNames_ );
      file->write( (char*)&strLen, 1 );
      // string C (8-bit string of length C)
      file->write( contentVarNames_.c_str(), strLen );
    } else
      file->write( (char*)&zero, 1 );
    
    if (extra_) {
      if (! extra_->fileWrite( file ) )
	return false;
    } else
      file->write( (char*)&zero, 1 );

    return true;
  }

  bool Module::fileRead( std::fstream *file ) {
    if (content_ == NULL)
      content_ = new ByteData32();

    read_ =  content_->fileRead( file, skip_ );
    return read_;
  }

} /* namespace */
