var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import moment from 'moment';
import { DateTimeValue } from '../util/DateTimeValue';
import { DateValue } from '../util/DateValue';
import { TimeValue } from '../util/TimeValue';
import { CodeRef } from './CodeRef';
import { DataAnnotation } from './DataAnnotation';
import { GpsReading } from './GpsReading';
import { MapLocation } from './MapLocation';
import { ObjectRef } from './ObjectRef';
import { TypeNames } from './types';
/**
 * Represents a 'value' or field in a row or record. See {@link Record}
 * A Prop has a corresponding {@link PropertyDef} that describes the property.
 * Like an {@link Record}, a Prop may also have {@link Annotation}s (style annotations),
 * but these apply to the property only
 */
export class Property {
    /**
     *
     * @param {string} name
     * @param value
     * @param {string} format
     * @param {Array<DataAnnotation>} annotations
     */
    constructor(name, value, format, annotations = []) {
        this.name = name;
        this.value = value;
        this.format = format;
        this.annotations = annotations;
        this.type = TypeNames.PropertyTypeName;
    }
    static fromJSON(jsonObject, modelUtil) {
        return modelUtil.jsonToModel(jsonObject.annotations).then((annotations) => __awaiter(this, void 0, void 0, function* () {
            const value = yield Property.parseJSONValue(jsonObject.value, jsonObject.format, modelUtil);
            return new Property(jsonObject.name, value, jsonObject.format, annotations);
        }));
    }
    /**
     * Produce a value that can be used for comparison purposes
     * Props considered 'equal' should produce the same identity value
     *
     * @param o
     * @returns {any}
     */
    static valueForCompare(o) {
        if (o === null || o === undefined) {
            return o;
        }
        if (typeof o === 'number') {
            return o;
        }
        else if (typeof o === 'object') {
            if (o instanceof Date) {
                return o.getTime();
            }
            else if (o instanceof DateValue) {
                return o.dateObj.getTime();
            }
            else if (o instanceof DateTimeValue) {
                return o.dateObj.getTime();
            }
            else if (o instanceof TimeValue) {
                return o.toDateValue().getTime();
            }
            else if (o instanceof CodeRef) {
                return o.code;
            }
            else if (o instanceof ObjectRef) {
                return o.objectId;
            }
            else if (o instanceof GpsReading) {
                return o.toString();
            }
            else if (o instanceof MapLocation) {
                return o.toString();
            }
            else {
                return String(o);
            }
        }
        else {
            return String(o);
        }
    }
    equals(prop) {
        return this.name === prop.name && this.value === prop.value;
    }
    get backgroundColor() {
        return DataAnnotation.backgroundColor(this.annotations);
    }
    get foregroundColor() {
        return DataAnnotation.foregroundColor(this.annotations);
    }
    get imageName() {
        return DataAnnotation.imageName(this.annotations);
    }
    get imagePlacement() {
        return DataAnnotation.imagePlacement(this.annotations);
    }
    get isBoldText() {
        return DataAnnotation.isBoldText(this.annotations);
    }
    get isItalicText() {
        return DataAnnotation.isItalicText(this.annotations);
    }
    get isPlacementCenter() {
        return DataAnnotation.isPlacementCenter(this.annotations);
    }
    get isPlacementLeft() {
        return DataAnnotation.isPlacementLeft(this.annotations);
    }
    get isPlacementRight() {
        return DataAnnotation.isPlacementRight(this.annotations);
    }
    get isPlacementStretchUnder() {
        return DataAnnotation.isPlacementStretchUnder(this.annotations);
    }
    get isPlacementUnder() {
        return DataAnnotation.isPlacementUnder(this.annotations);
    }
    get isUnderline() {
        return DataAnnotation.isUnderlineText(this.annotations);
    }
    get overrideText() {
        return DataAnnotation.overrideText(this.annotations);
    }
    get tipText() {
        return DataAnnotation.tipText(this.annotations);
    }
    /**
     * Get a primative value appropriate for comparison
     */
    get comparable() {
        return Property.valueForCompare(this.value);
    }
    /*
     Prepare value to be written to server
     */
    get valueForWrite() {
        const o = this.value;
        if (typeof o === 'object') {
            if (o instanceof Date) {
                // remove the 'Z' from the end of the ISO string for now, until the server supports timezones...
                return o.toISOString().slice(0, -1);
            }
            else if (o instanceof DateTimeValue) {
                // remove the 'Z' from the end of the ISO string for now, until the server supports timezones...
                return o.dateObj.toISOString().slice(0, -1);
            }
            else if (o instanceof DateValue) {
                // remove all Time information from the end of the ISO string from the 'T' to the end...
                const isoString = o.dateObj.toISOString();
                return isoString.slice(0, isoString.indexOf('T'));
            }
            else if (o instanceof TimeValue) {
                return o.toString();
            }
            else {
                // for any other type of value, return the object itself
                // this could include string, Array, CodeRef, ObjectRef, GpsReadingProperty, MapLocationProperty, InlineBinaryRef
                return o;
            }
        }
        else {
            return o;
        }
    }
    toJSON() {
        const jsonObject = {
            name: this.name,
            value: this.valueForWrite,
            type: TypeNames.PropertyTypeName,
            propertyType: null,
            format: null
        };
        if (this.format) {
            jsonObject.format = this.format;
        }
        return jsonObject;
    }
    static parseJSONValue(value, format, modelUtil) {
        let result = value;
        if (value && typeof value === 'string' && format) {
            if (['integer', 'decimal', 'int32', 'int64', 'float', 'double'].some(v => format === v)) {
                result = Number(value);
            }
            else if (format === 'date') {
                // parse as ISO - no offset specified by server right now, so we assume local time
                result = moment(value, 'YYYY-M-D').toDate();
            }
            else if (format === 'date-time') {
                // parse as ISO - no offset specified by server right now, so we assume local time
                // strip invalid suffix (sometimes) provided by server
                const i = value.indexOf('T0:');
                result = moment(i > -1 ? value.substring(0, i) : value).toDate();
            }
            else if (format === 'time') {
                result = TimeValue.fromString(value);
            }
            else {
                result = value;
            }
        }
        else if (value && typeof value === 'object' && value.type) {
            return modelUtil.jsonToModel(value);
        }
        return Promise.resolve(result);
    }
}
