/**
 * 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.
 */
//
// Created by furture on 2018/5/15.
//

#include "wson_parser.h"
#include "wson.h"
#include "wson_util.h"

wson_parser::wson_parser(const char *data) {

    this->wsonBuffer = wson_buffer_from((void *) data, 1024*1024);
}

wson_parser::wson_parser(const char *data, int length) {
    this->wsonBuffer = wson_buffer_from((void *) data, length);
}

wson_parser::~wson_parser() {
    if(wsonBuffer){
        wsonBuffer->data = nullptr;
        free(wsonBuffer);
        wsonBuffer = NULL;
    }
    if(decodingBuffer != nullptr && decodingBufferSize > 0){
        delete [] decodingBuffer;
        decodingBuffer = nullptr;
    }
}

std::string wson_parser::nextMapKeyUTF8(){
    int keyLength = wson_next_uint(wsonBuffer);
    uint16_t * utf16 = ( uint16_t *)wson_next_bts(wsonBuffer, keyLength);
    std::string str;
    wson::utf16_convert_to_utf8_string(utf16, keyLength/sizeof(uint16_t), requireDecodingBuffer(keyLength*2), str);
    return  str;
}



char* wson_parser::requireDecodingBuffer(int length){
    if(decodingBufferSize <= 0 || decodingBufferSize < length){
        if(decodingBuffer != nullptr && decodingBufferSize > 0){
            delete [] decodingBuffer;
            decodingBuffer = nullptr;
        }
        decodingBuffer = new char[length];
        decodingBufferSize = length;
    }else{
        return decodingBuffer;
    }
    return decodingBuffer;
}


void wson_parser::toJSONtring(std::string &builder){
    uint8_t  type = wson_next_type(wsonBuffer);
    switch (type) {
        case WSON_UINT8_STRING_TYPE: {
            int size = wson_next_uint(wsonBuffer);
            uint8_t *utf8 = wson_next_bts(wsonBuffer, size);
            builder.append(reinterpret_cast<char*>(utf8), size);
        }
            return;
        case WSON_STRING_TYPE:
        case WSON_NUMBER_BIG_INT_TYPE:
        case WSON_NUMBER_BIG_DECIMAL_TYPE: {
                int size = wson_next_uint(wsonBuffer);
                uint16_t *utf16 = (uint16_t *) wson_next_bts(wsonBuffer, size);
                wson::utf16_convert_to_utf8_quote_string(utf16, size / sizeof(uint16_t),  requireDecodingBuffer(size*2), builder);
            }
            return;
        case WSON_NULL_TYPE:
            builder.append("\"\"");
            break;
        case WSON_NUMBER_INT_TYPE: {
                int32_t num = wson_next_int(wsonBuffer);
                wson::str_append_number(builder, num);
            }
            return;
        case WSON_NUMBER_FLOAT_TYPE: {
                float num = wson_next_float(wsonBuffer);
                wson::str_append_number(builder, num);
            }
            return;
        case WSON_NUMBER_DOUBLE_TYPE: {
               double num = wson_next_double(wsonBuffer);
               wson::str_append_number(builder, num);
            }
            return;
        case WSON_NUMBER_LONG_TYPE: {
               int64_t num = wson_next_long(wsonBuffer);
               wson::str_append_number(builder, num);
            }
            return;
        case WSON_BOOLEAN_TYPE_TRUE:
            builder.append("true");
            return;
        case WSON_BOOLEAN_TYPE_FALSE:
            builder.append("false");
            return;
        case WSON_MAP_TYPE:{
                    int length = wson_next_uint(wsonBuffer);
                    builder.append("{");
                    for(int i=0; i<length; i++){
                        int keyLength = wson_next_uint(wsonBuffer);
                        uint16_t * utf16 = ( uint16_t *)wson_next_bts(wsonBuffer, keyLength);
                        wson::utf16_convert_to_utf8_quote_string(utf16, keyLength/sizeof(uint16_t), requireDecodingBuffer(keyLength*2),  builder);
                        builder.append(":");
                        toJSONtring(builder);
                        if(i != (length - 1)){
                            builder.append(",");
                        }
                    }
                    builder.append("}");
            }
            return;
        case WSON_ARRAY_TYPE:{
                builder.append("[");
                int length = wson_next_uint(wsonBuffer);
                for(int i=0; i<length; i++){
                    toJSONtring(builder);
                    if(i != (length - 1)){
                        builder.append(",");
                    }
                }
                builder.append("]");
            }
            return;
        default:
            break;
    }
}

std::string wson_parser::nextStringUTF8(uint8_t type) {
    std::string str;
    switch (type) {//!OCLint
        case WSON_UINT8_STRING_TYPE: {
            int size = wson_next_uint(wsonBuffer);
            uint8_t *utf8 = wson_next_bts(wsonBuffer, size);
            str.append(reinterpret_cast<char *>(utf8), size);
            return str;
        }
        case WSON_STRING_TYPE:
        case WSON_NUMBER_BIG_INT_TYPE:
        case WSON_NUMBER_BIG_DECIMAL_TYPE: {
            int size = wson_next_uint(wsonBuffer);
            uint16_t *utf16 = (uint16_t *) wson_next_bts(wsonBuffer, size);
            wson::utf16_convert_to_utf8_string(utf16, size / sizeof(uint16_t), requireDecodingBuffer(size*2), str);
            return str;
        }
        case WSON_NULL_TYPE:
            str.append("");
            break;
        case WSON_NUMBER_INT_TYPE: {
              int32_t num = wson_next_int(wsonBuffer);;
               wson::str_append_number(str, num);
            }
            return str;
        case WSON_NUMBER_FLOAT_TYPE: {
            float num = wson_next_float(wsonBuffer);
            wson::str_append_number(str, num);
        }
            return str;
        case WSON_NUMBER_DOUBLE_TYPE: {
            double num = wson_next_double(wsonBuffer);
            wson::str_append_number(str, num);
        }
            return str;
        case WSON_NUMBER_LONG_TYPE: {
            int64_t num = wson_next_long(wsonBuffer);
            wson::str_append_number(str, num);
        }
            return str;
        case WSON_BOOLEAN_TYPE_TRUE:
            str.append("true");
            return str;
        case WSON_BOOLEAN_TYPE_FALSE:
            str.append("false");
            return str;
        case WSON_MAP_TYPE:
        case WSON_ARRAY_TYPE:
            wsonBuffer->position--;
            toJSONtring(str);
        default:
            break;
    }
    return str;
}

double wson_parser::nextNumber(uint8_t type) {
    switch (type) {
        case WSON_UINT8_STRING_TYPE: {
            int size = wson_next_uint(wsonBuffer);
            std::string str;
            wson_next_bts(wsonBuffer, size);
            uint8_t *utf8 = wson_next_bts(wsonBuffer, size);
            str.append(reinterpret_cast<char *>(utf8), size);
            return strtod(str.c_str(), nullptr);
        }
        case WSON_STRING_TYPE:
        case WSON_NUMBER_BIG_INT_TYPE:
        case WSON_NUMBER_BIG_DECIMAL_TYPE: {
            int size = wson_next_uint(wsonBuffer);
            std::string str;
            wson_next_bts(wsonBuffer, size);
            uint16_t *utf16 = (uint16_t *) wson_next_bts(wsonBuffer, size);
            wson::utf16_convert_to_utf8_string(utf16, size/sizeof(uint16_t), requireDecodingBuffer(size*2),str);
            return strtod(str.c_str(), nullptr);
        }
        case WSON_NULL_TYPE:
            return  0;
        case WSON_NUMBER_INT_TYPE:{
              int32_t num = wson_next_int(wsonBuffer);
              return num;
        }
        case WSON_NUMBER_FLOAT_TYPE:{
                float num = wson_next_float(wsonBuffer);
                return num;
        }
        case WSON_NUMBER_DOUBLE_TYPE:{
                double num = wson_next_double(wsonBuffer);
                return num;
        }
        case WSON_NUMBER_LONG_TYPE:{
                int64_t num = wson_next_long(wsonBuffer);
                return num;
        }
        case WSON_BOOLEAN_TYPE_TRUE:
            return 1;
        case WSON_BOOLEAN_TYPE_FALSE:
            return 0;
        case WSON_MAP_TYPE:
        case WSON_ARRAY_TYPE:
        default:
            break;
    }
    skipValue(type);
    return 0;
}

bool wson_parser::nextBool(uint8_t type) {
    if(type == WSON_BOOLEAN_TYPE_FALSE || type == WSON_NULL_TYPE){
        return false;
    }
    skipValue(type);
    return true;
}

void wson_parser::skipValue(uint8_t type) {
    switch (type) {
        case WSON_STRING_TYPE:
        case WSON_UINT8_STRING_TYPE:
        case WSON_NUMBER_BIG_INT_TYPE:
        case WSON_NUMBER_BIG_DECIMAL_TYPE: {
            int size = wson_next_uint(wsonBuffer);
            wson_next_bts(wsonBuffer, size);
            return;
        }
        case WSON_NULL_TYPE:
            break;
        case WSON_NUMBER_INT_TYPE:
             wson_next_int(wsonBuffer);
            return;
        case WSON_NUMBER_FLOAT_TYPE:
            wson_next_float(wsonBuffer);
            return;
        case WSON_NUMBER_DOUBLE_TYPE:
            wson_next_double(wsonBuffer);
            return;
        case WSON_NUMBER_LONG_TYPE:
             wson_next_long(wsonBuffer);
            return;
        case WSON_BOOLEAN_TYPE_TRUE:
            return;
        case WSON_BOOLEAN_TYPE_FALSE:
            return;
        case WSON_MAP_TYPE:{
                int length = wson_next_uint(wsonBuffer);
                for(int i=0; i<length; i++){
                    int keyLength = wson_next_uint(wsonBuffer);
                    wson_next_bts(wsonBuffer, keyLength);
                    skipValue(wson_next_type(wsonBuffer));
                }
            }
            return;
        case WSON_ARRAY_TYPE:{
                int length = wson_next_uint(wsonBuffer);
                for(int i=0; i<length; i++){
                    skipValue(wson_next_type(wsonBuffer));
                }
            }
            return;
        default:
            break;
    }
}


std::string wson_parser::toStringUTF8() {
    int position = this->wsonBuffer->position;
    this->wsonBuffer->position = 0;
    std::string json = nextStringUTF8(nextType());
    this->wsonBuffer->position = position;
    return json;
}
