/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo;

import java.io.Serializable;
import java.util.Base64;

import com.fluidbpm.program.api.util.UtilGlobal;
import com.fluidbpm.program.api.vo.user.User;

/**
 *

 *     Base Value Object for any Fluid Value Object.
 *
 *

 *     Contains common properties such as {@code id} and {@code serviceTicket}.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see Serializable
 * @see User
 * @see com.fluidbpm.program.api.vo.ws.auth.ServiceTicket
 */
public class ABaseFluidVO implements Serializable {

    public static final long serialVersionUID = 1L;

    private Long id;
    protected String serviceTicket;
    protected String echo;

    private User loggedInUserFromTicket;

    /**
     * Sets the Id associated with any Fluid entity.
     *
     * @param idParam Unique Identifier.
     */
    public ABaseFluidVO(Long idParam) {
        super();
        this.id = idParam;
    }

    /**
	 * Default constructor.
	 */
    public ABaseFluidVO() {
        super();
    }

    /**
     * Gets the Id associated with any Fluid entity.
     *
     * @return Id associated with {@code this} entity.
     */
    public Long getId() {
        return this.id;
    }

    /**
     * Sets the Id associated with any Fluid entity.
     *
     * @param idParam Id associated with {@code this} entity.
     */
    public void setId(Long idParam) {
        this.id = idParam;
    }

    /**
     * Gets the Service Ticket associated with the Fluid session.
     *
     * @return Service Ticket of Fluid session.
     *
     * @see com.fluidbpm.program.api.vo.ws.auth.ServiceTicket
     */
    public String getServiceTicket() {
        return this.serviceTicket;
    }

    /**
     * Gets the Service Ticket associated with the Fluid session as HEX.
     *
     * @return Service Ticket of Fluid session in Hex format.
     *
     * @see com.fluidbpm.program.api.vo.ws.auth.ServiceTicket
     */
    public String getServiceTicketAsHexUpper() {

        String serviceTicket = this.getServiceTicket();

        if(serviceTicket == null)
        {
            return null;
        }

        if(serviceTicket.isEmpty())
        {
            return serviceTicket;
        }

        byte[] base64Bytes = Base64.getDecoder().decode(serviceTicket);

        return this.bytesToHex(base64Bytes);
    }

    final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();

    /**
     * Convert the byte[] to a HEX string as upper case.
     *
     * @param bytesToConvert The {@code byte[]} to convert.
     * @return String with upper case HEX.
     */
    private String bytesToHex(byte[] bytesToConvert) {

        if(bytesToConvert == null)
        {
            return null;
        }

        if(bytesToConvert.length == 0)
        {
            return UtilGlobal.EMPTY;
        }

        char[] hexChars = new char[bytesToConvert.length * 2];

        for ( int index = 0; index < bytesToConvert.length; index++ ) {
            int andWith127 = (bytesToConvert[index] & 0xFF);
            hexChars[index * 2] = HEX_ARRAY[andWith127 >>> 4];
            hexChars[index * 2 + 1] = HEX_ARRAY[andWith127 & 0x0F];
        }

        return new String(hexChars);
    }

    /**
     * Sets the Service Ticket associated with the Fluid session.
     *
     * @param serviceTicketParam Service Ticket of Fluid session.
     *
     * @see com.fluidbpm.program.api.vo.ws.auth.ServiceTicket
     */
    public void setServiceTicket(String serviceTicketParam) {
        this.serviceTicket = serviceTicketParam;
    }

    /**
     * Sets the Echo message for tracking async messages.
     *
     * @return Echo message.
     */
    public String getEcho() {
        return this.echo;
    }

    /**
     * Sets the Echo message for tracking async messages.
     *
     * @param echoParam Echo message.
     */
    public void setEcho(String echoParam) {
        this.echo = echoParam;
    }

    /**
     * Gets the {@code User} associated with the session.
     *
     * @return The Logged in user associated with the session.
     *
     * @see com.fluidbpm.program.api.vo.ws.auth.ServiceTicket
     * @see User
     */
    public User getLoggedInUserFromTicket() {
        return this.loggedInUserFromTicket;
    }

    /**
     * Sets the {@code User} associated with the session.
     *
     * @param loggedInUserFromTicket The Logged in user associated with the session.
     *
     * @see com.fluidbpm.program.api.vo.ws.auth.ServiceTicket
     * @see User
     */
    public void setLoggedInUserFromTicket(User loggedInUserFromTicket) {
        this.loggedInUserFromTicket = loggedInUserFromTicket;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.role;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;
import com.fluidbpm.program.api.vo.userquery.UserQuery;

/**
 * <p>
 *     Represents what UserQueries a {@code Role} has access to.
 * </p>
 *
 * @author jasonbruwer
 * @since v1.1
 *
 * @see com.fluidbpm.program.api.vo.userquery.UserQuery
 * @see Role
 */
public class RoleToUserQuery extends ABaseFluidJSONObject {

    public static final long serialVersionUID = 1L;

    private UserQuery userQuery;
    private Role role;

    /**
     * The JSON mapping for the {@code RoleToUserQuery} object.
     */
    public static class JSONMapping
    {
        public static final String USER_QUERY = "userQuery";
        public static final String ROLE = "role";
    }

    /**
     * Default constructor.
     */
    public RoleToUserQuery() {
        super();
    }

    /**
     * Sets the Id associated with a 'Role To User Query'.
     *
     * @param roleToUserQueryIdParam RoleToUserQuery Id.
     */
    public RoleToUserQuery(Long roleToUserQueryIdParam) {
        super();

        this.setId(roleToUserQueryIdParam);
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public RoleToUserQuery(JSONObject jsonObjectParam){
        super(jsonObjectParam);

        if(this.jsonObject == null)
        {
            return;
        }

        //User Query...
        if (!this.jsonObject.isNull(JSONMapping.USER_QUERY)) {
            this.setUserQuery(new UserQuery(this.jsonObject.getJSONObject(
                    JSONMapping.USER_QUERY)));
        }

        //Role...
        if (!this.jsonObject.isNull(JSONMapping.ROLE)) {
            this.setRole(new Role(this.jsonObject.getJSONObject(
                    JSONMapping.ROLE)));
        }
    }

    /**
     * Gets the User Query.
     *
     * @return User Query.
     */
    public UserQuery getUserQuery() {
        return this.userQuery;
    }

    /**
     * Sets the User Query for the Role.
     *
     * @param userQueryParam View Rule.
     */
    public void setUserQuery(UserQuery userQueryParam) {
        this.userQuery = userQueryParam;
    }

    /**
     * Gets the Role for the View.
     *
     * @return View Rule.
     */
    public Role getRole() {
        return this.role;
    }

    /**
     * Sets the Role for the View.
     *
     * @param roleParam View Rule.
     */
    public void setRole(Role roleParam) {
        this.role = roleParam;
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code RoleToUserQuery}
     * @throws JSONException If there is a problem with the JSON Body.
     *
     * @see ABaseFluidJSONObject#toJsonObject()
     */
    @Override
    public JSONObject toJsonObject() throws JSONException {

        JSONObject returnVal = super.toJsonObject();

        //User Query...
        if(this.getUserQuery() != null)
        {
            returnVal.put(JSONMapping.USER_QUERY,
                    this.getUserQuery().toJsonObject());
        }

        //Role...
        if(this.getRole() != null)
        {
            returnVal.put(JSONMapping.ROLE,
                    this.getRole().toJsonObject());
        }

        return returnVal;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.ws.client.v1.form;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONObject;

import com.fluidbpm.program.api.vo.field.Field;
import com.fluidbpm.program.api.vo.field.MultiChoice;
import com.fluidbpm.program.api.vo.form.Form;
import com.fluidbpm.program.api.vo.form.FormFieldListing;
import com.fluidbpm.program.api.vo.ws.WS;
import com.fluidbpm.ws.client.FluidClientException;
import com.fluidbpm.ws.client.v1.ABaseFieldClient;

/**
 * Java Web Service Client for Form Field related actions.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see JSONObject
 * @see com.fluidbpm.program.api.vo.ws.WS.Path.FormField
 * @see Field
 */
public class FormFieldClient extends ABaseFieldClient {

    /**
     * Constructor that sets the Service Ticket from authentication.
     *
     * @param endpointBaseUrlParam URL to base endpoint.
     * @param serviceTicketParam The Server issued Service Ticket.
     */
    public FormFieldClient(String endpointBaseUrlParam,
                           String serviceTicketParam) {
        super(endpointBaseUrlParam);

        this.setServiceTicket(serviceTicketParam);
    }

    /**
     * Create a new Plain Text field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldTextPlain(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.Text);
            formFieldParam.setTypeMetaData(FieldMetaData.Text.PLAIN);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Text Masked field.
     *
     * @param formFieldParam Field to Create.
     * @param maskValueParam The masked value. Example: (999) 999 xxx.
     * @return Created Field.
     */
    public Field createFieldTextMasked(
            Field formFieldParam, String maskValueParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(maskValueParam == null || maskValueParam.trim().isEmpty())
        {
            maskValueParam = "";
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Text);
            formFieldParam.setTypeMetaData(FieldMetaData.Text.MASKED.concat(maskValueParam));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Text Barcode field.
     *
     * @param formFieldParam Field to Create.
     * @param barcodeTypeParam The type of barcode.
     * @return Created Field.
     *
     * @see com.fluidbpm.ws.client.v1.ABaseFieldClient.FieldMetaData.Text
     */
    public Field createFieldTextBarcode(
            Field formFieldParam, String barcodeTypeParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(barcodeTypeParam == null || barcodeTypeParam.trim().isEmpty())
        {
            throw new FluidClientException(
                    "Barcode type cannot be empty.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Text);
            formFieldParam.setTypeMetaData(FieldMetaData.Text.BARCODE.concat(barcodeTypeParam));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Text field with latitude and longitude.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldTextLatitudeAndLongitude(
            Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Text);
            formFieldParam.setTypeMetaData(FieldMetaData.Text.LATITUDE_AND_LONGITUDE);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new True False field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldTrueFalse(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.TrueFalse);
            formFieldParam.setTypeMetaData(FieldMetaData.TrueFalse.TRUE_FALSE);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Paragraph Text field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldParagraphTextPlain(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.ParagraphText);
            formFieldParam.setTypeMetaData(FieldMetaData.ParagraphText.PLAIN);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Paragraph HTML field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldParagraphTextHTML(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.ParagraphText);
            formFieldParam.setTypeMetaData(FieldMetaData.ParagraphText.HTML);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Multi Choice field.
     *
     * @param formFieldParam Field to Create.
     * @param multiChoiceValuesParam The available multi choice values.
     * @return Created Field.
     */
    public Field createFieldMultiChoicePlain(
            Field formFieldParam, List<String> multiChoiceValuesParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null)
        {
            multiChoiceValuesParam = new ArrayList();
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.PLAIN);
            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Multi Choice field with Search abilities.
     *
     * @param formFieldParam Field to Create.
     * @param multiChoiceValuesParam The available multi choice values.
     * @return Created Field.
     */
    public Field createFieldMultiChoicePlainWithSearch(
            Field formFieldParam, List<String> multiChoiceValuesParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null ||
                multiChoiceValuesParam.isEmpty())
        {
            throw new FluidClientException(
                    "No Multi-choice values provided.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.PLAIN_SEARCH);
            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Multi Choice select Many field.
     *
     * @param formFieldParam Field to Create.
     * @param multiChoiceValuesParam The available multi choice values.
     * @return Created Field.
     */
    public Field createFieldMultiChoiceSelectMany(
            Field formFieldParam, List<String> multiChoiceValuesParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null ||
                multiChoiceValuesParam.isEmpty())
        {
            throw new FluidClientException(
                    "No Multi-choice values provided.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.SELECT_MANY);
            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Multi Choice select Many field with Search.
     *
     * @param formFieldParam Field to Create.
     * @param multiChoiceValuesParam The available multi choice values.
     * @return Created Field.
     */
    public Field createFieldMultiChoiceSelectManyWithSearch(
            Field formFieldParam, List<String> multiChoiceValuesParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null ||
                multiChoiceValuesParam.isEmpty())
        {
            throw new FluidClientException(
                    "No Multi-choice values provided.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.SELECT_MANY_SEARCH);
            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Date only field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldDateTimeDate(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.DateTime);
            formFieldParam.setTypeMetaData(FieldMetaData.DateTime.DATE);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Date and time field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldDateTimeDateAndTime(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.DateTime);
            formFieldParam.setTypeMetaData(FieldMetaData.DateTime.DATE_AND_TIME);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Decimal field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldDecimalPlain(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Decimal);
            formFieldParam.setTypeMetaData(FieldMetaData.Decimal.PLAIN);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Decimal Spinner field.
     *
     * @param formFieldParam Field to Create.
     * @param minParam The min allowed value.
     * @param maxParam The max allowed value.
     * @param stepFactorParam The increment factor.
     * @param prefixParam The Prefix of the spinner. Example. $.
     * @return Created Field.
     */
    public Field createFieldDecimalSpinner(
            Field formFieldParam,
            double minParam,
            double maxParam,
            double stepFactorParam,
            String prefixParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Decimal);
            formFieldParam.setTypeMetaData(
                    this.getMetaDataForDecimalAs(
                            FieldMetaData.Decimal.SPINNER,
                            minParam,maxParam, stepFactorParam, prefixParam));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Decimal Slider field.
     *
     * @param formFieldParam Field to Create.
     * @param minParam The min allowed value.
     * @param maxParam The max allowed value.
     * @param stepFactorParam The increment factor.
     * @return Created Field.
     */
    public Field createFieldDecimalSlider(
            Field formFieldParam,
            double minParam,
            double maxParam,
            double stepFactorParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Decimal);
            formFieldParam.setTypeMetaData(
                    this.getMetaDataForDecimalAs(
                            FieldMetaData.Decimal.SLIDER,
                            minParam,maxParam, stepFactorParam,
                            null));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Create a new Decimal Rating field.
     *
     * @param formFieldParam Field to Create.
     * @param minParam The min allowed value.
     * @param maxParam The max allowed value.
     * @return Created Field.
     */
    public Field createFieldDecimalRating(
            Field formFieldParam,
            double minParam,
            double maxParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Decimal);
            formFieldParam.setTypeMetaData(
                    this.getMetaDataForDecimalAs(
                            FieldMetaData.Decimal.SLIDER,
                            minParam,maxParam, 0.0, null));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Creates a new Table Field.
     *
     * @param formFieldParam The Base field to create.
     * @param formDefinitionParam The Form Definition for the Table Field.
     * @param sumDecimalsParam Whether decimals should be summed.
     * @return Table Field.
     */
    public Field createFieldTable(
            Field formFieldParam,
            Form formDefinitionParam,
            boolean sumDecimalsParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Table);
            formFieldParam.setTypeMetaData(
                    this.getMetaDataForTableField(
                            formDefinitionParam, sumDecimalsParam));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldCreate()));
    }

    /**
     * Update an existing Text field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldTextPlain(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Text);
            formFieldParam.setTypeMetaData(FieldMetaData.Text.PLAIN);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Update an existing Masked Text field.
     *
     * @param formFieldParam Field to Update.
     * @param maskValueParam The new masked value.
     * @return Updated Field.
     */
    public Field updateFieldTextMasked(Field formFieldParam, String maskValueParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(maskValueParam == null || maskValueParam.trim().isEmpty())
        {
            throw new FluidClientException(
                    "Masked value cannot be empty.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Text);
            formFieldParam.setTypeMetaData(FieldMetaData.Text.MASKED.concat(maskValueParam));
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Update an existing Barcode Text field.
     *
     * @param formFieldParam Field to Update.
     * @param barcodeTypeParam The new type of barcode image.
     * @return Updated Field.
     */
    public Field updateFieldTextBarcode(Field formFieldParam, String barcodeTypeParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(barcodeTypeParam == null || barcodeTypeParam.trim().isEmpty())
        {
            throw new FluidClientException(
                    "Barcode type cannot be empty.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Text);
            formFieldParam.setTypeMetaData(FieldMetaData.Text.BARCODE.concat(barcodeTypeParam));
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Update an existing Latitude Longitude Text field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldTextLatitudeAndLongitude(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Text);
            formFieldParam.setTypeMetaData(FieldMetaData.Text.LATITUDE_AND_LONGITUDE);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Update an existing True False field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldTrueFalse(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.TrueFalse);
            formFieldParam.setTypeMetaData(FieldMetaData.TrueFalse.TRUE_FALSE);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Update an existing Paragraph Text field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldParagraphTextPlain(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.ParagraphText);
            formFieldParam.setTypeMetaData(FieldMetaData.ParagraphText.PLAIN);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Update an existing Paragraph HTML field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldParagraphTextHTML(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.ParagraphText);
            formFieldParam.setTypeMetaData(FieldMetaData.ParagraphText.HTML);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Update an existing Multi Choice field.
     *
     * @param formFieldParam Field to Update.
     * @param multiChoiceValuesParam New available Multi-choices.
     * @return Updated Field.
     */
    public Field updateFieldMultiChoicePlain(
            Field formFieldParam,
            List<String> multiChoiceValuesParam) {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null ||
                multiChoiceValuesParam.isEmpty()) {
            throw new FluidClientException(
                    "No Multi-choice values provided.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        List<String> beforeAvail = null, beforeSelected = null;
        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.PLAIN);

            if(formFieldParam.getFieldValue() instanceof MultiChoice){
                MultiChoice casted = (MultiChoice)formFieldParam.getFieldValue();
                beforeAvail = casted.getAvailableMultiChoices();
                beforeSelected = casted.getSelectedMultiChoices();
            }

            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        Field returnVal = new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
        if(formFieldParam != null){
            formFieldParam.setFieldValue(new MultiChoice(beforeSelected, beforeAvail));
        }

        return returnVal;
    }

    /**
     * Update an existing Multi Choice field with Search.
     *
     * @param formFieldParam Field to Update.
     * @param multiChoiceValuesParam New available Multi-choices.
     * @return Updated Field.
     */
    public Field updateFieldMultiChoicePlainWithSearch(
            Field formFieldParam,
            List<String> multiChoiceValuesParam) {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null ||
                multiChoiceValuesParam.isEmpty()) {
            throw new FluidClientException(
                    "No Multi-choice values provided.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        List<String> beforeAvail = null, beforeSelected = null;
        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.PLAIN_SEARCH);

            if(formFieldParam.getFieldValue() instanceof MultiChoice){
                MultiChoice casted = (MultiChoice)formFieldParam.getFieldValue();
                beforeAvail = casted.getAvailableMultiChoices();
                beforeSelected = casted.getSelectedMultiChoices();
            }

            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        Field returnVal = new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
        if(formFieldParam != null){
            formFieldParam.setFieldValue(new MultiChoice(beforeSelected, beforeAvail));
        }

        return returnVal;
    }

    /**
     * Update an existing Multi Choice select many field.
     *
     * @param formFieldParam Field to Update.
     * @param multiChoiceValuesParam New available Multi-choices.
     * @return Updated Field.
     */
    public Field updateFieldMultiChoiceSelectMany(
            Field formFieldParam,
            List<String> multiChoiceValuesParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null ||
                multiChoiceValuesParam.isEmpty())
        {
            throw new FluidClientException(
                    "No Multi-choice values provided.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        List<String> beforeAvail = null, beforeSelected = null;
        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.SELECT_MANY);

            if(formFieldParam.getFieldValue() instanceof MultiChoice){
                MultiChoice casted = (MultiChoice)formFieldParam.getFieldValue();
                beforeAvail = casted.getAvailableMultiChoices();
                beforeSelected = casted.getSelectedMultiChoices();
            }

            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        Field returnVal = new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
        if(formFieldParam != null){
            formFieldParam.setFieldValue(new MultiChoice(beforeSelected, beforeAvail));
        }

        return returnVal;
    }

    /**
     * Update an existing Multi Choice select many field with Search.
     *
     * @param formFieldParam Field to Update.
     * @param multiChoiceValuesParam New available Multi-choices.
     * @return Updated Field.
     */
    public Field updateFieldMultiChoiceSelectManyWithSearch(
            Field formFieldParam,
            List<String> multiChoiceValuesParam) {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null ||
                multiChoiceValuesParam.isEmpty())
        {
            throw new FluidClientException(
                    "No Multi-choice values provided.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        List<String> beforeAvail = null, beforeSelected = null;
        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.SELECT_MANY_SEARCH);

            if(formFieldParam.getFieldValue() instanceof MultiChoice){
                MultiChoice casted = (MultiChoice)formFieldParam.getFieldValue();
                beforeAvail = casted.getAvailableMultiChoices();
                beforeSelected = casted.getSelectedMultiChoices();
            }

            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        Field returnVal = new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
        if(formFieldParam != null){
            formFieldParam.setFieldValue(new MultiChoice(beforeSelected, beforeAvail));
        }

        return returnVal;
    }

    /**
     * Update an existing Date field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldDateTimeDate(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.DateTime);
            formFieldParam.setTypeMetaData(FieldMetaData.DateTime.DATE);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Update an existing Date and Time field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldDateTimeDateAndTime(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.DateTime);
            formFieldParam.setTypeMetaData(FieldMetaData.DateTime.DATE_AND_TIME);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Update an existing Decimal field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldDecimalPlain(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Decimal);
            formFieldParam.setTypeMetaData(FieldMetaData.Decimal.PLAIN);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Updates the Decimal Spinner field.
     *
     * @param formFieldParam The Field.
     * @param minParam The min allowed value.
     * @param maxParam The max allowed value.
     * @param stepFactorParam The increments or step factor.
     * @param prefixParam The prefix to the decimal. Example is $ for currency.
     *
     * @return Updated Decimal Spinner.
     */
    public Field updateFieldDecimalSpinner(
            Field formFieldParam,
            double minParam,
            double maxParam,
            double stepFactorParam,
            String prefixParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Decimal);
            formFieldParam.setTypeMetaData(
                    this.getMetaDataForDecimalAs(
                            FieldMetaData.Decimal.SPINNER,
                            minParam,maxParam, stepFactorParam, prefixParam));
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Updates the Decimal Slider field.
     *
     * @param formFieldParam The Field.
     * @param minParam The min allowed value.
     * @param maxParam The max allowed value.
     * @param stepFactorParam The increments or step factor.
     *
     * @return Updated Decimal Slider.
     */
    public Field updateFieldDecimalSlider(
            Field formFieldParam,
            double minParam,
            double maxParam,
            double stepFactorParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Decimal);
            formFieldParam.setTypeMetaData(
                    this.getMetaDataForDecimalAs(
                            FieldMetaData.Decimal.SLIDER,
                            minParam,maxParam, stepFactorParam, null));
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Updates the Decimal Rating field.
     *
     * @param formFieldParam The Field.
     * @param minParam The min allowed value.
     * @param maxParam The max allowed value.
     *
     * @return Updated Decimal Rating.
     */
    public Field updateFieldDecimalRating(
            Field formFieldParam,
            double minParam,
            double maxParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Decimal);
            formFieldParam.setTypeMetaData(
                    this.getMetaDataForDecimalAs(
                            FieldMetaData.Decimal.SLIDER,
                            minParam,maxParam, 0.0, null));
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Updates a Table Field.
     *
     * @param formFieldParam The Field to Update.
     * @param formDefinitionParam The Form Definition to Update.
     * @param sumDecimalsParam Whether decimals should be summed.
     *
     * @return Updated Field.
     */
    public Field updateFieldTable(
            Field formFieldParam,
            Form formDefinitionParam,
            boolean sumDecimalsParam)
    {
        if(formFieldParam != null && this.serviceTicket != null)
        {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null)
        {
            formFieldParam.setTypeAsEnum(Field.Type.Table);
            formFieldParam.setTypeMetaData(
                    this.getMetaDataForTableField(
                            formDefinitionParam, sumDecimalsParam));
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.FormField.Version1.formFieldUpdate()));
    }

    /**
     * Retrieve a Form Field via Primary key.
     *
     * @param fieldIdParam The field Primary Key.
     * @return Field and properties.
     */
    public Field getFieldById(Long fieldIdParam)
    {
        Field field = new Field(fieldIdParam);

        //Set for Payara server...
        field.setFieldValue(new MultiChoice());

        if(this.serviceTicket != null)
        {
            field.setServiceTicket(this.serviceTicket);
        }

        return new Field(this.postJson(
                field, WS.Path.FormField.Version1.getById()));
    }

    /**
     * Retrieve a Form Field via name.
     *
     * @param fieldNameParam The field name.
     * @return Field and properties.
     */
    public Field getFieldByName(String fieldNameParam)
    {
        Field field = new Field();
        field.setFieldName(fieldNameParam);

        if(this.serviceTicket != null)
        {
            field.setServiceTicket(this.serviceTicket);
        }

        return new Field(this.postJson(
                field, WS.Path.FormField.Version1.getByName()));
    }

    /**
     * Retrieve the Form Fields via Form Definition name.
     *
     * @param formNameParam The form definition name.
     * @param editOnlyFieldsParam Only return the fields that are editable.
     *
     * @return Form Fields for {@code formNameParam}
     */
    public FormFieldListing getFieldsByFormNameAndLoggedInUser(
            String formNameParam,
            boolean editOnlyFieldsParam)
    {
        Form form = new Form();
        form.setFormType(formNameParam);

        if(this.serviceTicket != null)
        {
            form.setServiceTicket(this.serviceTicket);
        }

        return new FormFieldListing(this.postJson(
                form, WS.Path.FormField.Version1.getByFormDefinitionAndLoggedInUser(
                        editOnlyFieldsParam)));
    }

    /**
     * Retrieve the Form Fields via Form Definition id.
     *
     * @param formTypeIdParam The form definition id.
     * @param editOnlyFieldsParam Only return the fields that are editable.
     *
     * @return Form Fields for {@code formIdParam}
     */
    public FormFieldListing getFieldsByFormTypeIdAndLoggedInUser(
            Long formTypeIdParam,
            boolean editOnlyFieldsParam)
    {
        Form form = new Form();
        form.setFormTypeId(formTypeIdParam);

        if(this.serviceTicket != null)
        {
            form.setServiceTicket(this.serviceTicket);
        }

        return new FormFieldListing(this.postJson(
                form, WS.Path.FormField.Version1.getByFormDefinitionAndLoggedInUser(
                        editOnlyFieldsParam)));
    }

    /**
     * Deletes the provided field.
     *
     * Ensure that Id is set.
     *
     * @param fieldParam The Field to delete.
     * @return The deleted Field.
     */
    public Field deleteField(Field fieldParam)
    {
        if(fieldParam != null && this.serviceTicket != null)
        {
            fieldParam.setServiceTicket(this.serviceTicket);
        }

        return new Field(this.postJson(fieldParam, WS.Path.FormField.Version1.formFieldDelete()));
    }

    /**
     * Forcefully deletes the provided field.
     *
     * Ensure that Id is set.
     *
     * Only 'admin' is able to forcefully delete a field.
     *
     * @param fieldParam The Field to delete.
     * @return The deleted Field.
     */
    public Field forceDeleteField(Field fieldParam)
    {
        if(fieldParam != null && this.serviceTicket != null)
        {
            fieldParam.setServiceTicket(this.serviceTicket);
        }

        return new Field(this.postJson(
                fieldParam, WS.Path.FormField.Version1.formFieldDelete(true)));
    }

    /**
     * Generates the Meta Data for a table field.
     *
     * @param formDefinitionParam The Form Definition to use.
     * @param sumDecimalsParam Whether decimal values should be summarized.
     * @return Meta Data for the Table Field.
     */
    private String getMetaDataForTableField(
            Form formDefinitionParam,
            boolean sumDecimalsParam)
    {
        StringBuilder returnBuffer = new StringBuilder();

        Long definitionId =
                (formDefinitionParam == null) ? -1L:
                        formDefinitionParam.getId();

        //Min...
        returnBuffer.append(definitionId);
        returnBuffer.append(FieldMetaData.TableField.UNDERSCORE);
        returnBuffer.append(FieldMetaData.TableField.SUM_DECIMALS);
        returnBuffer.append(FieldMetaData.Decimal.SQ_OPEN);
        returnBuffer.append(sumDecimalsParam);
        returnBuffer.append(FieldMetaData.Decimal.SQ_CLOSE);

        return returnBuffer.toString();
    }

}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.ws.auth;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;

/**
 * Authorization encrypted data.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see ABaseFluidJSONObject
 * @see TokenStatus
 * @see AppRequestToken
 * @see AuthRequest
 * @see AuthResponse
 * @see AuthEncryptedData
 */
public class AuthEncryptedData extends ABaseFluidJSONObject {

    public static final long serialVersionUID = 1L;

    private String sessionKeyBase64;
    private Long ticketExpires;
    private String roleListing;

    /**
     * The JSON mapping for the {@code AuthEncryptedData} object.
     */
    public static final class JSONMapping {
        public static final String ROLE_LISTING = "roleListing";
        public static final String TICKET_EXPIRES = "ticketExpiration";
        public static final String SESSION_KEY = "sessionKeyBase64";
    }

    /**
     * Default constructor.
     */
    public AuthEncryptedData() {
        super();
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public AuthEncryptedData(JSONObject jsonObjectParam) {
        super();

        //Role Listing...
        if (!jsonObjectParam.isNull(JSONMapping.ROLE_LISTING)) {
            this.setRoleListing(jsonObjectParam.getString(JSONMapping.ROLE_LISTING));
        }

        //Session Key...
        if (!jsonObjectParam.isNull(JSONMapping.SESSION_KEY)) {
            this.setSessionKeyBase64(jsonObjectParam.getString(JSONMapping.SESSION_KEY));
        }

        //Ticket Expires...
        if (!jsonObjectParam.isNull(JSONMapping.TICKET_EXPIRES)) {
            this.setTicketExpires(jsonObjectParam.getLong(JSONMapping.TICKET_EXPIRES));
        }
    }

    /**
     * Gets when the ticket expires.
     *
     * @return The time the ticket expires.
     */
    public Long getTicketExpires() {
        return this.ticketExpires;
    }

    /**
     * Sets when the ticket expires.
     *
     * @param ticketExpirationParam The time the ticket expires.
     */
    public void setTicketExpires(Long ticketExpirationParam) {
        this.ticketExpires = ticketExpirationParam;
    }

    /**
     * Gets the Session Key in Base-64 format.
     *
     * @return Session Key.
     */
    public String getSessionKeyBase64() {
        return this.sessionKeyBase64;
    }

    /**
     * Sets the Session Key in Base-64 format.
     *
     * @param sessionKeyBase64Param Session Key.
     */
    public void setSessionKeyBase64(String sessionKeyBase64Param) {
        this.sessionKeyBase64 = sessionKeyBase64Param;
    }

    /**
     * Gets the role listing separated by ','.
     *
     * @return List of roles.
     */
    public String getRoleListing() {
        return this.roleListing;
    }

    /**
     * Sets the role listing separated by ','.
     *
     * @param roleListingParam List of roles.
     */
    public void setRoleListing(String roleListingParam) {
        this.roleListing = roleListingParam;
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code AuthEncryptedData}
     * @throws JSONException If there is a problem with the JSON Body.
     */
    @Override
    public JSONObject toJsonObject() throws JSONException {

        JSONObject returnVal = new JSONObject();

        returnVal.put(JSONMapping.TICKET_EXPIRES, this.getTicketExpires());
        returnVal.put(JSONMapping.ROLE_LISTING, this.getRoleListing());
        returnVal.put(JSONMapping.SESSION_KEY, this.getSessionKeyBase64());

        return returnVal;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.ws.client.v1.user;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONObject;

import com.fluidbpm.program.api.vo.field.Field;
import com.fluidbpm.program.api.vo.field.MultiChoice;
import com.fluidbpm.program.api.vo.ws.WS;
import com.fluidbpm.ws.client.FluidClientException;
import com.fluidbpm.ws.client.v1.ABaseFieldClient;

/**
 * Java Web Service Client for User Field related actions.
 *
 * @author jasonbruwer
 * @since v1.0
 *
 * @see JSONObject
 * @see com.fluidbpm.program.api.vo.ws.WS.Path.UserField
 * @see Field
 */
public class UserFieldClient extends ABaseFieldClient {

    /**
     * Constructor that sets the Service Ticket from authentication.
     *
     * @param endpointBaseUrlParam URL to base endpoint.
     * @param serviceTicketParam The Server issued Service Ticket.
     */
    public UserFieldClient(
            String endpointBaseUrlParam,
            String serviceTicketParam) {
        super(endpointBaseUrlParam);

        this.setServiceTicket(serviceTicketParam);
    }

    /**
     * Create a new Plain Text field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldTextPlain(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.Text);
            formFieldParam.setTypeMetaData(FieldMetaData.Text.PLAIN);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldCreate()));
    }

    /**
     * Create a new True False field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldTrueFalse(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.TrueFalse);
            formFieldParam.setTypeMetaData(FieldMetaData.TrueFalse.TRUE_FALSE);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldCreate()));
    }

    /**
     * Create a new Paragraph Text field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldParagraphTextPlain(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.ParagraphText);
            formFieldParam.setTypeMetaData(FieldMetaData.ParagraphText.PLAIN);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldCreate()));
    }

    /**
     * Create a new Paragraph HTML field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldParagraphTextHTML(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.ParagraphText);
            formFieldParam.setTypeMetaData(FieldMetaData.ParagraphText.HTML);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldCreate()));
    }

    /**
     * Create a new Multi Choice field.
     *
     * @param formFieldParam Field to Create.
     * @param multiChoiceValuesParam The available multi choice values.
     * @return Created Field.
     */
    public Field createFieldMultiChoicePlain(
            Field formFieldParam, List<String> multiChoiceValuesParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null) {
            multiChoiceValuesParam = new ArrayList();
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.PLAIN);
            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldCreate()));
    }

    /**
     * Create a new Multi Choice select Many field.
     *
     * @param formFieldParam Field to Create.
     * @param multiChoiceValuesParam The available multi choice values.
     * @return Created Field.
     */
    public Field createFieldMultiChoiceSelectMany(
            Field formFieldParam, List<String> multiChoiceValuesParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null ||
                multiChoiceValuesParam.isEmpty()) {
            throw new FluidClientException(
                    "No Multi-choice values provided.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.SELECT_MANY);
            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldCreate()));
    }

    /**
     * Create a new Date only field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldDateTimeDate(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.DateTime);
            formFieldParam.setTypeMetaData(FieldMetaData.DateTime.DATE);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldCreate()));
    }

    /**
     * Create a new Date and time field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldDateTimeDateAndTime(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.DateTime);
            formFieldParam.setTypeMetaData(FieldMetaData.DateTime.DATE_AND_TIME);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldCreate()));
    }

    /**
     * Create a new Decimal field.
     *
     * @param formFieldParam Field to Create.
     * @return Created Field.
     */
    public Field createFieldDecimalPlain(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.Decimal);
            formFieldParam.setTypeMetaData(FieldMetaData.Decimal.PLAIN);
        }

        return new Field(this.putJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldCreate()));
    }

    /**
     * Update an existing Text field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldTextPlain(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.Text);
            formFieldParam.setTypeMetaData(FieldMetaData.Text.PLAIN);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldUpdate()));
    }

    /**
     * Update an existing True False field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldTrueFalse(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.TrueFalse);
            formFieldParam.setTypeMetaData(FieldMetaData.TrueFalse.TRUE_FALSE);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldUpdate()));
    }

    /**
     * Update an existing Paragraph Text field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldParagraphTextPlain(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.ParagraphText);
            formFieldParam.setTypeMetaData(FieldMetaData.ParagraphText.PLAIN);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldUpdate()));
    }

    /**
     * Update an existing Paragraph HTML field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldParagraphTextHTML(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.ParagraphText);
            formFieldParam.setTypeMetaData(FieldMetaData.ParagraphText.HTML);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldUpdate()));
    }

    /**
     * Update an existing Multi Choice field.
     *
     * @param formFieldParam Field to Update.
     * @param multiChoiceValuesParam New available Multi-choices.
     * @return Updated Field.
     */
    public Field updateFieldMultiChoicePlain(
            Field formFieldParam,
            List<String> multiChoiceValuesParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null ||
                multiChoiceValuesParam.isEmpty()) {
            throw new FluidClientException(
                    "No Multi-choice values provided.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.PLAIN);
            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldUpdate()));
    }

    /**
     * Update an existing Multi Choice select many field.
     *
     * @param formFieldParam Field to Update.
     * @param multiChoiceValuesParam New available Multi-choices.
     * @return Updated Field.
     */
    public Field updateFieldMultiChoiceSelectMany(
            Field formFieldParam,
            List<String> multiChoiceValuesParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(multiChoiceValuesParam == null ||
                multiChoiceValuesParam.isEmpty()) {
            throw new FluidClientException(
                    "No Multi-choice values provided.",
                    FluidClientException.ErrorCode.FIELD_VALIDATE);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.MultipleChoice);
            formFieldParam.setTypeMetaData(FieldMetaData.MultiChoice.SELECT_MANY);
            formFieldParam.setFieldValue(new MultiChoice(multiChoiceValuesParam));
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldUpdate()));
    }

    /**
     * Update an existing Date field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldDateTimeDate(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.DateTime);
            formFieldParam.setTypeMetaData(FieldMetaData.DateTime.DATE);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldUpdate()));
    }

    /**
     * Update an existing Date and Time field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldDateTimeDateAndTime(Field formFieldParam)
    {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.DateTime);
            formFieldParam.setTypeMetaData(FieldMetaData.DateTime.DATE_AND_TIME);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldUpdate()));
    }

    /**
     * Update an existing Decimal field.
     *
     * @param formFieldParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldDecimalPlain(Field formFieldParam) {
        if(formFieldParam != null && this.serviceTicket != null) {
            formFieldParam.setServiceTicket(this.serviceTicket);
        }

        if(formFieldParam != null) {
            formFieldParam.setTypeAsEnum(Field.Type.Decimal);
            formFieldParam.setTypeMetaData(FieldMetaData.Decimal.PLAIN);
        }

        return new Field(this.postJson(
                formFieldParam, WS.Path.UserField.Version1.userFieldUpdate()));
    }

    /**
     * Update an existing User field value.
     *
     * @param userFieldValueParam Field to Update.
     * @return Updated Field.
     */
    public Field updateFieldValue(Field userFieldValueParam) {
        if(userFieldValueParam != null && this.serviceTicket != null) {
            userFieldValueParam.setServiceTicket(this.serviceTicket);
        }

        return new Field(this.postJson(
                userFieldValueParam,
                WS.Path.UserField.Version1.userFieldUpdateValue()));
    }

    /**
     * Retrieves field information by {@code fieldIdParam}.
     *
     * @param fieldIdParam The field Primary Key.
     * @return Field Definition by primary key.
     */
    public Field getFieldById(Long fieldIdParam) {
        Field field = new Field(fieldIdParam);

        //Set for Payara server...
        field.setFieldValue(new MultiChoice());

        if(this.serviceTicket != null) {
            field.setServiceTicket(this.serviceTicket);
        }

        return new Field(this.postJson(
                field, WS.Path.UserField.Version1.getById()));
    }

    /**
     * Retrieves field information by {@code fieldIdParam}.
     *
     * @param fieldNameParam The field name.
     * @return Field Definition by primary key.
     */
    public Field getFieldByName(String fieldNameParam) {
        Field field = new Field();
        field.setFieldName(fieldNameParam);

        //Set for Payara server...
        field.setFieldValue(new MultiChoice());

        if(this.serviceTicket != null)
        {
            field.setServiceTicket(this.serviceTicket);
        }

        return new Field(this.postJson(
                field, WS.Path.UserField.Version1.getByName()));
    }

    /**
     * Deletes a field from Fluid.
     *
     * @param fieldParam The field to delete. Important that Id is set.
     * @return Deleted Field.
     */
    public Field deleteField(Field fieldParam) {
        if(fieldParam != null && this.serviceTicket != null) {
            fieldParam.setServiceTicket(this.serviceTicket);
        }

        return new Field(this.postJson(fieldParam, WS.Path.UserField.Version1.userFieldDelete()));
    }

    /**
     * Forcefully Deletes a field from Fluid.
     *
     * Only 'admin' is allowed to make this call.
     *
     * @param fieldParam The field to delete. Important that Id is set.
     * @return Deleted Field.
     */
    public Field forceDeleteField(Field fieldParam)
    {
        if(fieldParam != null && this.serviceTicket != null)
        {
            fieldParam.setServiceTicket(this.serviceTicket);
        }

        return new Field(this.postJson(
                fieldParam, WS.Path.UserField.Version1.userFieldDelete(true)));
    }

}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.util.elasticsearch;

import java.io.IOException;

import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.xcontent.XContentType;
import org.json.JSONObject;

import com.carrotsearch.hppc.cursors.ObjectCursor;
import com.fluidbpm.program.api.util.ABaseUtil;
import com.fluidbpm.program.api.util.elasticsearch.exception.FluidElasticSearchException;
import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;
import com.fluidbpm.program.api.vo.field.Field;
import com.fluidbpm.program.api.vo.form.Form;

/**
 * ElasticSearch Utility class used for {@code Field} mappings.
 *
 * @author jasonbruwer on 2016/11/18.
 * @since 1.3
 * @see ABaseUtil
 * @see ABaseESUtil
 * @see Field.ElasticSearchType
 *
 */
public class ESFormFieldMappingUtil extends ABaseESUtil{

	/**
	 * Initialise with the ElasticSearch client.
	 *
	 * @param esClientParam The ES Client.
	 */
	public ESFormFieldMappingUtil(Client esClientParam) {
		super(esClientParam);
	}

	/**
	 * Creates / Updates the index {@code indexParam} with mappings
	 * provided in {@code fluidFormMappingToUpdateParam}.
	 *
	 * @param indexParam The ElasticSearch index effected.
	 * @param fluidFormMappingToUpdateParam The Fluid {@code Form} to be
	 *                                      update.
	 *
	 * @see Form
	 * @throws FluidElasticSearchException If validation or acknowledgement problems occur.
	 */
	public void mergeMappingForIndex(
			String indexParam,
			Form fluidFormMappingToUpdateParam) {
		this.mergeMappingForIndex(
				indexParam, null, fluidFormMappingToUpdateParam);
	}

	/**
	 * Creates / Updates the index {@code indexParam} with mappings
	 * provided in {@code fluidFormMappingToUpdateParam}.
	 *
	 *
	 * @param indexParam The ElasticSearch index effected.
	 * @param parentTypeParam The {@code _parent} type to be set.
	 * @param fluidFormMappingToUpdateParam The Fluid {@code Form} to be
	 *                                      update.
	 *
	 * @see Form
	 * @throws FluidElasticSearchException If validation or acknowledgement problems occur.
	 */
	public void mergeMappingForIndex(
			String indexParam,
			String parentTypeParam,
			Form fluidFormMappingToUpdateParam
	) {
		if(indexParam == null) {
			throw new FluidElasticSearchException(
					"Index name '"+indexParam+"' is invalid.");
		}

		//The Form mapping to update...
		if(fluidFormMappingToUpdateParam == null) {
			throw new FluidElasticSearchException(
					"Form for mapping not set.");
		}

		//Form Type Id...
		if(fluidFormMappingToUpdateParam.getFormTypeId() == null ||
				fluidFormMappingToUpdateParam.getFormTypeId().longValue() < 1) {
			throw new FluidElasticSearchException(
					"Form 'FormType' not set for mapping.");
		}

		String formTypeString =
				fluidFormMappingToUpdateParam.getFormTypeId().toString();

		JSONObject newContentMappingBuilderFromParam =
				fluidFormMappingToUpdateParam.toJsonMappingForElasticSearch();

		//Retrieve and update...
		GetIndexResponse getExistingIndex = this.getOrCreateIndex(indexParam);
		JSONObject existingPropsToUpdate = null;

		for(ObjectCursor mappingKey : getExistingIndex.getMappings().keys()) {
			//Found index...
			if(!mappingKey.value.toString().equals(indexParam)) {
				continue;
			}

			//Found a match...
			Object obj = getExistingIndex.getMappings().get(mappingKey.value.toString());
			if(obj instanceof ImmutableOpenMap) {
				ImmutableOpenMap casted = (ImmutableOpenMap)obj;

				//Type...
				if(casted.containsKey(formTypeString) &&
						casted.get(formTypeString) instanceof MappingMetaData) {
					MappingMetaData mappingMetaData = (MappingMetaData)casted.get(formTypeString);

					try {
						existingPropsToUpdate = new JSONObject(mappingMetaData.source().string());
						break;
					} catch (IOException eParam) {
						throw new FluidElasticSearchException(
								"Unable to retrieve source from 'Mapping Meta-Data'. "+
										eParam.getMessage(),eParam);
					}
				}
			}
		}

		//No mapping for the type create a new one...
		if(existingPropsToUpdate == null) {
			existingPropsToUpdate = new JSONObject();

			existingPropsToUpdate.put(
					ABaseFluidJSONObject.JSONMapping.Elastic.PROPERTIES,
					newContentMappingBuilderFromParam);

			//Set the additional properties...
			this.setAdditionalProps(
					existingPropsToUpdate, parentTypeParam);

			PutMappingRequestBuilder putMappingRequestBuilder =
					this.client.admin().indices().preparePutMapping(indexParam);

			putMappingRequestBuilder = putMappingRequestBuilder.setType(formTypeString);
			putMappingRequestBuilder = putMappingRequestBuilder.setSource(
					existingPropsToUpdate.toString(), XContentType.JSON);

			PutMappingResponse putMappingResponse = putMappingRequestBuilder.get();

			if(!putMappingResponse.isAcknowledged()) {
				throw new FluidElasticSearchException(
						"Index Update for Creating '"+
								indexParam+"' and type '"+
								formTypeString+"' not acknowledged by ElasticSearch.");
			}

			//Creation done.
			return;
		}

		//Update the existing index...
		JSONObject existingPropertiesUpdated =
				existingPropsToUpdate.getJSONObject(formTypeString).getJSONObject(
						ABaseFluidJSONObject.JSONMapping.Elastic.PROPERTIES);

		//Merge existing with new...
		for(String existingKey : existingPropertiesUpdated.keySet()) {
			newContentMappingBuilderFromParam.put(existingKey,
					existingPropertiesUpdated.get(existingKey));
		}

		//Check to see whether there are any new fields added...
		boolean noChanges = true;
		for(String possibleExistingKey : newContentMappingBuilderFromParam.keySet()) {
			if(!existingPropertiesUpdated.has(possibleExistingKey)) {
				noChanges = false;
				break;
			}
		}

		if(noChanges) {
			return;
		}

		//Update the properties to new values...
		existingPropsToUpdate.put(
				ABaseFluidJSONObject.JSONMapping.Elastic.PROPERTIES,
				newContentMappingBuilderFromParam);

		//Set the additional properties...
		this.setAdditionalProps(
				existingPropsToUpdate,
				parentTypeParam);

		//Push the change...
		PutMappingRequestBuilder putMappingRequestBuilder =
				this.client.admin().indices().preparePutMapping(indexParam);

		putMappingRequestBuilder = putMappingRequestBuilder.setType(formTypeString);
		putMappingRequestBuilder = putMappingRequestBuilder.setSource(
				existingPropsToUpdate.toString(), XContentType.JSON);

		PutMappingResponse putMappingResponse = putMappingRequestBuilder.get();

		if(!putMappingResponse.isAcknowledged()) {
			throw new FluidElasticSearchException(
					"Index Update for '"+
							indexParam+"' and type '"+
							formTypeString+"' not acknowledged by ElasticSearch.");
		}
	}

	/**
	 * Set the additional properties on the {@code existingPropsToUpdateParam} json object.
	 *
	 * @param existingPropsToUpdateParam The existing properties to update.
	 * @param parentTypeParam The {@code _parent} type to set.
	 */
	private void setAdditionalProps(
			JSONObject existingPropsToUpdateParam,
			String parentTypeParam
	) {
		if(parentTypeParam == null || parentTypeParam.trim().length() == 0) {
			return;
		}

		JSONObject typeJson = new JSONObject();
		typeJson.put(Field.JSONMapping.FIELD_TYPE, parentTypeParam);

		existingPropsToUpdateParam.put(
				Form.JSONMapping._PARENT, typeJson);
	}

	/**
	 * Creates a new index or fetches existing index.
	 *
	 * @param indexParam The name of the index to create in lower-case.
	 * @return Newly created or existing index.
	 *
	 * @see GetIndexResponse
	 */
	public GetIndexResponse getOrCreateIndex(String indexParam) {
		if(this.doesIndexExist(indexParam)) {
			return this.client.admin().indices().prepareGetIndex().get();
		} else {
			CreateIndexRequestBuilder createIndexRequestBuilder =
					this.client.admin().indices().prepareCreate(indexParam);

			CreateIndexResponse mappingCreateResponse =
					createIndexRequestBuilder.execute().actionGet();

			if(!mappingCreateResponse.isAcknowledged()) {
				throw new FluidElasticSearchException(
						"Index Creation for '"+
								indexParam+"' not acknowledged by ElasticSearch.");
			}

			return this.client.admin().indices().prepareGetIndex().get();
		}
	}
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.flow;

import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;

/**
 * <p>
 *     Represents a {@code JobView}.
 * </p>
 *
 * @author jasonbruwer
 * @since v1.1
 * @version v1.8
 *
 * @see com.fluidbpm.program.api.vo.role.RoleToJobView
 */
public class JobView extends ABaseFluidJSONObject {

    public static final long serialVersionUID = 1L;

    private String rule;
    private String viewName;
    private String viewGroupName;
    private String viewStepName;
    private String viewFlowName;

    private Integer viewPriority;
    private String viewType;

    /**
     * The JSON mapping for the {@code RoleToJobView} object.
     */
    public static class JSONMapping
    {
        public static final String RULE = "rule";
        public static final String VIEW_NAME = "viewName";
        public static final String VIEW_GROUP_NAME = "viewGroupName";
        public static final String VIEW_STEP_NAME = "viewStepName";
        public static final String VIEW_FLOW_NAME = "viewFlowName";

        public static final String VIEW_PRIORITY = "viewPriority";
        public static final String VIEW_TYPE = "viewType";
    }

    /**
     * Default constructor.
     */
    public JobView() {
        super();
    }

    /**
     * Sets the Id associated with a 'Job View'.
     *
     * @param jobViewIdParam RoleToJobView Id.
     */
    public JobView(Long jobViewIdParam) {
        super();

        this.setId(jobViewIdParam);
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public JobView(JSONObject jsonObjectParam){
        super(jsonObjectParam);

        if(this.jsonObject == null)
        {
            return;
        }

        //Rule...
        if (!this.jsonObject.isNull(JSONMapping.RULE)) {
            this.setRule(this.jsonObject.getString(JSONMapping.RULE));
        }

        //View Name...
        if (!this.jsonObject.isNull(JSONMapping.VIEW_NAME)) {
            this.setViewName(this.jsonObject.getString(
                    JSONMapping.VIEW_NAME));
        }

        //View Group...
        if (!this.jsonObject.isNull(JSONMapping.VIEW_GROUP_NAME)) {
            this.setViewGroupName(this.jsonObject.getString(
                    JSONMapping.VIEW_GROUP_NAME));
        }

        //View Flow...
        if (!this.jsonObject.isNull(JSONMapping.VIEW_FLOW_NAME)) {
            this.setViewFlowName(this.jsonObject.getString(
                    JSONMapping.VIEW_FLOW_NAME));
        }

        //View Step...
        if (!this.jsonObject.isNull(JSONMapping.VIEW_STEP_NAME)) {
            this.setViewStepName(this.jsonObject.getString(
                    JSONMapping.VIEW_STEP_NAME));
        }

        //View Priority...
        if (!this.jsonObject.isNull(JSONMapping.VIEW_PRIORITY)) {
            this.setViewPriority(this.jsonObject.getInt(
                    JSONMapping.VIEW_PRIORITY));
        }

        //View Type...
        if (!this.jsonObject.isNull(JSONMapping.VIEW_TYPE)) {
            this.setViewType(this.jsonObject.getString(
                    JSONMapping.VIEW_TYPE));
        }
    }

    /**
     * Gets the Rule for the View.
     *
     * @return View Rule.
     */
    public String getRule() {
        return this.rule;
    }

    /**
     * Sets the Rule for the View.
     *
     * @param ruleParam View Rule.
     */
    public void setRule(String ruleParam) {
        this.rule = ruleParam;
    }

    /**
     * Gets the Name of the View.
     *
     * @return View Name.
     */
    public String getViewName() {
        return this.viewName;
    }

    /**
     * Sets the Name of the View.
     *
     * @param viewNameParam View Name.
     */
    public void setViewName(String viewNameParam) {
        this.viewName = viewNameParam;
    }

    /**
     * Gets the Name of the View Group.
     *
     * @return View Name.
     */
    public String getViewGroupName() {
        return this.viewGroupName;
    }

    /**
     * Sets the Group Name of the View.
     *
     * @param viewGroupNameParam View Name.
     */
    public void setViewGroupName(String viewGroupNameParam) {
        this.viewGroupName = viewGroupNameParam;
    }

    /**
     * Gets the Step of the View.
     *
     * @return View Step Name.
     */
    public String getViewStepName() {
        return this.viewStepName;
    }

    /**
     * Sets the Step of the View.
     *
     * @param viewStepNameParam View Name.
     */
    public void setViewStepName(String viewStepNameParam) {
        this.viewStepName = viewStepNameParam;
    }

    /**
     * Gets the Flow of the View.
     *
     * @return View Flow Name.
     */
    public String getViewFlowName() {
        return this.viewFlowName;
    }

    /**
     * Sets the Flow of the View.
     *
     * @param viewFlowNameParam View Flow.
     */
    public void setViewFlowName(String viewFlowNameParam) {
        this.viewFlowName = viewFlowNameParam;
    }

    /**
     * Gets the Type of the View.
     *
     * @return Type Name.
     */
    public String getViewType() {
        return this.viewType;
    }

    /**
     * Sets the Type of the View.
     *
     * @param viewTypeParam Type Name.
     */
    public void setViewType(String viewTypeParam) {
        this.viewType = viewTypeParam;
    }

    /**
     * Gets the priority in the Group for the View.
     *
     * @return View Priority.
     */
    public Integer getViewPriority() {
        return this.viewPriority;
    }

    /**
     * Sets the priority in the Group for the View.
     *
     * @param viewPriorityParam View Priority.
     */
    public void setViewPriority(Integer viewPriorityParam) {
        this.viewPriority = viewPriorityParam;
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code RoleToJobView}
     * @throws JSONException If there is a problem with the JSON Body.
     *
     * @see ABaseFluidJSONObject#toJsonObject()
     */
    @Override
    public JSONObject toJsonObject() throws JSONException {

        JSONObject returnVal = super.toJsonObject();

        //Rule...
        if(this.getRule() != null)
        {
            returnVal.put(JSONMapping.RULE, this.getRule());
        }

        //View Name...
        if(this.getViewName() != null)
        {
            returnVal.put(JSONMapping.VIEW_NAME, this.getViewName());
        }

        //View Group Name...
        if(this.getViewGroupName() != null)
        {
            returnVal.put(JSONMapping.VIEW_GROUP_NAME, this.getViewGroupName());
        }

        //View Flow Name...
        if(this.getViewGroupName() != null)
        {
            returnVal.put(JSONMapping.VIEW_FLOW_NAME, this.getViewFlowName());
        }

        //View Step Name...
        if(this.getViewGroupName() != null)
        {
            returnVal.put(JSONMapping.VIEW_STEP_NAME, this.getViewStepName());
        }

        //View Priority...
        if(this.getViewPriority() != null)
        {
            returnVal.put(JSONMapping.VIEW_PRIORITY, this.getViewPriority());
        }

        //View Type...
        if(this.getViewType() != null)
        {
            returnVal.put(JSONMapping.VIEW_TYPE, this.getViewType());
        }

        return returnVal;
    }
}


/*
 * Koekiebox CONFIDENTIAL
 *
 * [2012] - [2017] Koekiebox (Pty) Ltd
 * All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property
 * of Koekiebox and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Koekiebox
 * and its suppliers and may be covered by South African and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material is strictly
 * forbidden unless prior written permission is obtained from Koekiebox.
 */

package com.fluidbpm.program.api.vo.sqlutil.sqlnative;

import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlTransient;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.fluidbpm.program.api.vo.ABaseFluidJSONObject;

/**
 * <p>
 * The Native SQL Query that will be executed on the
 * Fluid BPM Core server.
 * </p>
 *
 * @see java.sql.PreparedStatement
 * @see java.sql.Connection#prepareCall(String)
 *
 * @author jasonbruwer on 2018-03-13
 * @since v1.8
 */
public class NativeSQLQuery extends ABaseFluidJSONObject {

    public static final long serialVersionUID = 1L;

    private String datasourceName;

    private String query;
    private String storedProcedure;

    private List<SQLColumn> sqlInputs;

    /**
     * The JSON mapping for the {@code NativeSQLQuery} object.
     */
    public static class JSONMapping {

        public static final String DATASOURCE_NAME = "datasourceName";
        public static final String QUERY = "query";
        public static final String STORED_PROCEDURE = "storedProcedure";

        public static final String SQL_INPUTS = "sqlInputs";
    }

    /**
     * Default constructor.
     */
    public NativeSQLQuery() {
        super();
    }

    /**
     * Sets the Id associated with a 'User Query'.
     *
     * @param userQueryIdParam UserQuery Id.
     */
    public NativeSQLQuery(Long userQueryIdParam) {
        super();

        this.setId(userQueryIdParam);
    }

    /**
     * Populates local variables with {@code jsonObjectParam}.
     *
     * @param jsonObjectParam The JSON Object.
     */
    public NativeSQLQuery(JSONObject jsonObjectParam){
        super(jsonObjectParam);

        if(this.jsonObject == null)
        {
            return;
        }

        //Datasource Name...
        if (!this.jsonObject.isNull(JSONMapping.DATASOURCE_NAME)) {
            this.setDatasourceName(
                    this.jsonObject.getString(JSONMapping.DATASOURCE_NAME));
        }

        //Query...
        if (!this.jsonObject.isNull(JSONMapping.QUERY)) {
            this.setQuery(this.jsonObject.getString(JSONMapping.QUERY));
        }

        //Stored Procedure...
        if (!this.jsonObject.isNull(JSONMapping.STORED_PROCEDURE)) {
            this.setStoredProcedure(
                    this.jsonObject.getString(JSONMapping.STORED_PROCEDURE));
        }

        //Inputs...
        if (!this.jsonObject.isNull(JSONMapping.SQL_INPUTS)) {

            JSONArray rulesArr = this.jsonObject.getJSONArray(
                    JSONMapping.SQL_INPUTS);

            List<SQLColumn> inputs = new ArrayList();
            for(int index = 0;index < rulesArr.length();index++)
            {
                inputs.add(new SQLColumn(rulesArr.getJSONObject(index)));
            }

            this.setSqlInputs(inputs);
        }
        else{
            this.setSqlInputs(null);
        }
    }

    /**
     * Conversion to {@code JSONObject} from Java Object.
     *
     * @return {@code JSONObject} representation of {@code UserQuery}
     * @throws JSONException If there is a problem with the JSON Body.
     *
     * @see ABaseFluidJSONObject#toJsonObject()
     */
    @Override
    public JSONObject toJsonObject() throws JSONException {

        JSONObject returnVal = super.toJsonObject();

        //Datasource Name...
        if(this.getDatasourceName() != null) {

            returnVal.put(JSONMapping.DATASOURCE_NAME,
                    this.getDatasourceName());
        }

        //Query...
        if(this.getQuery() != null) {

            returnVal.put(JSONMapping.QUERY, this.getQuery());
        }

        //Stored Procedure...
        if(this.getStoredProcedure() != null) {

            returnVal.put(
                    JSONMapping.STORED_PROCEDURE,
                    this.getStoredProcedure());
        }

        //Inputs...
        if(this.getSqlInputs() != null)
        {
            JSONArray jsonArray = new JSONArray();

            for(SQLColumn toAdd : this.getSqlInputs())
            {
                jsonArray.put(toAdd.toJsonObject());
            }

            returnVal.put(JSONMapping.SQL_INPUTS, jsonArray);
        }

        return returnVal;
    }

    /**
     * Gets name of the datasource alias to use as lookup.
     *
     * @return The datasource name.
     *
     * @see javax.sql.DataSource
     */
    public String getDatasourceName() {
        return this.datasourceName;
    }

    /**
     * Sets name of the datasource alias to use as lookup.
     *
     * @param datasourceNameParam The datasource name.
     *
     * @see javax.sql.DataSource
     */
    public void setDatasourceName(String datasourceNameParam) {
        this.datasourceName = datasourceNameParam;
    }

    /**
     * Gets SQL Query to execute.
     * If the query is not provided, the stored procedure should be.
     *
     * @return The SQL Query to execute.
     *
     * @see PreparedStatement#executeQuery()
     */
    public String getQuery() {
        return this.query;
    }

    /**
     * Sets SQL Query to execute.
     * If the query is not provided, the stored procedure should be.
     *
     * @param queryParam The SQL Query to execute.
     *
     * @see PreparedStatement#executeQuery()
     */
    public void setQuery(String queryParam) {
        this.query = queryParam;
    }

    /**
     * Get the stored procedure to execute.
     *
     * The format for the procedure is as follows;
     *
     * {@code {{call spShowSuppliers(?,?,?)}}}
     * {@code {{call spShowSuppliers()}}}
     *
     * @return The stored procedure.
     */
    public String getStoredProcedure() {
        return this.storedProcedure;
    }

    /**
     * Set the stored procedure to execute.
     *
     * The format for the procedure is as follows;
     *
     * {@code {{call spShowSuppliers(?,?,?)}}}
     * {@code {{call spShowSuppliers()}}}
     *
     * @param storedProcedureParam The stored procedure to execute.
     *
     * @see java.sql.Connection#prepareCall(String)
     */
    public void setStoredProcedure(String storedProcedureParam) {
        this.storedProcedure = storedProcedureParam;
    }

    /**
     * Add a SQL input parameter.
     * If the sql inputs is {@code null}, a new instance
     * of {@code ArrayList} will be created prior to adding the parameter.
     *
     * @param sqlInputToAddParam The SQL Input to add.
     *
     * @see SQLColumn
     */
    @XmlTransient
    public void addSqlInput(SQLColumn sqlInputToAddParam){
        if(this.sqlInputs == null){
            this.sqlInputs = new ArrayList<>();
        }

        if(sqlInputToAddParam == null){
            return;
        }

        this.sqlInputs.add(sqlInputToAddParam);
    }

    /**
     * Get the SQL input parameters.
     *
     * @return The SQL input parameters.
     */
    public List<SQLColumn> getSqlInputs() {
        return this.sqlInputs;
    }

    /**
     * Set the SQL input parameters.
     *
     * @param sqlInputsParam The SQL input parameters.
     */
    public void setSqlInputs(List<SQLColumn> sqlInputsParam) {
        this.sqlInputs = sqlInputsParam;
    }

    /**
     * Checks whether the stored procedure value is populated.
     *
     * @return {@code true} if stored procedure is populated, otherwise {@code false}.
     */
    @XmlTransient
    public boolean isTypeStoredProcedure(){

        return (this.getStoredProcedure() == null ||
                this.getStoredProcedure().trim().isEmpty()) ? false:true;
    }
}