/*
 *
 * 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 "qpid/amqp/Decoder.h"
#include "qpid/amqp/CharSequence.h"
#include "qpid/amqp/Constructor.h"
#include "qpid/amqp/Descriptor.h"
#include "qpid/amqp/Reader.h"
#include "qpid/amqp/typecodes.h"
#include "qpid/types/Uuid.h"
#include "qpid/types/Variant.h"
#include "qpid/log/Statement.h"
#include "qpid/Exception.h"

namespace qpid {
namespace amqp {

using namespace qpid::amqp::typecodes;

Decoder::Decoder(const char* d, size_t s) : start(d), size(s), position(0) {}

namespace {
class MapBuilder : public Reader
{
  public:
    void onNull(const Descriptor*)
    {
        qpid::types::Variant v;
        handle(v, NULL_NAME);
    }
    void onBoolean(bool v, const Descriptor*)
    {
        handle(v, BOOLEAN_NAME);
    }
    void onUByte(uint8_t v, const Descriptor*)
    {
        handle(v, UBYTE_NAME);
    }
    void onUShort(uint16_t v, const Descriptor*)
    {
        handle(v, USHORT_NAME);
    }
    void onUInt(uint32_t v, const Descriptor*)
    {
        handle(v, UINT_NAME);
    }
    void onULong(uint64_t v, const Descriptor*)
    {
        handle(v, ULONG_NAME);
    }
    void onByte(int8_t v, const Descriptor*)
    {
        handle(v, BYTE_NAME);
    }
    void onShort(int16_t v, const Descriptor*)
    {
        handle(v, SHORT_NAME);
    }
    void onInt(int32_t v, const Descriptor*)
    {
        handle(v, INT_NAME);
    }
    void onLong(int64_t v, const Descriptor*)
    {
        handle(v, LONG_NAME);
    }
    void onFloat(float v, const Descriptor*)
    {
        handle(v, FLOAT_NAME);
    }
    void onDouble(double v, const Descriptor*)
    {
        handle(v, DOUBLE_NAME);
    }
    void onUuid(const CharSequence& v, const Descriptor*)
    {
        handle(v, UUID_NAME);
    }
    void onTimestamp(int64_t v, const Descriptor*)
    {
        handle(v, TIMESTAMP_NAME);
    }
    void onBinary(const CharSequence& v, const Descriptor*)
    {
        handle(v);
    }
    void onString(const CharSequence& v, const Descriptor*)
    {
        handle(v);
    }
    void onSymbol(const CharSequence& v, const Descriptor*)
    {
        handle(v);
    }
    MapBuilder(qpid::types::Variant::Map& m) : map(m), state(KEY) {}
  private:
    qpid::types::Variant::Map& map;
    enum {KEY, SKIP, VALUE} state;
    std::string key;

    template <typename T> void handle(T value, const std::string& name)
    {
        switch (state) {
          case KEY:
            QPID_LOG(warning, "Ignoring key of type " << name);
            state = SKIP;
            break;
          case VALUE:
            map[key] = value;
          case SKIP:
            state = KEY;
            break;
        }
    }
    void handle(const CharSequence& value)
    {
        switch (state) {
          case KEY:
            key = value.str();
            state = VALUE;
            break;
          case VALUE:
            map[key] = value.str();
          case SKIP:
            state = KEY;
            break;
        }
    }
};
}
void Decoder::readMap(qpid::types::Variant::Map& map)
{
    MapBuilder builder(map);
    read(builder);
}

qpid::types::Variant::Map Decoder::readMap()
{
    qpid::types::Variant::Map map;
    readMap(map);
    return map;
}

void Decoder::read(Reader& reader)
{
    while (available() && reader.proceed()) {
        readOne(reader);
    }
}

void Decoder::readOne(Reader& reader)
{
    const char* temp = start + position;
    Constructor c = readConstructor();
    if (c.isDescribed) reader.onDescriptor(c.descriptor, temp);
    readValue(reader, c.code, c.isDescribed ? &c.descriptor : 0);
}

void Decoder::readValue(Reader& reader, uint8_t code, const Descriptor* descriptor)
{
    switch(code) {
      case NULL_VALUE:
        reader.onNull(descriptor);
        break;
      case BOOLEAN:
        reader.onBoolean(readBoolean(), descriptor);
        break;
      case BOOLEAN_TRUE:
        reader.onBoolean(true, descriptor);
        break;
      case BOOLEAN_FALSE:
        reader.onBoolean(false, descriptor);
        break;
      case UBYTE:
        reader.onUByte(readUByte(), descriptor);
        break;
      case USHORT:
        reader.onUShort(readUShort(), descriptor);
        break;
      case UINT:
        reader.onUInt(readUInt(), descriptor);
        break;
      case UINT_SMALL:
        reader.onUInt(readUByte(), descriptor);
        break;
      case UINT_ZERO:
        reader.onUInt(0, descriptor);
        break;
      case ULONG:
        reader.onULong(readULong(), descriptor);
        break;
      case ULONG_SMALL:
        reader.onULong(readUByte(), descriptor);
        break;
      case ULONG_ZERO:
        reader.onULong(0, descriptor);
        break;
      case BYTE:
        reader.onByte(readByte(), descriptor);
        break;
      case SHORT:
        reader.onShort(readShort(), descriptor);
        break;
      case INT:
        reader.onInt(readInt(), descriptor);
        break;
      case INT_SMALL:
        reader.onInt(readByte(), descriptor);
        break;
      case LONG:
        reader.onLong(readLong(), descriptor);
        break;
      case LONG_SMALL:
        reader.onLong(readByte(), descriptor);
        break;
      case FLOAT:
        reader.onFloat(readFloat(), descriptor);
        break;
      case DOUBLE:
        reader.onDouble(readDouble(), descriptor);
        break;
      case UUID:
        reader.onUuid(readRawUuid(), descriptor);
        break;
      case TIMESTAMP:
        reader.onTimestamp(readLong(), descriptor);
        break;

      case BINARY8:
        reader.onBinary(readSequence8(), descriptor);
        break;
      case BINARY32:
        reader.onBinary(readSequence32(), descriptor);
        break;
      case STRING8:
        reader.onString(readSequence8(), descriptor);
        break;
      case STRING32:
        reader.onString(readSequence32(), descriptor);
        break;
      case SYMBOL8:
        reader.onSymbol(readSequence8(), descriptor);
        break;
      case SYMBOL32:
        reader.onSymbol(readSequence32(), descriptor);
        break;

      case LIST0:
        reader.onStartList(0, CharSequence::create(0, 0), descriptor);
        reader.onEndList(0, descriptor);
        break;
      case LIST8:
        readList8(reader, descriptor);
        break;
      case LIST32:
        readList32(reader, descriptor);
        break;
      case MAP8:
        readMap8(reader, descriptor);
        break;
      case MAP32:
        readMap32(reader, descriptor);
        break;
      case ARRAY8:
        readArray8(reader, descriptor);
        break;
      case ARRAY32:
        readArray32(reader, descriptor);
        break;
      default:
        break;
    }
}

void Decoder::readList8(Reader& reader, const Descriptor* descriptor)
{
    uint8_t size = readUByte();
    uint8_t count = readUByte();
    readList(reader, size-sizeof(size), count, descriptor);
}

void Decoder::readList32(Reader& reader, const Descriptor* descriptor)
{
    uint32_t size = readUInt();
    uint32_t count = readUInt();
    readList(reader, size-sizeof(size), count, descriptor);
}

void Decoder::readMap8(Reader& reader, const Descriptor* descriptor)
{
    uint8_t size = readUByte();
    uint8_t count = readUByte();
    readMap(reader, size-sizeof(size), count, descriptor);
}

void Decoder::readMap32(Reader& reader, const Descriptor* descriptor)
{
    uint32_t size = readUInt();
    uint32_t count = readUInt();
    readMap(reader, size-sizeof(size), count, descriptor);
}

void Decoder::readArray8(Reader& reader, const Descriptor* descriptor)
{
    uint8_t size = readUByte();
    uint8_t count = readUByte();
    readArray(reader, size-sizeof(size), count, descriptor);
}

void Decoder::readArray32(Reader& reader, const Descriptor* descriptor)
{
    uint32_t size = readUInt();
    uint32_t count = readUInt();
    readArray(reader, size-sizeof(size), count, descriptor);
}

void Decoder::readList(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor)
{
    if (reader.onStartList(count, CharSequence::create(data(), size), descriptor)) {
        for (uint32_t i = 0; i < count; ++i) {
            readOne(reader);
        }
        reader.onEndList(count, descriptor);
    } else {
        //skip
        advance(size);
    }
}
void Decoder::readMap(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor)
{
    if (reader.onStartMap(count, CharSequence::create(data(), size), descriptor)) {
        for (uint32_t i = 0; i < count; ++i) {
            readOne(reader);
        }
        reader.onEndMap(count, descriptor);
    } else {
        //skip
        advance(size);
    }
}

void Decoder::readArray(Reader& reader, uint32_t size, uint32_t count, const Descriptor* descriptor)
{
    size_t temp = position;
    Constructor constructor = readConstructor();
    CharSequence raw = CharSequence::create(data(), size-(position-temp));
    if (reader.onStartArray(count, raw, constructor, descriptor)) {
        for (uint32_t i = 0; i < count; ++i) {
            readValue(reader, constructor.code, constructor.isDescribed ? &constructor.descriptor : 0);
        }
        reader.onEndArray(count, descriptor);
    } else {
        //skip
        advance(raw.size);
    }
}


Constructor Decoder::readConstructor()
{
    Constructor result(readCode());
    if (result.code == DESCRIPTOR) {
        result.isDescribed = true;
        result.descriptor = readDescriptor();
        result.code = readCode();
    } else {
        result.isDescribed = false;
    }
    return result;
}

Descriptor Decoder::readDescriptor()
{
    uint8_t code = readCode();
    switch(code) {
      case SYMBOL8:
        return Descriptor(readSequence8());
      case SYMBOL32:
        return Descriptor(readSequence32());
      case ULONG:
        return Descriptor(readULong());
      case ULONG_SMALL:
        return Descriptor((uint64_t) readUByte());
      case ULONG_ZERO:
        return Descriptor((uint64_t) 0);
      default:
        throw qpid::Exception(QPID_MSG("Expected descriptor of type ulong or symbol; found " << code));
    }
}

void Decoder::advance(size_t n)
{
    if (n > available()) throw qpid::Exception(QPID_MSG("Out of Bounds"));
    position += n;
}

const char* Decoder::data()
{
    return start + position;
}

size_t Decoder::available()
{
    return size - position;
}

uint8_t Decoder::readCode()
{
    return readUByte();
}

bool Decoder::readBoolean()
{
    return readUByte();
}

uint8_t Decoder::readUByte()
{
    return static_cast<uint8_t>(start[position++]);
}

uint16_t Decoder::readUShort()
{
    uint16_t hi = (unsigned char) start[position++];
    hi = hi << 8;
    hi |= (unsigned char) start[position++];
    return hi;
}

uint32_t Decoder::readUInt()
{
    uint32_t a = (unsigned char) start[position++];
    uint32_t b = (unsigned char) start[position++];
    uint32_t c = (unsigned char) start[position++];
    uint32_t d = (unsigned char) start[position++];
    a = a << 24;
    a |= b << 16;
    a |= c << 8;
    a |= d;
    return a;
}

uint64_t Decoder::readULong()
{
    uint64_t hi =readUInt();
    uint64_t lo = readUInt();
    hi = hi << 32;
    return hi | lo;
}

int8_t Decoder::readByte()
{
    return (int8_t) readUByte();
}

int16_t Decoder::readShort()
{
    return (int16_t) readUShort();
}

int32_t Decoder::readInt()
{
    return (int32_t) readUInt();
}

int64_t Decoder::readLong()
{
    return (int64_t) readULong();
}

float Decoder::readFloat()
{
    union {
        uint32_t i;
        float    f;
    } val;
    val.i = readUInt();
    return val.f;
}

double Decoder::readDouble()
{
    union {
        uint64_t i;
        double   f;
    } val;
    val.i = readULong();
    return val.f;
}

CharSequence Decoder::readSequence8()
{
    CharSequence s;
    s.size = readUByte();
    s.data = start + position;
    advance(s.size);
    return s;
}

CharSequence Decoder::readSequence32()
{
    CharSequence s;
    s.size = readUInt();
    s.data = start + position;
    advance(s.size);
    return s;
}

qpid::types::Uuid Decoder::readUuid()
{
    qpid::types::Uuid uuid(start + position);
    advance(16);
    return uuid;
}

CharSequence Decoder::readRawUuid()
{
    CharSequence s;
    s.data = start + position;
    s.size = 16;
    advance(s.size);
    return s;
}

size_t Decoder::getPosition() const { return position; }
size_t Decoder::getSize() const { return size; }
void Decoder::resetSize(size_t s) { size = s; }
}} // namespace qpid::amqp
