/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 com.datastax.driver.core;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import java.util.Arrays;
import java.util.List;
import org.assertj.core.api.AbstractListAssert;
import org.assertj.core.data.Index;

// better use ListAssert than IterableAssert to avoid attempting to consume the resultset more than
// once
public class ResultSetAssert extends AbstractListAssert<ResultSetAssert, List<Row>, Row> {

  // a helper assert object to simplify assertions on row contents
  // by considering a row as a mere tuple
  private final TupleListAssert helper;

  public ResultSetAssert(ResultSet actual) {
    super(actual.all(), ResultSetAssert.class);
    helper = new TupleListAssert(Lists.transform(this.actual, ROW_TO_TUPLE));
  }

  public static Tuple row(Object... cols) {
    return new Tuple(cols);
  }

  public ResultSetAssert contains(Tuple value, Index index) {
    helper.contains(value, index);
    return this;
  }

  public ResultSetAssert containsSubsequence(Tuple... sequence) {
    helper.containsSubsequence(sequence);
    return this;
  }

  public ResultSetAssert containsOnly(Tuple... values) {
    helper.containsOnly(values);
    return this;
  }

  public ResultSetAssert endsWith(Tuple... sequence) {
    helper.endsWith(sequence);
    return this;
  }

  public ResultSetAssert startsWith(Tuple... sequence) {
    helper.startsWith(sequence);
    return this;
  }

  public ResultSetAssert doesNotContain(Tuple value, Index index) {
    helper.doesNotContain(value, index);
    return this;
  }

  public ResultSetAssert doesNotContain(Tuple... values) {
    helper.doesNotContain(values);
    return this;
  }

  public ResultSetAssert containsExactly(Tuple... values) {
    helper.containsExactly(values);
    return this;
  }

  public ResultSetAssert containsOnlyOnce(Tuple... values) {
    helper.containsOnlyOnce(values);
    return this;
  }

  public ResultSetAssert contains(Tuple... values) {
    helper.contains(values);
    return this;
  }

  public ResultSetAssert containsSequence(Tuple... sequence) {
    helper.containsSequence(sequence);
    return this;
  }

  public static class Tuple {

    final Object[] cols;

    public Tuple(Object... cols) {
      this.cols = cols;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (getClass() != o.getClass()) return false;
      Tuple tuple = (Tuple) o;
      return Arrays.equals(cols, tuple.cols);
    }

    @Override
    public int hashCode() {
      return Arrays.hashCode(cols);
    }

    @Override
    public String toString() {
      return '(' + Joiner.on(',').join(cols) + ')';
    }
  }

  private static final Function<Row, Tuple> ROW_TO_TUPLE =
      new Function<Row, Tuple>() {
        @Override
        public Tuple apply(Row input) {
          Object[] cols = new Object[input.getColumnDefinitions().size()];
          for (int i = 0; i < input.getColumnDefinitions().size(); i++) {
            cols[i] = input.getObject(i);
          }
          return new Tuple(cols);
        }
      };

  private static class TupleListAssert
      extends AbstractListAssert<TupleListAssert, List<Tuple>, Tuple> {

    private TupleListAssert(List<Tuple> rows) {
      super(rows, TupleListAssert.class);
    }
  }
}
