/*      $NetBSD: prop_bool.c,v 1.24 2025/05/14 03:25:45 thorpej Exp $   */

/*-
* Copyright (c) 2006, 2025 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS
* 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 "prop_object_impl.h"
#include <prop/prop_bool.h>

struct _prop_bool {
       struct _prop_object     pb_obj;
       bool                    pb_value;
};

static struct _prop_bool _prop_bool_true;
static struct _prop_bool _prop_bool_false;

static const char truestr[] = "true";
static const char falsestr[] = "false";

static const struct _prop_object_type_tags _prop_bool_true_type_tags = {
       .xml_tag        =       truestr,
       .json_open_tag  =       truestr,
};
static const struct _prop_object_type_tags _prop_bool_false_type_tags = {
       .xml_tag        =       falsestr,
       .json_open_tag  =       falsestr,
};

static _prop_object_free_rv_t
               _prop_bool_free(prop_stack_t, prop_object_t *);
static bool     _prop_bool_externalize(
                               struct _prop_object_externalize_context *,
                               void *);
static _prop_object_equals_rv_t
               _prop_bool_equals(prop_object_t, prop_object_t,
                                 void **, void **,
                                 prop_object_t *, prop_object_t *);

static const struct _prop_object_type _prop_object_type_bool = {
       .pot_type       =       PROP_TYPE_BOOL,
       .pot_free       =       _prop_bool_free,
       .pot_extern     =       _prop_bool_externalize,
       .pot_equals     =       _prop_bool_equals,
};

#define prop_object_is_bool(x)          \
       ((x) != NULL && (x)->pb_obj.po_type == &_prop_object_type_bool)

/* ARGSUSED */
static _prop_object_free_rv_t
_prop_bool_free(prop_stack_t stack, prop_object_t *obj)
{
       /*
        * This should never happen as we "leak" our initial reference
        * count.
        */

       /* XXX forced assertion failure? */
       return (_PROP_OBJECT_FREE_DONE);
}

static bool
_prop_bool_externalize(struct _prop_object_externalize_context *ctx,
                      void *v)
{
       prop_bool_t pb = v;
       const struct _prop_object_type_tags *tags =
           pb->pb_value ? &_prop_bool_true_type_tags
                        : &_prop_bool_false_type_tags;

       return _prop_extern_append_empty_tag(ctx, tags);
}

/* ARGSUSED */
static _prop_object_equals_rv_t
_prop_bool_equals(prop_object_t v1, prop_object_t v2,
   void **stored_pointer1, void **stored_pointer2,
   prop_object_t *next_obj1, prop_object_t *next_obj2)
{
       prop_bool_t b1 = v1;
       prop_bool_t b2 = v2;

       if (! (prop_object_is_bool(b1) &&
              prop_object_is_bool(b2)))
               return (_PROP_OBJECT_EQUALS_FALSE);

       /*
        * Since we only ever allocate one true and one false,
        * save ourselves a couple of memory operations.
        */
       if (b1 == b2)
               return (_PROP_OBJECT_EQUALS_TRUE);
       else
               return (_PROP_OBJECT_EQUALS_FALSE);
}

_PROP_ONCE_DECL(_prop_bool_init_once)

static int
_prop_bool_init(void)
{

       _prop_object_init(&_prop_bool_true.pb_obj,
           &_prop_object_type_bool);
       _prop_bool_true.pb_value = true;

       _prop_object_init(&_prop_bool_false.pb_obj,
           &_prop_object_type_bool);
       _prop_bool_false.pb_value = false;

       return 0;
}

static prop_bool_t
_prop_bool_alloc(bool val)
{
       prop_bool_t pb;

       _PROP_ONCE_RUN(_prop_bool_init_once, _prop_bool_init);
       pb = val ? &_prop_bool_true : &_prop_bool_false;
       prop_object_retain(pb);

       return (pb);
}

/*
* prop_bool_create --
*      Create a prop_bool_t and initialize it with the
*      provided boolean value.
*/
_PROP_EXPORT prop_bool_t
prop_bool_create(bool val)
{

       return (_prop_bool_alloc(val));
}

/*
* prop_bool_copy --
*      Copy a prop_bool_t.
*/
_PROP_EXPORT prop_bool_t
prop_bool_copy(prop_bool_t opb)
{

       if (! prop_object_is_bool(opb))
               return (NULL);

       /*
        * Because we only ever allocate one true and one false, this
        * can be reduced to a simple retain operation.
        */
       prop_object_retain(opb);
       return (opb);
}

/*
* prop_bool_value --
*      Get the value of a prop_bool_t.
*/
_PROP_EXPORT bool
prop_bool_value(prop_bool_t pb)
{

       if (! prop_object_is_bool(pb))
               return (false);

       return (pb->pb_value);
}

/*
* prop_bool_true --
*      Historical alias for prop_bool_value().
*/
_PROP_EXPORT bool
prop_bool_true(prop_bool_t pb)
{
       return prop_bool_value(pb);
}

/*
* prop_bool_equals --
*      Return true if the boolean values are equivalent.
*/
_PROP_EXPORT bool
prop_bool_equals(prop_bool_t b1, prop_bool_t b2)
{
       if (!prop_object_is_bool(b1) || !prop_object_is_bool(b2))
               return (false);

       return (prop_object_equals(b1, b2));
}

/*
* _prop_bool_internalize --
*      Parse a <true/> or <false/> and return the object created from
*      the external representation.
*/

/* ARGSUSED */
bool
_prop_bool_internalize(prop_stack_t stack, prop_object_t *obj,
   struct _prop_object_internalize_context *ctx)
{
       bool val;

       /*
        * N.B. For internalizing JSON, the layer above us has
        * made it look like XML for this object type.
        */

       /* No attributes, and it must be an empty element. */
       if (ctx->poic_tagattr != NULL ||
           ctx->poic_is_empty_element == false)
               return (true);

       if (_PROP_TAG_MATCH(ctx, truestr))
               val = true;
       else {
               _PROP_ASSERT(_PROP_TAG_MATCH(ctx, falsestr));
               val = false;
       }
       *obj = prop_bool_create(val);
       return (true);
}