/*
 * Copyright (c) 2020-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * pldm_buff.cpp
 *
 *  Created on: Feb 27, 2019
 *      Author: Samer Deeb
 */

#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <vector>


#include "pldm_buff.h"
#include "pldm_record_descriptor.h"
#include "pldm_dev_id_record.h"



PldmDevIdRecord::PldmDevIdRecord(u_int8_t bitmapBitLength):
        componentBitmapBitLength(bitmapBitLength), recordLength(0),
        descriptorCount(0), deviceUpdateOptionFlags(0),
        componentImageSetVersionStringType(0),
        componentImageSetVersionStringLength(0),
        firmwareDevicePackageDataLength(0), applicableComponents(NULL),
        firmwareDevicePackageData(NULL) {
}

PldmDevIdRecord::~PldmDevIdRecord() {
    if(applicableComponents) {
        delete [] applicableComponents;
    }
    if(firmwareDevicePackageData) {
        delete [] firmwareDevicePackageData;
    }
    while(!recordDescriptors.empty()) {
        delete recordDescriptors.back();
        recordDescriptors.pop_back();
    }

}

bool PldmDevIdRecord::unpack(PldmBuffer & buff) {
    buff.read(recordLength);
    buff.read(descriptorCount);
    buff.read(deviceUpdateOptionFlags);
    buff.read(componentImageSetVersionStringType);
    buff.read(componentImageSetVersionStringLength);
    buff.read(firmwareDevicePackageDataLength);
    if(componentBitmapBitLength) {
        u_int8_t applicableComponentsLen = componentBitmapBitLength/8;
        applicableComponents = new u_int8_t[applicableComponentsLen];
        buff.read(applicableComponents, applicableComponentsLen);
    }
    buff.read(componentImageSetVersionString,
            componentImageSetVersionStringLength);
    for(u_int8_t i=0; i<descriptorCount; i++) {
        PldmRecordDescriptor * descriptor = new PldmRecordDescriptor();
        descriptor->unpack(buff);
        recordDescriptors.push_back(descriptor);
    }
    if(firmwareDevicePackageDataLength) {
        buff.read(firmwareDevicePackageData, firmwareDevicePackageDataLength);
    }
    return true;
}

void PldmDevIdRecord::print(FILE * fp) {
    fprintf(fp, "recordLength: 0x%X\n", recordLength);
    fprintf(fp, "descriptorCount: 0x%X\n", descriptorCount);
    fprintf(fp, "deviceUpdateOptionFlags: 0x%X\n", deviceUpdateOptionFlags);
    fprintf(fp, "componentImageSetVersionStringType: 0x%X\n", componentImageSetVersionStringType);
    fprintf(fp, "componentImageSetVersionStringLength: 0x%X\n", componentImageSetVersionStringLength);
    fprintf(fp, "firmwareDevicePackageDataLength: 0x%X\n", firmwareDevicePackageDataLength);
    if(componentBitmapBitLength) {
        u_int8_t applicableComponentsLen = componentBitmapBitLength/8;
        for(u_int8_t i=0; i<applicableComponentsLen; i++) {
            fprintf(fp, "applicableComponents[%d]: 0x%X\n", i, applicableComponents[i]);
        }
    }
    fprintf(fp, "componentImageSetVersionString: %s\n", componentImageSetVersionString.c_str());
    for(u_int8_t i=0; i<descriptorCount; i++) {
        fprintf(fp, "recordDescriptors[%d]:\n", i);
        recordDescriptors[i]->print(fp);
    }

}

int PldmDevIdRecord::getComponentImageIndex() const {
    u_int8_t applicableComponentsLen = componentBitmapBitLength/8;
    int index = -1;
    static const u_int8_t index_map[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
    for(u_int8_t i=0; i<applicableComponentsLen; i++) {
        u_int8_t flags = applicableComponents[i];
        for(u_int8_t j=0; j<8; j++) {
            if (flags & index_map[j]) {
                index = i * 8 + j;
                break;
            }
        }
        if(index != -1)
            break;
    }
    return index;
}

std::string PldmDevIdRecord::getDevicePsid() const {
    std::string psid;
    for(u_int8_t i=0; i<descriptorCount; i++) {
        psid = recordDescriptors[i]->getPsid();
        if(psid != "")
            break;
    }
    return psid;
}

std::string PldmDevIdRecord::getDescription() const {

    std::string description;
    for(u_int8_t i=0; i<descriptorCount - 1; i++) {
        description += (recordDescriptors[i]->getDescription() + ", ");
    }
    if(descriptorCount > 0) {
        description += recordDescriptors[descriptorCount-1]->getDescription();
    }
    return description;
}
