// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Code generated by Microsoft (R) TypeSpec Code Generator.

package tsptest.armstreamstyleserialization.models;

import com.azure.core.annotation.Fluent;
import com.azure.core.util.BinaryData;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.logging.ClientLogger;
import com.azure.json.JsonReader;
import com.azure.json.JsonSerializable;
import com.azure.json.JsonToken;
import com.azure.json.JsonWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * The Builtin model.
 */
@Fluent
public final class Builtin implements JsonSerializable<Builtin> {
    /*
     * The boolean property.
     */
    private boolean booleanProperty;

    /*
     * The string property.
     */
    private String string;

    /*
     * The bytes property.
     */
    private byte[] bytes;

    /*
     * The int property.
     */
    private int intProperty;

    /*
     * The safeint property.
     */
    private long safeint;

    /*
     * The decimal property.
     */
    private BigDecimal decimal;

    /*
     * The long property.
     */
    private long longProperty;

    /*
     * The float property.
     */
    private double floatProperty;

    /*
     * The double property.
     */
    private double doubleProperty;

    /*
     * The duration property.
     */
    private Duration duration;

    /*
     * The date property.
     */
    private LocalDate date;

    /*
     * The dateTime property.
     */
    private OffsetDateTime dateTime;

    /*
     * The stringList property.
     */
    private List<String> stringList;

    /*
     * The bytesDict property.
     */
    private Map<String, byte[]> bytesDict;

    /*
     * The url property.
     */
    private String url;

    /*
     * The nullableFloatDict property.
     */
    private Map<String, Double> nullableFloatDict;

    /*
     * The encoding property.
     */
    private Encoded encoding;

    /*
     * The uuid property.
     */
    private String uuid;

    /*
     * The unknown property.
     */
    private BinaryData unknown;

    /*
     * The unknownDict property.
     */
    private Map<String, BinaryData> unknownDict;

    /*
     * The unknownArray property.
     */
    private List<BinaryData> unknownArray;

    /*
     * The unknownDictArray property.
     */
    private List<Map<String, BinaryData>> unknownDictArray;

    /**
     * Creates an instance of Builtin class.
     */
    public Builtin() {
    }

    /**
     * Get the booleanProperty property: The boolean property.
     * 
     * @return the booleanProperty value.
     */
    public boolean booleanProperty() {
        return this.booleanProperty;
    }

    /**
     * Set the booleanProperty property: The boolean property.
     * 
     * @param booleanProperty the booleanProperty value to set.
     * @return the Builtin object itself.
     */
    public Builtin withBooleanProperty(boolean booleanProperty) {
        this.booleanProperty = booleanProperty;
        return this;
    }

    /**
     * Get the string property: The string property.
     * 
     * @return the string value.
     */
    public String string() {
        return this.string;
    }

    /**
     * Set the string property: The string property.
     * 
     * @param string the string value to set.
     * @return the Builtin object itself.
     */
    public Builtin withString(String string) {
        this.string = string;
        return this;
    }

    /**
     * Get the bytes property: The bytes property.
     * 
     * @return the bytes value.
     */
    public byte[] bytes() {
        return CoreUtils.clone(this.bytes);
    }

    /**
     * Set the bytes property: The bytes property.
     * 
     * @param bytes the bytes value to set.
     * @return the Builtin object itself.
     */
    public Builtin withBytes(byte[] bytes) {
        this.bytes = CoreUtils.clone(bytes);
        return this;
    }

    /**
     * Get the intProperty property: The int property.
     * 
     * @return the intProperty value.
     */
    public int intProperty() {
        return this.intProperty;
    }

    /**
     * Set the intProperty property: The int property.
     * 
     * @param intProperty the intProperty value to set.
     * @return the Builtin object itself.
     */
    public Builtin withIntProperty(int intProperty) {
        this.intProperty = intProperty;
        return this;
    }

    /**
     * Get the safeint property: The safeint property.
     * 
     * @return the safeint value.
     */
    public long safeint() {
        return this.safeint;
    }

    /**
     * Set the safeint property: The safeint property.
     * 
     * @param safeint the safeint value to set.
     * @return the Builtin object itself.
     */
    public Builtin withSafeint(long safeint) {
        this.safeint = safeint;
        return this;
    }

    /**
     * Get the decimal property: The decimal property.
     * 
     * @return the decimal value.
     */
    public BigDecimal decimal() {
        return this.decimal;
    }

    /**
     * Set the decimal property: The decimal property.
     * 
     * @param decimal the decimal value to set.
     * @return the Builtin object itself.
     */
    public Builtin withDecimal(BigDecimal decimal) {
        this.decimal = decimal;
        return this;
    }

    /**
     * Get the longProperty property: The long property.
     * 
     * @return the longProperty value.
     */
    public long longProperty() {
        return this.longProperty;
    }

    /**
     * Set the longProperty property: The long property.
     * 
     * @param longProperty the longProperty value to set.
     * @return the Builtin object itself.
     */
    public Builtin withLongProperty(long longProperty) {
        this.longProperty = longProperty;
        return this;
    }

    /**
     * Get the floatProperty property: The float property.
     * 
     * @return the floatProperty value.
     */
    public double floatProperty() {
        return this.floatProperty;
    }

    /**
     * Set the floatProperty property: The float property.
     * 
     * @param floatProperty the floatProperty value to set.
     * @return the Builtin object itself.
     */
    public Builtin withFloatProperty(double floatProperty) {
        this.floatProperty = floatProperty;
        return this;
    }

    /**
     * Get the doubleProperty property: The double property.
     * 
     * @return the doubleProperty value.
     */
    public double doubleProperty() {
        return this.doubleProperty;
    }

    /**
     * Set the doubleProperty property: The double property.
     * 
     * @param doubleProperty the doubleProperty value to set.
     * @return the Builtin object itself.
     */
    public Builtin withDoubleProperty(double doubleProperty) {
        this.doubleProperty = doubleProperty;
        return this;
    }

    /**
     * Get the duration property: The duration property.
     * 
     * @return the duration value.
     */
    public Duration duration() {
        return this.duration;
    }

    /**
     * Set the duration property: The duration property.
     * 
     * @param duration the duration value to set.
     * @return the Builtin object itself.
     */
    public Builtin withDuration(Duration duration) {
        this.duration = duration;
        return this;
    }

    /**
     * Get the date property: The date property.
     * 
     * @return the date value.
     */
    public LocalDate date() {
        return this.date;
    }

    /**
     * Set the date property: The date property.
     * 
     * @param date the date value to set.
     * @return the Builtin object itself.
     */
    public Builtin withDate(LocalDate date) {
        this.date = date;
        return this;
    }

    /**
     * Get the dateTime property: The dateTime property.
     * 
     * @return the dateTime value.
     */
    public OffsetDateTime dateTime() {
        return this.dateTime;
    }

    /**
     * Set the dateTime property: The dateTime property.
     * 
     * @param dateTime the dateTime value to set.
     * @return the Builtin object itself.
     */
    public Builtin withDateTime(OffsetDateTime dateTime) {
        this.dateTime = dateTime;
        return this;
    }

    /**
     * Get the stringList property: The stringList property.
     * 
     * @return the stringList value.
     */
    public List<String> stringList() {
        return this.stringList;
    }

    /**
     * Set the stringList property: The stringList property.
     * 
     * @param stringList the stringList value to set.
     * @return the Builtin object itself.
     */
    public Builtin withStringList(List<String> stringList) {
        this.stringList = stringList;
        return this;
    }

    /**
     * Get the bytesDict property: The bytesDict property.
     * 
     * @return the bytesDict value.
     */
    public Map<String, byte[]> bytesDict() {
        return this.bytesDict;
    }

    /**
     * Set the bytesDict property: The bytesDict property.
     * 
     * @param bytesDict the bytesDict value to set.
     * @return the Builtin object itself.
     */
    public Builtin withBytesDict(Map<String, byte[]> bytesDict) {
        this.bytesDict = bytesDict;
        return this;
    }

    /**
     * Get the url property: The url property.
     * 
     * @return the url value.
     */
    public String url() {
        return this.url;
    }

    /**
     * Set the url property: The url property.
     * 
     * @param url the url value to set.
     * @return the Builtin object itself.
     */
    public Builtin withUrl(String url) {
        this.url = url;
        return this;
    }

    /**
     * Get the nullableFloatDict property: The nullableFloatDict property.
     * 
     * @return the nullableFloatDict value.
     */
    public Map<String, Double> nullableFloatDict() {
        return this.nullableFloatDict;
    }

    /**
     * Set the nullableFloatDict property: The nullableFloatDict property.
     * 
     * @param nullableFloatDict the nullableFloatDict value to set.
     * @return the Builtin object itself.
     */
    public Builtin withNullableFloatDict(Map<String, Double> nullableFloatDict) {
        this.nullableFloatDict = nullableFloatDict;
        return this;
    }

    /**
     * Get the encoding property: The encoding property.
     * 
     * @return the encoding value.
     */
    public Encoded encoding() {
        return this.encoding;
    }

    /**
     * Set the encoding property: The encoding property.
     * 
     * @param encoding the encoding value to set.
     * @return the Builtin object itself.
     */
    public Builtin withEncoding(Encoded encoding) {
        this.encoding = encoding;
        return this;
    }

    /**
     * Get the uuid property: The uuid property.
     * 
     * @return the uuid value.
     */
    public String uuid() {
        return this.uuid;
    }

    /**
     * Set the uuid property: The uuid property.
     * 
     * @param uuid the uuid value to set.
     * @return the Builtin object itself.
     */
    public Builtin withUuid(String uuid) {
        this.uuid = uuid;
        return this;
    }

    /**
     * Get the unknown property: The unknown property.
     * 
     * @return the unknown value.
     */
    public BinaryData unknown() {
        return this.unknown;
    }

    /**
     * Set the unknown property: The unknown property.
     * 
     * @param unknown the unknown value to set.
     * @return the Builtin object itself.
     */
    public Builtin withUnknown(BinaryData unknown) {
        this.unknown = unknown;
        return this;
    }

    /**
     * Get the unknownDict property: The unknownDict property.
     * 
     * @return the unknownDict value.
     */
    public Map<String, BinaryData> unknownDict() {
        return this.unknownDict;
    }

    /**
     * Set the unknownDict property: The unknownDict property.
     * 
     * @param unknownDict the unknownDict value to set.
     * @return the Builtin object itself.
     */
    public Builtin withUnknownDict(Map<String, BinaryData> unknownDict) {
        this.unknownDict = unknownDict;
        return this;
    }

    /**
     * Get the unknownArray property: The unknownArray property.
     * 
     * @return the unknownArray value.
     */
    public List<BinaryData> unknownArray() {
        return this.unknownArray;
    }

    /**
     * Set the unknownArray property: The unknownArray property.
     * 
     * @param unknownArray the unknownArray value to set.
     * @return the Builtin object itself.
     */
    public Builtin withUnknownArray(List<BinaryData> unknownArray) {
        this.unknownArray = unknownArray;
        return this;
    }

    /**
     * Get the unknownDictArray property: The unknownDictArray property.
     * 
     * @return the unknownDictArray value.
     */
    public List<Map<String, BinaryData>> unknownDictArray() {
        return this.unknownDictArray;
    }

    /**
     * Set the unknownDictArray property: The unknownDictArray property.
     * 
     * @param unknownDictArray the unknownDictArray value to set.
     * @return the Builtin object itself.
     */
    public Builtin withUnknownDictArray(List<Map<String, BinaryData>> unknownDictArray) {
        this.unknownDictArray = unknownDictArray;
        return this;
    }

    /**
     * Validates the instance.
     * 
     * @throws IllegalArgumentException thrown if the instance is not valid.
     */
    public void validate() {
        if (string() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property string in model Builtin"));
        }
        if (bytes() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property bytes in model Builtin"));
        }
        if (decimal() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property decimal in model Builtin"));
        }
        if (duration() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property duration in model Builtin"));
        }
        if (date() == null) {
            throw LOGGER.atError().log(new IllegalArgumentException("Missing required property date in model Builtin"));
        }
        if (dateTime() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property dateTime in model Builtin"));
        }
        if (stringList() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property stringList in model Builtin"));
        }
        if (bytesDict() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property bytesDict in model Builtin"));
        }
        if (url() == null) {
            throw LOGGER.atError().log(new IllegalArgumentException("Missing required property url in model Builtin"));
        }
        if (nullableFloatDict() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property nullableFloatDict in model Builtin"));
        }
        if (encoding() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property encoding in model Builtin"));
        } else {
            encoding().validate();
        }
        if (uuid() == null) {
            throw LOGGER.atError().log(new IllegalArgumentException("Missing required property uuid in model Builtin"));
        }
        if (unknown() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property unknown in model Builtin"));
        }
        if (unknownDict() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property unknownDict in model Builtin"));
        }
        if (unknownArray() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property unknownArray in model Builtin"));
        }
        if (unknownDictArray() == null) {
            throw LOGGER.atError()
                .log(new IllegalArgumentException("Missing required property unknownDictArray in model Builtin"));
        }
    }

    private static final ClientLogger LOGGER = new ClientLogger(Builtin.class);

    /**
     * {@inheritDoc}
     */
    @Override
    public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
        jsonWriter.writeStartObject();
        jsonWriter.writeBooleanField("boolean", this.booleanProperty);
        jsonWriter.writeStringField("string", this.string);
        jsonWriter.writeBinaryField("bytes", this.bytes);
        jsonWriter.writeIntField("int", this.intProperty);
        jsonWriter.writeLongField("safeint", this.safeint);
        jsonWriter.writeNumberField("decimal", this.decimal);
        jsonWriter.writeLongField("long", this.longProperty);
        jsonWriter.writeDoubleField("float", this.floatProperty);
        jsonWriter.writeDoubleField("double", this.doubleProperty);
        jsonWriter.writeStringField("duration", CoreUtils.durationToStringWithDays(this.duration));
        jsonWriter.writeStringField("date", Objects.toString(this.date, null));
        jsonWriter.writeStringField("dateTime",
            this.dateTime == null ? null : DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(this.dateTime));
        jsonWriter.writeArrayField("stringList", this.stringList, (writer, element) -> writer.writeString(element));
        jsonWriter.writeMapField("bytesDict", this.bytesDict, (writer, element) -> writer.writeBinary(element));
        jsonWriter.writeStringField("url", this.url);
        jsonWriter.writeMapField("nullableFloatDict", this.nullableFloatDict,
            (writer, element) -> writer.writeNumber(element));
        jsonWriter.writeJsonField("encoding", this.encoding);
        jsonWriter.writeStringField("uuid", this.uuid);
        jsonWriter.writeFieldName("unknown");
        this.unknown.writeTo(jsonWriter);
        jsonWriter.writeMapField("unknownDict", this.unknownDict, (writer, element) -> {
            if (element == null) {
                writer.writeNull();
            } else {
                element.writeTo(writer);
            }
        });
        jsonWriter.writeArrayField("unknownArray", this.unknownArray, (writer, element) -> {
            if (element == null) {
                writer.writeNull();
            } else {
                element.writeTo(writer);
            }
        });
        jsonWriter.writeArrayField("unknownDictArray", this.unknownDictArray,
            (writer, element) -> writer.writeMap(element, (writer1, element1) -> {
                if (element1 == null) {
                    writer1.writeNull();
                } else {
                    element1.writeTo(writer1);
                }
            }));
        return jsonWriter.writeEndObject();
    }

    /**
     * Reads an instance of Builtin from the JsonReader.
     * 
     * @param jsonReader The JsonReader being read.
     * @return An instance of Builtin if the JsonReader was pointing to an instance of it, or null if it was pointing to
     * JSON null.
     * @throws IllegalStateException If the deserialized JSON object was missing any required properties.
     * @throws IOException If an error occurs while reading the Builtin.
     */
    public static Builtin fromJson(JsonReader jsonReader) throws IOException {
        return jsonReader.readObject(reader -> {
            Builtin deserializedBuiltin = new Builtin();
            while (reader.nextToken() != JsonToken.END_OBJECT) {
                String fieldName = reader.getFieldName();
                reader.nextToken();

                if ("boolean".equals(fieldName)) {
                    deserializedBuiltin.booleanProperty = reader.getBoolean();
                } else if ("string".equals(fieldName)) {
                    deserializedBuiltin.string = reader.getString();
                } else if ("bytes".equals(fieldName)) {
                    deserializedBuiltin.bytes = reader.getBinary();
                } else if ("int".equals(fieldName)) {
                    deserializedBuiltin.intProperty = reader.getInt();
                } else if ("safeint".equals(fieldName)) {
                    deserializedBuiltin.safeint = reader.getLong();
                } else if ("decimal".equals(fieldName)) {
                    deserializedBuiltin.decimal
                        = reader.getNullable(nonNullReader -> new BigDecimal(nonNullReader.getString()));
                } else if ("long".equals(fieldName)) {
                    deserializedBuiltin.longProperty = reader.getLong();
                } else if ("float".equals(fieldName)) {
                    deserializedBuiltin.floatProperty = reader.getDouble();
                } else if ("double".equals(fieldName)) {
                    deserializedBuiltin.doubleProperty = reader.getDouble();
                } else if ("duration".equals(fieldName)) {
                    deserializedBuiltin.duration
                        = reader.getNullable(nonNullReader -> Duration.parse(nonNullReader.getString()));
                } else if ("date".equals(fieldName)) {
                    deserializedBuiltin.date
                        = reader.getNullable(nonNullReader -> LocalDate.parse(nonNullReader.getString()));
                } else if ("dateTime".equals(fieldName)) {
                    deserializedBuiltin.dateTime = reader
                        .getNullable(nonNullReader -> CoreUtils.parseBestOffsetDateTime(nonNullReader.getString()));
                } else if ("stringList".equals(fieldName)) {
                    List<String> stringList = reader.readArray(reader1 -> reader1.getString());
                    deserializedBuiltin.stringList = stringList;
                } else if ("bytesDict".equals(fieldName)) {
                    Map<String, byte[]> bytesDict = reader.readMap(reader1 -> reader1.getBinary());
                    deserializedBuiltin.bytesDict = bytesDict;
                } else if ("url".equals(fieldName)) {
                    deserializedBuiltin.url = reader.getString();
                } else if ("nullableFloatDict".equals(fieldName)) {
                    Map<String, Double> nullableFloatDict
                        = reader.readMap(reader1 -> reader1.getNullable(JsonReader::getDouble));
                    deserializedBuiltin.nullableFloatDict = nullableFloatDict;
                } else if ("encoding".equals(fieldName)) {
                    deserializedBuiltin.encoding = Encoded.fromJson(reader);
                } else if ("uuid".equals(fieldName)) {
                    deserializedBuiltin.uuid = reader.getString();
                } else if ("unknown".equals(fieldName)) {
                    deserializedBuiltin.unknown
                        = reader.getNullable(nonNullReader -> BinaryData.fromObject(nonNullReader.readUntyped()));
                } else if ("unknownDict".equals(fieldName)) {
                    Map<String, BinaryData> unknownDict = reader.readMap(reader1 -> reader1
                        .getNullable(nonNullReader -> BinaryData.fromObject(nonNullReader.readUntyped())));
                    deserializedBuiltin.unknownDict = unknownDict;
                } else if ("unknownArray".equals(fieldName)) {
                    List<BinaryData> unknownArray = reader.readArray(reader1 -> reader1
                        .getNullable(nonNullReader -> BinaryData.fromObject(nonNullReader.readUntyped())));
                    deserializedBuiltin.unknownArray = unknownArray;
                } else if ("unknownDictArray".equals(fieldName)) {
                    List<Map<String, BinaryData>> unknownDictArray
                        = reader.readArray(reader1 -> reader1.readMap(reader2 -> reader2
                            .getNullable(nonNullReader -> BinaryData.fromObject(nonNullReader.readUntyped()))));
                    deserializedBuiltin.unknownDictArray = unknownDictArray;
                } else {
                    reader.skipChildren();
                }
            }

            return deserializedBuiltin;
        });
    }
}
