#include <ctype.h>
#include <iostream>
#include <string>
#include <cassert>

#include "ns3/core-module.h"
#if 1
#include "ns3/helper-module.h"
#include "ns3/node-module.h"
#include "ns3/global-route-manager.h"
#include "ns3/simulator-module.h"
#endif

using namespace ns3;
class Atag : public Tag {
public:
  static TypeId GetTypeId(void);
  virtual TypeId GetInstanceTypeId(void) const;
  virtual uint32_t GetSerializedSize(void) const;
  virtual void Serialize(TagBuffer) const;
  virtual void Deserialize(TagBuffer);

  void Print (std::ostream &os) const;
  void SetIntValue(uint32_t);
  uint32_t GetIntValue(void) const;
  void SetStrValue(const std::string &s);
  std::string GetStrValue(void) const;
private:
  uint32_t int_value;
  std::string str_value;
  const static bool debug = 0;
};


TypeId Atag::GetTypeId(void) {
  static TypeId tid = TypeId("ns3::Atag")
    .AddConstructor<Atag>() // had to add this to get Socket::PrintTags to work
    .SetParent<Tag>(); // didn't do accessors how?
  
  return tid;
}

TypeId Atag::GetInstanceTypeId(void) const {
  return GetTypeId();
}

uint32_t Atag::GetSerializedSize (void) const {
  return 2*sizeof(uint32_t) + str_value.size();
}

void Atag::Serialize (TagBuffer i) const {
  if (debug)
    std::cout << "Atag::Serialize() called" << std::endl;
  if (debug)
    std::cout << " int_value = " << int_value << std::endl;
  i.WriteU32 (int_value);
  uint32_t str_len = str_value.size();
  if (debug)
    std::cout << " str_len = " << str_len << std::endl;
  i.WriteU32 (str_len);
  for (int j = 0; j < str_value.size(); j++) {
    if (debug)
      std::cout << " str_value[" << j << "] = " << str_value[j] << std::endl;
    i.WriteU8(str_value[j]); // NOTE: assumes 8bit chars.
  }
}
  
void Atag::Deserialize (TagBuffer i) {
  if (debug)
    std::cout << "Atag::Deserialize() called" << std::endl;
  int_value = i.ReadU32 ();
  if (debug)
    std::cout << " int_value = " << int_value << std::endl;
  uint32_t str_len = i.ReadU32();
  if (debug)
    std::cout << " str_len = " << str_len << std::endl;
  str_value.reserve(str_len);
  for (int j = 0; j < str_len; j++) {
    char v = i.ReadU8();
    str_value.push_back(v);
    if (debug)
      std::cout << " str_value[" << j << "] = " << str_value[j] << std::endl;
  }
  if (debug)
    std::cout << "string is " << str_value << std::endl;
}


void Atag::SetIntValue(uint32_t v_) {
  int_value = v_;
}

uint32_t Atag::GetIntValue(void) const {
  return int_value;
}

void Atag::SetStrValue(const std::string &s) {
  str_value = s;
  if (debug)
    std::cout << "SetStrValue=" << str_value << std::endl;
}

std::string Atag::GetStrValue(void) const {
  return str_value;
}
   
void Atag::Print (std::ostream &os) const {
  os << "(Atag=" << str_value << ":" << int_value << ")" << std::endl;
}

int main() {
  Ptr<Packet> p = Create<Packet>(100);
  Atag t;
  t.SetIntValue(10);
  t.SetStrValue("hi");
  p->AddTag(t);
  Atag t2;
  p->FindFirstMatchingTag(t2);
  t2.Print(std::cout << "with Packet::FindFirstMatchingTag ");
  p->PrintTags(std::cout << "with Packet::PrintTags ");
}
