/*
 * pxtools.h
 *
 * Copyright (C) 2006  Joshua Hoblitt
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * program; see the file COPYING. If not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifndef PXTOOLS_H
#define PXTOOLS_H 1

#include <stdio.h>
#include <strings.h>  // for strcasecmp
#include <unistd.h>   // for unlink
#include <pslib.h>
#include <psmodules.h>
#include <ippdb.h>

#include "pxtoolsErrorCodes.h"
#include "pxio.h"
#include "pxdata.h"
#include "pxtag.h"
#include "pxregister.h"
#include "pxchip.h"
#include "pxcam.h"
#include "pxtree.h"
#include "pxconfig.h"

# define MAX_ROWS 10e9
# define PXTOOL_MODE_NONE 0x0

bool pxCreateTables (pxConfig *config);
bool pxDeleteTables (pxConfig *config);

bool pxSetFaultCode(psDB *dbh, const char *tableName, psMetadata *where, psS16 code);

psExit pxerrorGetExitStatus ();

void pxUsage(FILE *stream, int argc, char **argv, const char *modeName, psMetadata *argSet);
bool pxGetOptions(FILE *stream, int argc, char **argv, pxConfig *config, psMetadata *modes, psMetadata *argSets);

#define PXOPT_ADD_MODE(option, comment, modeval, argset) \
{ \
    if (!psMetadataAddMetadata(argSets, PS_LIST_TAIL, option, 0, comment, argset)) {;\
        psError(PS_ERR_UNKNOWN, false, "failed to add argset for %s", option); \
    } \
    psFree(argset); \
\
    if (!psMetadataAddU32(modes, PS_LIST_TAIL, option, 0, comment, modeval)) {;\
        psError(PS_ERR_UNKNOWN, false, "failed to add argset for %s", option); \
    } \
}

#define PXOPT_LOOKUP_STR(var, md, key, required, ret) \
psString var; \
{ \
    bool status; \
 \
    var = psMetadataLookupStr(&status, md, key); \
    if (!status) { \
        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", key); \
        return ret; \
    } \
 \
    if (required && (!var)) { \
        psError(PS_ERR_UNKNOWN, true, "%s is required", key); \
        return ret; \
    } \
}

#define PXOPT_LOOKUP_F(var, md, key, type, required, ret) \
ps##type var; \
{ \
    bool status; \
 \
    var = psMetadataLookup##type(&status, md, key); \
    if (!status) { \
        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", key); \
        return ret; \
    } \
 \
    if (required && isnan(var)) { \
        psError(PS_ERR_UNKNOWN, true, "%s is required", key); \
        return ret; \
    } \
}

#define PXOPT_LOOKUP_F32(var, md, key, required, ret) \
    PXOPT_LOOKUP_F(var, md, key, F64, required, ret)

#define PXOPT_LOOKUP_F64(var, md, key, required, ret) \
    PXOPT_LOOKUP_F(var, md, key, F64, required, ret)

#define PXOPT_LOOKUP_PRIMITIVE(var, md, key, type, suffix, max, required, ret) \
type var; \
{ \
    bool status; \
 \
    var = psMetadataLookup##suffix(&status, md, key); \
    if (!status) { \
        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", key); \
        return ret; \
    } \
 \
    if (required && (var == max)) { \
        psError(PS_ERR_UNKNOWN, true, "%s is required", key); \
        return ret; \
    } \
}

#define PXOPT_LOOKUP_S16(var, md, key, required, ret) \
    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psS16, S16, INT16_MAX, required, ret)

#define PXOPT_LOOKUP_S32(var, md, key, required, ret) \
    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psS32, S32, INT32_MAX, required, ret)

#define PXOPT_LOOKUP_S64(var, md, key, required, ret) \
    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psS64, S64, INT64_MAX, required, ret)

#define PXOPT_LOOKUP_U16(var, md, key, required, ret) \
    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psU16, U16, UINT16_MAX, required, ret)

#define PXOPT_LOOKUP_U32(var, md, key, required, ret) \
    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psU32, U32, UINT32_MAX, required, ret)

#define PXOPT_LOOKUP_U64(var, md, key, required, ret) \
    PXOPT_LOOKUP_PRIMITIVE(var, md, key, psU64, U64, UINT64_MAX, required, ret)

#define PXOPT_LOOKUP_TIME(var, md, key, required, ret) \
psTime *var; \
{ \
    bool status; \
 \
    var = psMetadataLookupTime(&status, md, key); \
    if (!status) { \
        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", key); \
        return ret; \
    } \
 \
    if (required && (!var)) { \
        psError(PS_ERR_UNKNOWN, true, "%s is required", key); \
        return ret; \
    } \
}

#define PXOPT_LOOKUP_BOOL(var, md, key, ret) \
bool var; \
{ \
    bool status; \
 \
    var = psMetadataLookupBool(&status, md, key); \
    if (!status) { \
        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", key); \
        return ret; \
    } \
}

// XXX the PXOPT_COPY_* macros free 'to' on error

#define PXOPT_COPY_PRIMITIVE(from, to, type, suffix, oldname, newname, comment) \
{ \
    bool status = false; \
    type var = psMetadataLookup##suffix(&status, from, oldname); \
    if (!status) { \
        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for " oldname); \
        return false; \
    } \
    if (!psMetadataAdd##suffix(to, PS_LIST_TAIL, newname, PS_META_DUPLICATE_OK, comment, var)) { \
        psError(PS_ERR_UNKNOWN, false, "failed to add item " newname); \
        psFree(to); \
        return false; \
    } \
}

#define PXOPT_COPY_V(from, to, type, suffix, oldname, newname, comment) \
{ \
    bool status = false; \
    type var = psMetadataLookup##suffix(&status, from, oldname); \
    if (!status) { \
        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for " oldname); \
        return false; \
    } \
    if (var) { \
        if (!psMetadataAdd##suffix(to, PS_LIST_TAIL, newname, PS_META_DUPLICATE_OK, comment, var)) { \
            psError(PS_ERR_UNKNOWN, false, "failed to add item " newname); \
            psFree(to); \
            return false; \
        } \
    }\
}

#define PXOPT_COPY_F(from, to, type, oldname, newname, comment) \
{ \
    bool status = false; \
    ps##type var = psMetadataLookup##type(&status, from, oldname); \
    if (!status) { \
        psError(PS_ERR_UNKNOWN, false, "failed to lookup value for " oldname); \
        return false; \
    } \
    if (!isnan(var)) { \
        if (!psMetadataAdd##type(to, PS_LIST_TAIL, newname, PS_META_DUPLICATE_OK, comment, var)) { \
            psError(PS_ERR_UNKNOWN, false, "failed to add item " newname); \
            psFree(to); \
            return false; \
        } \
    } \
}

#define PXOPT_COPY_F32(from, to, oldname, newname, comment) \
  PXOPT_COPY_F(from, to, F32, oldname, newname, comment)

#define PXOPT_COPY_F64(from, to, oldname, newname, comment) \
  PXOPT_COPY_F(from, to, F64, oldname, newname, comment)

#define PXOPT_COPY_TIME(from, to, oldname, newname, comment) \
  PXOPT_COPY_V(from, to, psTime *, Time, oldname, newname, comment)

#define PXOPT_COPY_STR(from, to, oldname, newname, comment) \
  PXOPT_COPY_V(from, to, psString, Str, oldname, newname, comment)

#define PXOPT_COPY_S16(from, to, oldname, newname, comment) \
  PXOPT_COPY_PRIMITIVE(from, to, psS16, S16, oldname, newname, comment)

#define PXOPT_COPY_S32(from, to, oldname, newname, comment) \
  PXOPT_COPY_PRIMITIVE(from, to, psS32, S32, oldname, newname, comment)

#define PXOPT_COPY_S64(from, to, oldname, newname, comment) \
  PXOPT_COPY_PRIMITIVE(from, to, psS64, S64, oldname, newname, comment)

#define PXOPT_COPY_U16(from, to, oldname, newname, comment) \
  PXOPT_COPY_PRIMITIVE(from, to, psU16, U16, oldname, newname, comment)

#define PXOPT_COPY_U32(from, to, oldname, newname, comment) \
  PXOPT_COPY_PRIMITIVE(from, to, psU32, U32, oldname, newname, comment)

#define PXOPT_COPY_U64(from, to, oldname, newname, comment) \
  PXOPT_COPY_PRIMITIVE(from, to, psU64, U64, oldname, newname, comment)

#endif // PXTOOLS_H
