/*
 * Copyright 2017 Crown Copyright
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 stroom.query.api.v2;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import io.swagger.v3.oas.annotations.media.Schema;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

@JsonPropertyOrder({"componentId", "fields", "rows", "resultRange", "totalResults", "error"})
@JsonInclude(Include.NON_NULL)
@Schema(description = "Object for describing a set of results in a table form that supports grouped data")
public class TableResult extends Result {

    @Schema(required = true)
    @JsonProperty
    private final List<Field> fields;

    @Schema(required = true)
    @JsonProperty
    private final List<Row> rows;

    @Schema(required = true)
    @JsonProperty
    private final OffsetRange resultRange;

    @JsonPropertyDescription("The total number of results in this result set")
    @JsonProperty
    private final Long totalResults;

    @JsonCreator
    public TableResult(@JsonProperty("componentId") final String componentId,
                       @JsonProperty("fields") final List<Field> fields,
                       @JsonProperty("rows") final List<Row> rows,
                       @JsonProperty("resultRange") final OffsetRange resultRange,
                       @JsonProperty("totalResults") final Long totalResults,
                       @JsonProperty("errors") final List<String> errors) {
        super(componentId, errors);
        this.fields = fields;
        this.rows = rows;
        this.resultRange = resultRange;
        this.totalResults = totalResults;
    }

    public List<Field> getFields() {
        return fields;
    }

    public List<Row> getRows() {
        return rows;
    }

    public OffsetRange getResultRange() {
        return resultRange;
    }

    public Long getTotalResults() {
        return totalResults;
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        if (!super.equals(o)) {
            return false;
        }
        final TableResult that = (TableResult) o;
        return Objects.equals(fields, that.fields) &&
                Objects.equals(rows, that.rows) &&
                Objects.equals(resultRange, that.resultRange) &&
                Objects.equals(totalResults, that.totalResults);
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), fields, rows, resultRange, totalResults);
    }

    @Override
    public String toString() {
        if (rows == null) {
            return "0 rows";
        }

        return rows.size() + " rows";
    }

    public static TableResultBuilderImpl builder() {
        return new TableResultBuilderImpl();
    }

    public TableResultBuilderImpl copy() {
        return new TableResultBuilderImpl(this);
    }

    /**
     * Builder for constructing a {@link TableResult tableResult}
     */
    public static final class TableResultBuilderImpl
            implements TableResultBuilder {

        private String componentId;
        private List<Field> fields;
        private final List<Row> rows;
        private List<String> errors;
        private OffsetRange resultRange;
        private Long totalResults;

        private TableResultBuilderImpl() {
            rows = new ArrayList<>();
            errors = Collections.emptyList();
        }

        private TableResultBuilderImpl(final TableResult tableResult) {
            componentId = tableResult.getComponentId();
            fields = tableResult.fields;
            rows = new ArrayList<>(tableResult.rows);
            errors = tableResult.getErrors();
            resultRange = tableResult.resultRange;
            totalResults = tableResult.totalResults;
        }

        public TableResultBuilderImpl componentId(final String componentId) {
            this.componentId = componentId;
            return this;
        }

        @Override
        public TableResultBuilderImpl fields(final List<Field> fields) {
            this.fields = fields;
            return this;
        }

        @Override
        public TableResultBuilder addRow(final Row row) {
            this.rows.add(row);
            return this;
        }

        @Override
        public TableResultBuilder errors(final List<String> errors) {
            this.errors = errors;
            return this;
        }

        @Override
        public TableResultBuilder resultRange(final OffsetRange resultRange) {
            this.resultRange = resultRange;
            return this;
        }

        @Override
        public TableResultBuilder totalResults(final Long totalResults) {
            this.totalResults = totalResults;
            return this;
        }

        @Override
        public TableResult build() {
            Long totalResults = this.totalResults;
            if (totalResults == null && rows != null) {
                totalResults = (long) rows.size();
            }
            return new TableResult(componentId, fields, rows, resultRange, totalResults, errors);
        }
    }
}
