/*
 * Copyright 2017, TeamDev Ltd. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.server.storage.datastore;

import com.google.cloud.datastore.DateTime;
import com.google.cloud.datastore.Entity;
import com.google.cloud.datastore.StructuredQuery.OrderBy;
import com.google.protobuf.Message;
import com.google.protobuf.Timestamp;
import com.google.protobuf.util.Timestamps;
import io.spine.base.Version;
import io.spine.server.storage.LifecycleFlagField;
import io.spine.string.Stringifiers;

import java.util.Date;

import static com.google.cloud.datastore.StructuredQuery.OrderBy.asc;
import static com.google.cloud.datastore.StructuredQuery.OrderBy.desc;
import static com.google.common.base.Preconditions.checkNotNull;
import static io.spine.server.aggregate.storage.AggregateField.aggregate_id;
import static io.spine.server.storage.LifecycleFlagField.archived;

/**
 * Utility class, which simplifies creation of the Datastore properties.
 *
 * @author Mikhail Mikhaylov
 * @author Dmytro Dashenkov
 */
@SuppressWarnings("UtilityClass")
final class DsProperties {

    private DsProperties() {
        // Prevent utility class instantiation.
    }

    /**
     * Makes AggregateId property from given {@link Message} value.
     */
    static void addAggregateIdProperty(Object aggregateId, Entity.Builder entity) {
        final String propertyValue = Stringifiers.toString(aggregateId);
        entity.set(aggregate_id.toString(), propertyValue);
    }

    static void addCreatedProperty(Timestamp when, Entity.Builder entity) {
        final long millis = Timestamps.toMillis(when);
        final Date date = new Date(millis);
        final DateTime dateTime = DateTime.copyFrom(date);
        entity.set(AggregateEventRecordProperty.CREATED.toString(), dateTime);
    }

    static void addVersionProperty(Version version, Entity.Builder entity) {
        final int number = version.getNumber();
        entity.set(AggregateEventRecordProperty.VERSION.toString(), number);
    }

    static void markSnapshotProperty(boolean snapshot, Entity.Builder entity) {
        entity.set(AggregateEventRecordProperty.SNAPSHOT.toString(), snapshot);
    }

    static void addArchivedProperty(Entity.Builder entity, boolean archived) {
        entity.set(LifecycleFlagField.archived.toString(), archived);
    }

    static void addDeletedProperty(Entity.Builder entity, boolean deleted) {
        entity.set(LifecycleFlagField.deleted.toString(), deleted);
    }

    static boolean isArchived(Entity entity) {
        checkNotNull(entity);
        return hasFlag(entity, archived.toString());
    }

    static boolean isDeleted(Entity entity) {
        checkNotNull(entity);
        return hasFlag(entity, LifecycleFlagField.deleted.toString());
    }

    static OrderBy byCreatedTime() {
        return AggregateEventRecordOrdering.BY_CREATED_PROPERTY.getOrdering();
    }

    static OrderBy byVersion() {
        return AggregateEventRecordOrdering.BY_VERSION_PROPERTY.getOrdering();
    }

    static OrderBy byRecordType() {
        return AggregateEventRecordOrdering.BY_SNAPSHOT_PROPERTY.getOrdering();
    }

    private static boolean hasFlag(Entity entity, String flagName) {
        final boolean result =
                entity.contains(flagName)
                        && entity.getBoolean(flagName);
        return result;
    }

    private enum AggregateEventRecordProperty {

        CREATED,
        VERSION,
        SNAPSHOT;

        @Override
        public String toString() {
            return name().toLowerCase();
        }
    }

    private enum AggregateEventRecordOrdering {

        BY_CREATED_PROPERTY(desc(AggregateEventRecordProperty.CREATED.toString())),
        BY_VERSION_PROPERTY(desc(AggregateEventRecordProperty.VERSION.toString())),
        BY_SNAPSHOT_PROPERTY(asc(AggregateEventRecordProperty.SNAPSHOT.toString()));

        private final OrderBy ordering;

        AggregateEventRecordOrdering(OrderBy ordering) {
            this.ordering = ordering;
        }

        private OrderBy getOrdering() {
            return ordering;
        }
    }
}

@@ -1398,16 +1398,61 @@
 gField;%0A
+import io.spine.server.storage.StorageField;%0A
 import i
@@ -2725,39 +2725,39 @@
 tRecordProperty.
-CREATED
+created
 .toString(), dat
@@ -2936,39 +2936,39 @@
 tRecordProperty.
-VERSION
+version
 .toString(), num
@@ -3104,32 +3104,32 @@
 ordProperty.
-SNAPSHOT
+snapshot
 .toString(),
@@ -3837,25 +3837,16 @@
 _CREATED
-_PROPERTY
 .getOrde
@@ -3947,25 +3947,16 @@
 _VERSION
-_PROPERTY
 .getOrde
@@ -4061,25 +4061,16 @@
 SNAPSHOT
-_PROPERTY
 .getOrde
@@ -4358,16 +4358,40 @@
 roperty 
+implements StorageField 
 %7B%0A%0A     
@@ -4397,16 +4397,11 @@
     
-CREATED,
+/**
 %0A   
@@ -4409,129 +4409,330 @@
     
-VERSION,%0A        SNAPSHOT;%0A%0A        @Override%0A        public String toString() %7B%0A            return name().toLowerCase();
+ * A property storing the Event creation time.%0A         */%0A        created,%0A%0A        /**%0A         * A property storing the Aggregate version.%0A         */%0A        version,%0A%0A        /**%0A         * A boolean property storing %7B@code true%7D if the Record represents a Snapshot and%0A         * %7B@code false%7D otherwise.%0A         */
 %0A   
@@ -4732,25 +4732,32 @@
  */%0A        
-%7D
+snapshot
 %0A    %7D%0A%0A    
@@ -4819,25 +4819,16 @@
 _CREATED
-_PROPERTY
 (desc(Ag
@@ -4854,23 +4854,23 @@
 roperty.
-CREATED
+created
 .toStrin
@@ -4894,25 +4894,16 @@
 _VERSION
-_PROPERTY
 (desc(Ag
@@ -4929,23 +4929,23 @@
 roperty.
-VERSION
+version
 .toStrin
@@ -4974,17 +4974,8 @@
 SHOT
-_PROPERTY
 (asc
@@ -5004,24 +5004,24 @@
 roperty.
-SNAPSHOT
+snapshot
 .toStrin

Simplify the `AggregateEventRecordProperty` enum naming and add some Javadoc.