/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */
#include "Buffer.h"
#include "FramingContent.h" 
#include "FieldTable.h" 

qpid::framing::Buffer::Buffer(uint32_t _size) : size(_size), owner(true), position(0), limit(_size){
    data = new char[size];
}

qpid::framing::Buffer::Buffer(char* _data, uint32_t _size) : size(_size), owner(false), data(_data), position(0), limit(_size){
}

qpid::framing::Buffer::~Buffer(){
    if(owner) delete[] data;
}

void qpid::framing::Buffer::flip(){
    limit = position;
    position = 0;
}

void qpid::framing::Buffer::clear(){
    limit = size;
    position = 0;
}

void qpid::framing::Buffer::compact(){
    uint32_t p = limit - position;
    //copy p chars from position to 0
    memmove(data, data + position, p);
    limit = size;
    position = p;
}

void qpid::framing::Buffer::record(){
    r_position = position;
    r_limit = limit;
}

void qpid::framing::Buffer::restore(){
    position = r_position;
    limit = r_limit;
}

uint32_t qpid::framing::Buffer::available(){
    return limit - position;
}

char* qpid::framing::Buffer::start(){
    return data + position;
}

void qpid::framing::Buffer::move(uint32_t bytes){
    position += bytes;
}
    
void qpid::framing::Buffer::putOctet(uint8_t i){
    data[position++] = i;
}

void qpid::framing::Buffer::putShort(uint16_t i){
    uint16_t b = i;
    data[position++] = (uint8_t) (0xFF & (b >> 8));
    data[position++] = (uint8_t) (0xFF & b);
}

void qpid::framing::Buffer::putLong(uint32_t i){
    uint32_t b = i;
    data[position++] = (uint8_t) (0xFF & (b >> 24));
    data[position++] = (uint8_t) (0xFF & (b >> 16));
    data[position++] = (uint8_t) (0xFF & (b >> 8));
    data[position++] = (uint8_t) (0xFF & b);
}

void qpid::framing::Buffer::putLongLong(uint64_t i){
    uint32_t hi = i >> 32;
    uint32_t lo = i;
    putLong(hi);
    putLong(lo);
}

uint8_t qpid::framing::Buffer::getOctet(){ 
    return (uint8_t) data[position++]; 
}

uint16_t qpid::framing::Buffer::getShort(){ 
    uint16_t hi = (unsigned char) data[position++];
    hi = hi << 8;
    hi |= (unsigned char) data[position++];
    return hi;
}

uint32_t qpid::framing::Buffer::getLong(){ 
    uint32_t a = (unsigned char) data[position++];
    uint32_t b = (unsigned char) data[position++];
    uint32_t c = (unsigned char) data[position++];
    uint32_t d = (unsigned char) data[position++];
    a = a << 24;
    a |= b << 16;
    a |= c << 8;
    a |= d;
    return a;
}

uint64_t qpid::framing::Buffer::getLongLong(){
    uint64_t hi = getLong();
    uint64_t lo = getLong();
    hi = hi << 32;
    return hi | lo;
}


void qpid::framing::Buffer::putShortString(const string& s){
    uint8_t len = s.length();
    putOctet(len);
    s.copy(data + position, len);
    position += len;    
}

void qpid::framing::Buffer::putLongString(const string& s){
    uint32_t len = s.length();
    putLong(len);
    s.copy(data + position, len);
    position += len;    
}

void qpid::framing::Buffer::getShortString(string& s){
    uint8_t len = getOctet();
    s.assign(data + position, len);
    position += len;
}

void qpid::framing::Buffer::getLongString(string& s){
    uint32_t len = getLong();
    s.assign(data + position, len);
    position += len;
}

void qpid::framing::Buffer::putFieldTable(const FieldTable& t){
    t.encode(*this);
}

void qpid::framing::Buffer::getFieldTable(FieldTable& t){
    t.decode(*this);
}

void qpid::framing::Buffer::putContent(const Content& c){
    c.encode(*this);
}

void qpid::framing::Buffer::getContent(Content& c){
    c.decode(*this);
}

void qpid::framing::Buffer::putRawData(const string& s){
    uint32_t len = s.length();
    s.copy(data + position, len);
    position += len;    
}

void qpid::framing::Buffer::getRawData(string& s, uint32_t len){
    s.assign(data + position, len);
    position += len;
}

void qpid::framing::Buffer::putRawData(const uint8_t* s, size_t len){
    memcpy(data + position, s, len);
    position += len;    
}

void qpid::framing::Buffer::getRawData(uint8_t* s, size_t len){
    memcpy(s, data + position, len);
    position += len;
}
