/**
 * Copyright 2015 Palantir Technologies
 *
 * Licensed under the BSD-3 License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/BSD-3-Clause
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.palantir.atlasdb.keyvalue.api;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.primitives.UnsignedBytes;

public class RowResult<T> implements Serializable {
    private static final long serialVersionUID = 1L;

    private final byte[] row;
    private final ImmutableSortedMap<byte[], T> columns;

    public static <T> RowResult<T> of(Cell cell, T value) {
        return new RowResult<T>(cell.getRowName(),
            ImmutableSortedMap.<byte[], T>orderedBy(UnsignedBytes.lexicographicalComparator())
                .put(cell.getColumnName(), value).build());
    }

    @JsonCreator
    public static <T> RowResult<T> create(@JsonProperty("row") byte[] row,
                                          @JsonProperty("columns") SortedMap<byte[], T> columns) {
        return new RowResult<T>(row, columns);
    }

    private RowResult(byte[] row, SortedMap<byte[], T> columns) {
        Preconditions.checkArgument(Cell.isNameValid(row));
        Preconditions.checkArgument(UnsignedBytes.lexicographicalComparator().equals(columns.comparator()), "comparator for the map must be the bytes comparator");
        for (byte[] colName : columns.keySet()) {
            Preconditions.checkArgument(Cell.isNameValid(colName));
        }
        this.row = row.clone();
        this.columns = ImmutableSortedMap.copyOf(columns, UnsignedBytes.lexicographicalComparator());
    }

    @JsonProperty("row")
    public byte[] getRowName() {
        return row.clone();
    }

    @JsonProperty("columns")
    public SortedMap<byte[], T> getColumns() {
        return columns;
    }

    @JsonIgnore
    public Set<Cell> getCellSet() {
        Set<Cell> cells = Sets.newHashSet();
        for (byte[] column : columns.keySet()) {
            cells.add(Cell.create(row, column));
        }
        return cells;
    }

    public static <T> Ordering<RowResult<T>> getOrderingByRowName() {
        return Ordering.from(UnsignedBytes.lexicographicalComparator()).onResultOf(RowResult.<T>getRowNameFun());
    }

    public static <T> Function<RowResult<T>, byte[]> getRowNameFun() {
        return new Function<RowResult<T>, byte[]>() {
            @Override
            public byte[] apply(RowResult<T> input) {
                return input.getRowName();
            }
        };
    }

    @JsonIgnore
    public T getOnlyColumnValue() {
        Preconditions.checkState(columns.size() == 1,
                "Works only when the row result has a single column value.");
        return Iterables.getOnlyElement(columns.values());
    }

    @JsonIgnore
    public Iterable<Map.Entry<Cell, T>> getCells() {
        return Iterables.transform(columns.entrySet(), new Function<Map.Entry<byte[], T>, Map.Entry<Cell, T>>() {
            @Override
            public Entry<Cell, T> apply(Entry<byte[], T> from) {
                return Maps.immutableEntry(Cell.create(row, from.getKey()), from.getValue());
            }
        });
    }

    @Override
    public String toString() {
        return "RowResult [row=" + Arrays.toString(row) + ", columns="
                + columns + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((columns == null) ? 0 : columns.hashCode());
        result = prime * result + Arrays.hashCode(row);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        @SuppressWarnings("rawtypes")
        RowResult other = (RowResult) obj;
        if (columns == null) {
            if (other.columns != null) {
                return false;
            }
        } else if (!columns.equals(other.columns)) {
            return false;
        }
        if (!Arrays.equals(row, other.row)) {
            return false;
        }
        return true;
    }


}

@@ -785,167 +785,8 @@
 p;%0A%0A
-import com.fasterxml.jackson.annotation.JsonCreator;%0Aimport com.fasterxml.jackson.annotation.JsonIgnore;%0Aimport com.fasterxml.jackson.annotation.JsonProperty;%0A
 impo
@@ -821,16 +821,16 @@
 nction;%0A
+
 import c
@@ -1607,25 +1607,8 @@
  %7D%0A%0A
-    @JsonCreator%0A
     
@@ -1649,107 +1649,19 @@
 ate(
-@JsonProperty(%22row%22) byte%5B%5D row,%0A                                          @JsonProperty(%22columns%22)
+byte%5B%5D row,
  Sor
@@ -2306,33 +2306,8 @@
  %7D%0A%0A
-    @JsonProperty(%22row%22)%0A
     
@@ -2374,37 +2374,8 @@
  %7D%0A%0A
-    @JsonProperty(%22columns%22)%0A
     
@@ -2448,32 +2448,16 @@
 %0A    %7D%0A%0A
-    @JsonIgnore%0A
     publ
@@ -3133,32 +3133,16 @@
 %0A    %7D%0A%0A
-    @JsonIgnore%0A
     publ
@@ -3371,24 +3371,8 @@
  %7D%0A%0A
-    @JsonIgnore%0A
     

Remove annotations from RowResult.