#include <stdio.h>
#include <stdint.h>
#include <memory.h>
#include "ASN1.h"


ASN1Object::ASN1Object(const std::vector<uint8_t>& asn1_encoded_data)
  : m_type(ASN1_Invalid)
{
  m_tmp = 1;

  if (asn1_encoded_data.size() < 2)
  {
    printf("not enough data to form an object\n");
    return;
  }
  uint8_t sizeByte = asn1_encoded_data[1];
  size_t size = 0;
  if (sizeByte & 0x80)
  {
    sizeByte &= 0x7F;
    if (asn1_encoded_data.size() < (sizeByte + 2))
    {
      printf("not enough data to decode the size bytes %i (data size: %li)\n", sizeByte, asn1_encoded_data.size());
      return;
    }
    for (int i = 0; i < sizeByte; i++)
    {
      size <<= 8;
      size |= asn1_encoded_data[2 + i];
    }
    if (asn1_encoded_data.size() < (sizeByte + 2 + size))
    {
      printf("not enough data to decode an object of size %li (data size: %li)\n", size, asn1_encoded_data.size());
      return;
    }
  }
  else
  {
    size = sizeByte;
    if (asn1_encoded_data.size() < (size + 2))
    {
      printf("not enough data to decode a small object of size %li (data size: %li)\n", size, asn1_encoded_data.size());
      return;
    }
    sizeByte = 0;
  }

  m_tmp = 2 + sizeByte;

  // Copy the data
  m_data.resize(size);
  memcpy(&m_data[0], &asn1_encoded_data[sizeByte + 2], size);

  // Set the type
  m_type = (ASN1Type)asn1_encoded_data[0];

  if (m_type == ASN1_Sequence || m_type == ASN1_Set)
  {
    size_t sizeRemaining = size;
    size_t offset = 0;
    while (sizeRemaining)
    {
        std::vector<uint8_t> data;
        data.resize(sizeRemaining);
        memcpy(&data[0], &m_data[offset], sizeRemaining);

        ASN1Object child(data);
        if (child.m_type == ASN1_Invalid)
        {
          printf("error with decoding the children of sequence or set\n");
          return;
        }
        m_children.push_back(child);

        sizeRemaining -= child.m_data.size() + child.m_tmp;
        offset += child.m_data.size() + child.m_tmp;
    }
  }
}


void ASN1Object::Print(int indent) const
{
  const char* ASN1TypeNames[0x32] = {
    "INVALID", "BOOLEAN", "INTEGER", "BIT_STRING", "OCTET_STRING", "NULL", "OBJECT_ID",
    0, 0, 0, 0, 0, "UTF8String", 0, 0, 0, 0, 0, 0, "PrintableString", "TeletexString",
    0, "IA5String", 0, 0, 0, 0, 0, 0, 0, "BMPString", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, "SEQUENCE", "SET"
  };
  // Encoded Length:
  //    https://msdn.microsoft.com/en-us/library/windows/desktop/bb648641(v=vs.85).aspx
  //   if len < 128, single byte, otherwise it is bit 7 + sizeof(len), followed by len

  const char* typeName = 0;
  if (m_type >= 0 && m_type < 0x32)
  {
    typeName = ASN1TypeNames[m_type];
  }
  if (!typeName)
  {
    printf("invalid type: %i\n", m_type);
    return;
  }
  for (int i = 0; i < indent; i++)
    printf(" ");
  printf("Type: %s, Size: %li\n", typeName, m_data.size());
  if (m_type == ASN1_Sequence || m_type == ASN1_Set)
    for (const ASN1Object& obj: m_children)
        obj.Print(indent + 2);
}


