//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / ILMI/ASN
//
//File: asnutil.cpp
//
//Version: $Revision: 1.5 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/07/07 07:01:07 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Timo Pärnänen
//      Sami Raatikainen
//
//Description:
//      See corresponding header file.
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//Licence:
//
//
//History: 

#include "asnutil.h"
#include "pf/exception.h"

AsnOid asnUtil :: stringToAsnOid(const string &string_)
{
    oidList list;    
    stringToList(string_, list);

    AsnOid oid = encode(list);
    return oid;
}

string asnUtil :: asnOidToString(const AsnOid &oid_)
{
    oidList list;
    decode(oid_, list);

    string result = listToString(list);
    return result;
}

AsnOid asnUtil :: listToAsnOid(const oidList &list_)
{
    AsnOid oid = encode(list_);
    return oid;
}

void asnUtil :: asnOidToList(const AsnOid &oid_, oidList &list_)
{
    decode(oid_, list_);
    return;
}

string asnUtil :: listToString(const oidList &list_)
{
    string result;
    oidListConstIterator iter;
    oidListConstIterator last = list_.end();
    last--;;
    
    // may include 0 (IP-/ATM-address/port etc.)
    for (iter = list_.begin(); iter != list_.end(); iter++)
    {
        string tempStr;
        int number = (*iter);
        if(number == 0)
        {
            tempStr = (char)(48);
        }
        while(number > 0)
        {
            pfUlong tempInt = number % 10;
            tempStr = (char)(tempInt + 48) + tempStr;
            number /= 10;
        }
        result += tempStr;
        
        // don't put a dot at the end of the string
        if (iter != last) 
        {
            result.append(1, '.');
        }
    }
    return result;
}

void asnUtil :: stringToList(const string &string_, oidList &list_)
{
    string temp = string_;
    while (temp.empty() == 0)
    {
        pfUlong arcValue = atol(temp.c_str());
        list_.push_back(arcValue);
        
        if (temp.find('.', 0) > temp.length())  // eg. "1.2.3"
        {
#ifndef NON_STD_STL   
            temp.erase();
#else
            temp.remove(temp.begin(), temp.end());
#endif // NON_STD_STL    
        }
        else  // eg. "1.2.3."
        {
            temp = temp.substr(temp.find_first_of('.') + 1);
        }
    }
    return;
}


//Functions: decode and encode
//
//Description:
//    These methods handle coding of AsnOid.
//    Routines are copied from Snacc AsnOid class and modified
//    to use STL list container.
//

void asnUtil :: decode(const AsnOid &oid_, oidList &list_)
{
    const char *oid = oid_.Str();
    if (oid != 0)
    {
        size_t octetLen = oid_.Len();
        
        unsigned short int firstArcNum;
        pfUlong arcNum;
        unsigned int i;

        //++TODO++
        // octetLen < 2 ?!?
        
        // un-munge first two arc numbers
        for (arcNum = 0, i=0; (i < octetLen) && (oid[i] & 0x80); i++)
        {
            arcNum = (arcNum << 7) + (oid[i] & 0x7f);
        }

        arcNum = (arcNum << 7) + (oid[i] & 0x7f);
        i++;
        firstArcNum = arcNum/40;

        if (firstArcNum > 2)
        {
            firstArcNum = 2;
        }

        list_.push_back(firstArcNum);
        list_.push_back(arcNum - (firstArcNum * 40));
            
        for (; i < octetLen; )
        {
            for (arcNum = 0; (i < octetLen) && (oid[i] & 0x80); i++)
            {
                arcNum = (arcNum << 7) + (oid[i] & 0x7f);
            }
            
            arcNum = (arcNum << 7) + (oid[i] & 0x7f);
            i++;
            list_.push_back(arcNum);
        }
    }
    // else nothing
    return;
}

AsnOid asnUtil :: encode(const oidList &list_)
{
    int size = list_.size();
    
    char buf[(size+1)*5];
    char *tmpBuf;
    size_t totalLen;
    size_t elmtLen;
    long int tmpArcNum;
    long int headArcNum;

    tmpBuf = buf;

    oidListConstIterator iter = list_.begin();
    pfUlong firstArcNum = (*iter);
    iter++;
        
    // munge together first oid arc numbers
    headArcNum = tmpArcNum = (firstArcNum * 40) + (*iter);

    // figure encoded length for this arc number
    for (elmtLen = 1; (tmpArcNum >>= 7) != 0; elmtLen++);
    
    // write bytes except the last/least significant of the head arc number
    // more bit is on
    totalLen = elmtLen;
    for (unsigned int i = 1; i < elmtLen; i++)
    {
        *(tmpBuf++) = 0x80 | (headArcNum >> ((elmtLen-i)*7));
    }

    // write least significant (more bit is off)
    *(tmpBuf++) = 0x7f & headArcNum;

    // repeat for the rest of the arc numbers
    for (++iter; iter != list_.end(); iter++)        
    {
        tmpArcNum = (*iter);
        for (elmtLen = 1; (tmpArcNum >>= 7) != 0; elmtLen++);
        
        totalLen += elmtLen;
        tmpArcNum = (*iter);
        for (unsigned int j = 1; j < elmtLen; j++)
        {
            *(tmpBuf++) = 0x80 | (tmpArcNum >> ((elmtLen-j)*7));
        }
        *(tmpBuf++) = 0x7f & tmpArcNum;
    }

    char *oid = Asn1Alloc (totalLen);
    memcpy (oid, buf, totalLen);
    AsnOid asnOid(oid, totalLen);
    delete oid;
    
    return asnOid;
}



