/*******************************************************************************
 * Copyright (c) 2025, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 *
 * 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.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include <memory>
#include <cstring>

#include "types.h"
#include "gpo.h"
#include "index.h"
#include "exception.h"

#include <assert.h>

void authdb::GpoData::setName(const char* src){
    setAuthData<char>(DataType::PolicyData,StorageType::TextStorage, this,&_Name,"gponame",src,strlen(src)+1);
}

const char * authdb::GpoData::getName(){
    return getAuthData<char>(DataType::PolicyData,StorageType::TextStorage,
                             this,&_Name,"gponame");
}

void authdb::GpoData::setDesc(const char* src){
    setAuthData<char>(DataType::PolicyData,StorageType::TextStorage,this,&_Desc,"gpodesc",src,strlen(src)+1);
}

const char * authdb::GpoData::getDesc(){
    return getAuthData<char>(DataType::PolicyData,StorageType::TextStorage,
                             this,&_Desc,"gpodesc");
}

void authdb::GpoData::setGpoValue(const bool dest){
    setAuthData<bool>(DataType::PolicyData,StorageType::BoolStorage,this,&_GpoDefault,"gpodefault",&dest,sizeof(bool));
}

bool authdb::GpoData::getGpoValue(){
    return *getAuthData<bool>(DataType::PolicyData,StorageType::BoolStorage,
                              this,&_GpoDefault,"gpodefault");
}

void authdb::GpoData::setAllowed(const std::vector<uuid::uuid> &allowed){
    size_t csize=allowed.size()*sizeof(uuid::uuid_t);

    char *data=new  char[csize];
    memset(data,0,csize);

    csize=0;

    for(const uuid::uuid &al : allowed){
        std::copy(
           &al.value[0],
           &al.value[sizeof(uuid::uuid_t)],
           data+csize
        );
        csize+=sizeof(uuid::uuid_t);
    }

    setAuthData<char>(DataType::PolicyData,StorageType::BinaryStorage,this,&_Allowed,"gpoallowed",data,csize);

    delete[] data;
}

void authdb::GpoData::getAlowed(std::vector<uuid::uuid> &allowed){
    try{
        if(!getAuthData<char>(DataType::PolicyData,StorageType::BinaryStorage,
                                                                        this,&_Allowed,"gpoallowed"))
               return;
    }catch(...){
        return;
    }

    for(size_t i =0; i<_Allowed->datasize; i+=sizeof(uuid::uuid_t) ){
        uuid::uuid_t tmp;
        std::copy(_Allowed->data+i,_Allowed->data+(i+sizeof(uuid::uuid_t)),&tmp[0]);
        allowed.emplace_back(tmp);
    }
}


authdb::GpoData::GpoData(uuid::uuid gpid) : AuthData(gpid){
    _Name=nullptr;
    _Desc=nullptr;
    _GpoDefault=nullptr;
    _Allowed=nullptr;
    Data.type=PolicyData;
}

authdb::GpoData::GpoData(const GpoData& src) : AuthData(src){
    _Name=nullptr;
    _Desc=nullptr;
    _GpoDefault=nullptr;
    _Allowed=nullptr;
    Data.type=PolicyData;
}

authdb::GpoData::~GpoData(){
}

bool authdb::Gpo::exists(AuthBackend& backend,const uuid::uuid &gpid, const char* name, int& ret){
    AuthBackend::Guard guard(backend, AuthBackend::Shared);
    RecordIndex &idx = backend.getIndex();
    if (!idx.valid() || idx.revision() != backend.getRevesion()) {
        idx.build(backend);
    }

    if (name && name[0] != '\0') {
        uuid::uuid found;
        if (idx.findByName(DataType::PolicyData, "gponame", name, found)) {
            ret = 1;
            return true;
        }
    }

    if (!gpid.empty() && idx.findByUuid(DataType::PolicyData, gpid)) {
        ret = 2;
        return true;
    }

    ret = -1;
    return false;
}

void authdb::Gpo::create(AuthBackend& backend, class GpoData* dat){
    AuthBackend::Guard guard(backend);
    int exi=0;

    if(uuid::uuid(dat->Data.ruid).empty())
        throw authdb::AuthBackendError("Create GPO: GUID not set !");

    if(exists(backend,dat->Data.ruid,dat->getName(),exi)){
        switch(exi){
            case 1:
                throw authdb::AuthBackendError("Create GPO: GPOname already Exists aborting!");
            default:
                throw authdb::AuthBackendError("Create GPO: GPIID already Exists aborting!");
        }
    }

    backend.createRecord(*dat,DataType::PolicyData);
}

void authdb::Gpo::remove(AuthBackend &backend,const uuid::uuid &gpid){
    backend.delRecord(gpid,DataType::PolicyData);
}

void authdb::Gpo::edit(AuthBackend &backend, class GpoData &dat){
    backend.editRecord(dat, DataType::PolicyData);
}

void authdb::Gpo::info(AuthBackend &backend, class GpoData& dat, size_t &pos){
    if(uuid::uuid(dat.Data.ruid).empty())
        throw AuthBackendError("gpo info gpoid required!");

    if(!backend.getRecord(dat,DataType::PolicyData))
        return;

    dat._Name=&dat.Data;
}

void authdb::Gpo::list(AuthBackend &backend,std::vector<uuid::uuid> &uuids){
    AuthBackend::Guard guard(backend, AuthBackend::Shared);
    RecordIndex &idx = backend.getIndex();
    if(!idx.valid() || idx.revision()!=backend.getRevesion())
        idx.build(backend);
    idx.listByType(DataType::PolicyData, uuids);
}
