/*
 * 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 org.autobet.ai;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.Iterables;
import org.autobet.model.Bet;
import org.autobet.model.Game;
import org.autobet.util.GamesProcessorDriver;
import org.autobet.util.KeyValueStore;
import org.javalite.activejdbc.Base;
import org.javalite.activejdbc.RowListener;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;

public class PlayerEvaluator
{
    private final GamesProcessorDriver gamesProcessorDriver;
    private final Player player;

    public PlayerEvaluator(GamesProcessorDriver gamesProcessorDriver, Player player)
    {
        this.gamesProcessorDriver = gamesProcessorDriver;
        this.player = player;
    }

    public Statistics evaluate(Optional<Integer> limit)
    {
        return gamesProcessorDriver.driveProcessors(() -> new GameProcessor(), limit);
    }

    private final class GameProcessor
            implements GamesProcessorDriver.GamesProcessor<Statistics>
    {
        private double result = 0;

        @Override
        public void process(Game game)
        {
            List<Bet> selectedBets = player.guess(game, game.getBets());
            for (Bet bet : selectedBets) {
                if (bet.isWinning(game)) {
                    result += bet.getDouble("odds");
                }
                result -= 1;
            }
        }

        @Override
        public Statistics finish()
        {
            return new Statistics(player.getName(), result);
        }
    }

    public static class Statistics
            implements KeyValueStore.Storable<Statistics>
    {
        private final String storageKey;
        private final double result;

        @JsonCreator
        public Statistics(@JsonProperty("storageKey") String storageKey, @JsonProperty("result") double result)
        {
            this.storageKey = storageKey;
            this.result = result;
        }

        @JsonProperty("result")
        public double getResult()
        {
            return result;
        }

        @JsonProperty("storageKey")
        @Override
        public String getStorageKey()
        {
            return storageKey;
        }

        @Override
        public Statistics merge(Statistics other)
        {
            checkArgument(
                    storageKey.equals(other.getStorageKey()),
                    "Statistics have to have same storageKey to be merged: %s vs %s",
                    storageKey,
                    other.getStorageKey());

            return new Statistics(storageKey, result + other.getResult());
        }
    }

    private static <T> T querySingleValue(String query, Object... params)
    {
        SingleValueRowProcessor singleValueRowProcessor = new SingleValueRowProcessor();
        Base.find(query, params)
                .with(singleValueRowProcessor);
        T value = singleValueRowProcessor.get();
        return (T) requireNonNull(value, () -> "got null for: '" + query + "' with params: " + Arrays.toString(params));
    }

    private static class SingleValueRowProcessor
            implements RowListener
    {
        Object value;

        @Override
        public boolean next(Map<String, Object> row)
        {
            checkState(value == null, "Query returned to many rows");
            checkState(row.size() == 1, "Query returned to many columns");
            value = Iterables.getOnlyElement(row.values());
            return true;
        }

        public <T> T get()
        {
            return (T) value;
        }
    }
}

@@ -692,52 +692,8 @@
 ty;%0A
-import com.google.common.collect.Iterables;%0A
 impo
@@ -838,114 +838,8 @@
 re;%0A
-import org.javalite.activejdbc.Base;%0Aimport org.javalite.activejdbc.RowListener;%0A%0Aimport java.util.Arrays;
 %0Aimp
@@ -862,30 +862,8 @@
 st;%0A
-import java.util.Map;%0A
 impo
@@ -955,119 +955,8 @@
 ent;
-%0Aimport static com.google.common.base.Preconditions.checkState;%0Aimport static java.util.Objects.requireNonNull;
 %0A%0Apu
@@ -3154,953 +3154,7 @@
    %7D
-%0A%0A    private static %3CT%3E T querySingleValue(String query, Object... params)%0A    %7B%0A        SingleValueRowProcessor singleValueRowProcessor = new SingleValueRowProcessor();%0A        Base.find(query, params)%0A                .with(singleValueRowProcessor);%0A        T value = singleValueRowProcessor.get();%0A        return (T) requireNonNull(value, () -%3E %22got null for: '%22 + query + %22' with params: %22 + Arrays.toString(params));%0A    %7D%0A%0A    private static class SingleValueRowProcessor%0A            implements RowListener%0A    %7B%0A        Object value;%0A%0A        @Override%0A        public boolean next(Map%3CString, Object%3E row)%0A        %7B%0A            checkState(value == null, %22Query returned to many rows%22);%0A            checkState(row.size() == 1, %22Query returned to many columns%22);%0A            value = Iterables.getOnlyElement(row.values());%0A            return true;%0A        %7D%0A%0A        public %3CT%3E T get()%0A        %7B%0A            return (T) value;%0A        %7D%0A    %7D
 %0A%7D%0A

Remove unused code