/*
 * Copyright 2019-2023 The Polypheny Project
 *
 * 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.polypheny.db.sql;


import com.google.common.collect.ImmutableList;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.AbstractList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.calcite.linq4j.AbstractEnumerable;
import org.apache.calcite.linq4j.BaseQueryable;
import org.apache.calcite.linq4j.Enumerable;
import org.apache.calcite.linq4j.Enumerator;
import org.apache.calcite.linq4j.Linq4j;
import org.apache.calcite.linq4j.Queryable;
import org.apache.calcite.linq4j.function.Deterministic;
import org.apache.calcite.linq4j.function.Parameter;
import org.apache.calcite.linq4j.function.SemiStrict;
import org.apache.calcite.linq4j.tree.Types;
import org.polypheny.db.adapter.DataContext;
import org.polypheny.db.adapter.java.AbstractQueryableTable;
import org.polypheny.db.algebra.type.AlgDataType;
import org.polypheny.db.algebra.type.AlgDataTypeFactory;
import org.polypheny.db.nodes.Call;
import org.polypheny.db.nodes.Node;
import org.polypheny.db.runtime.functions.Functions;
import org.polypheny.db.schema.QueryableTable;
import org.polypheny.db.schema.ScannableTable;
import org.polypheny.db.schema.Schema;
import org.polypheny.db.schema.SchemaPlus;
import org.polypheny.db.schema.Statistic;
import org.polypheny.db.schema.Statistics;
import org.polypheny.db.schema.impl.AbstractTable;
import org.polypheny.db.type.PolyType;


/**
 * Holder for various classes and functions used in tests as user-defined functions and so forth.
 */
public class Smalls {

    public static final Method GENERATE_STRINGS_METHOD = Types.lookupMethod( Smalls.class, "generateStrings", Integer.class );
    public static final Method MAZE_METHOD = Types.lookupMethod( MazeTable.class, "generate", int.class, int.class, int.class );
    public static final Method MAZE2_METHOD = Types.lookupMethod( MazeTable.class, "generate2", int.class, int.class, Integer.class );
    public static final Method MAZE3_METHOD = Types.lookupMethod( MazeTable.class, "generate3", String.class );
    public static final Method MULTIPLICATION_TABLE_METHOD = Types.lookupMethod( Smalls.class, "multiplicationTable", int.class, int.class, Integer.class );
    public static final Method FIBONACCI_TABLE_METHOD = Types.lookupMethod( Smalls.class, "fibonacciTable" );
    public static final Method FIBONACCI2_TABLE_METHOD = Types.lookupMethod( Smalls.class, "fibonacciTableWithLimit", long.class );
    public static final Method VIEW_METHOD = Types.lookupMethod( Smalls.class, "view", String.class );
    public static final Method STR_METHOD = Types.lookupMethod( Smalls.class, "str", Object.class, Object.class );
    public static final Method STRING_UNION_METHOD = Types.lookupMethod( Smalls.class, "stringUnion", Queryable.class, Queryable.class );
    public static final Method PROCESS_CURSOR_METHOD = Types.lookupMethod( Smalls.class, "processCursor", int.class, Enumerable.class );
    public static final Method PROCESS_CURSORS_METHOD = Types.lookupMethod( Smalls.class, "processCursors", int.class, Enumerable.class, Enumerable.class );


    private Smalls() {
    }


    private static QueryableTable oneThreePlus( String s ) {
        List<Integer> items;
        // Argument is null in case SQL contains function call with expression. Then the engine calls a function with null arguments to get getRowType.
        if ( s == null ) {
            items = ImmutableList.of();
        } else {
            Integer latest = Integer.parseInt( s.substring( 1, s.length() - 1 ) );
            items = ImmutableList.of( 1, 3, latest );
        }
        final Enumerable<Integer> enumerable = Linq4j.asEnumerable( items );
        return new AbstractQueryableTable( Integer.class ) {
            @Override
            public <E> Queryable<E> asQueryable( DataContext dataContext, SchemaPlus schema, String tableName ) {
                //noinspection unchecked
                return (Queryable<E>) enumerable.asQueryable();
            }


            @Override
            public AlgDataType getRowType( AlgDataTypeFactory typeFactory ) {
                return typeFactory.builder().add( "c", null, PolyType.INTEGER ).build();
            }
        };
    }


    public static <T> Queryable<T> stringUnion( Queryable<T> q0, Queryable<T> q1 ) {
        return q0.concat( q1 );
    }


    /**
     * A function that generates a table that generates a sequence of {@link IntString} values.
     */
    public static QueryableTable generateStrings( final Integer count ) {
        return new AbstractQueryableTable( IntString.class ) {
            @Override
            public AlgDataType getRowType( AlgDataTypeFactory typeFactory ) {
                return typeFactory.createJavaType( IntString.class );
            }


            @Override
            public <T> Queryable<T> asQueryable( DataContext dataContext, SchemaPlus schema, String tableName ) {
                BaseQueryable<IntString> queryable =
                        new BaseQueryable<IntString>( null, IntString.class, null ) {
                            @Override
                            public Enumerator<IntString> enumerator() {
                                return new Enumerator<IntString>() {
                                    static final String Z = "abcdefghijklm";

                                    int i = 0;
                                    int curI;
                                    String curS;


                                    @Override
                                    public IntString current() {
                                        return new IntString( curI, curS );
                                    }


                                    @Override
                                    public boolean moveNext() {
                                        if ( i < count ) {
                                            curI = i;
                                            curS = Z.substring( 0, i % Z.length() );
                                            ++i;
                                            return true;
                                        } else {
                                            return false;
                                        }
                                    }


                                    @Override
                                    public void reset() {
                                        i = 0;
                                    }


                                    @Override
                                    public void close() {
                                    }
                                };
                            }
                        };
                //noinspection unchecked
                return (Queryable<T>) queryable;
            }
        };
    }


    /**
     * A function that generates multiplication table of {@code ncol} columns x {@code nrow} rows.
     */
    public static QueryableTable multiplicationTable( final int ncol, final int nrow, Integer offset ) {
        final int offs = offset == null ? 0 : offset;
        return new AbstractQueryableTable( Object[].class ) {
            @Override
            public AlgDataType getRowType( AlgDataTypeFactory typeFactory ) {
                final AlgDataTypeFactory.Builder builder = typeFactory.builder();
                builder.add( "row_name", null, typeFactory.createJavaType( String.class ) );
                final AlgDataType int_ = typeFactory.createJavaType( int.class );
                for ( int i = 1; i <= ncol; i++ ) {
                    builder.add( "c" + i, null, int_ );
                }
                return builder.build();
            }


            @Override
            public Queryable<Object[]> asQueryable( DataContext dataContext, SchemaPlus schema, String tableName ) {
                final List<Object[]> table = new AbstractList<Object[]>() {
                    @Override
                    public Object[] get( int index ) {
                        Object[] cur = new Object[ncol + 1];
                        cur[0] = "row " + index;
                        for ( int j = 1; j <= ncol; j++ ) {
                            cur[j] = j * (index + 1) + offs;
                        }
                        return cur;
                    }


                    @Override
                    public int size() {
                        return nrow;
                    }
                };
                return Linq4j.asEnumerable( table ).asQueryable();
            }
        };
    }


    /**
     * A function that generates the Fibonacci sequence. Interesting because it has one column and no arguments.
     */
    public static ScannableTable fibonacciTable() {
        return fibonacciTableWithLimit( -1L );
    }


    /**
     * A function that generates the Fibonacci sequence. Interesting because it has one column and no arguments.
     */
    public static ScannableTable fibonacciTableWithLimit( final long limit ) {
        return new ScannableTable() {
            @Override
            public AlgDataType getRowType( AlgDataTypeFactory typeFactory ) {
                return typeFactory.builder().add( "N", null, PolyType.BIGINT ).build();
            }


            @Override
            public Enumerable<Object[]> scan( DataContext root ) {
                return new AbstractEnumerable<Object[]>() {
                    @Override
                    public Enumerator<Object[]> enumerator() {
                        return new Enumerator<Object[]>() {
                            private long prev = 1;
                            private long current = 0;


                            @Override
                            public Object[] current() {
                                return new Object[]{ current };
                            }


                            @Override
                            public boolean moveNext() {
                                final long next = current + prev;
                                if ( limit >= 0 && next > limit ) {
                                    return false;
                                }
                                prev = current;
                                current = next;
                                return true;
                            }


                            @Override
                            public void reset() {
                                prev = 0;
                                current = 1;
                            }


                            @Override
                            public void close() {
                            }
                        };
                    }
                };
            }


            @Override
            public Statistic getStatistic() {
                return Statistics.UNKNOWN;
            }


            @Override
            public Long getTableId() {
                return null;
            }


            @Override
            public Schema.TableType getJdbcTableType() {
                return Schema.TableType.TABLE;
            }


            @Override
            public boolean isRolledUp( String column ) {
                return false;
            }


            @Override
            public boolean rolledUpColumnValidInsideAgg( String column, Call call, Node parent ) {
                return true;
            }

        };
    }


    /**
     * A function that adds a number to the first column of input cursor
     */
    public static QueryableTable processCursor( final int offset, final Enumerable<Object[]> a ) {
        return new AbstractQueryableTable( Object[].class ) {
            @Override
            public AlgDataType getRowType( AlgDataTypeFactory typeFactory ) {
                return typeFactory.builder()
                        .add( "result", null, PolyType.INTEGER )
                        .build();
            }


            @Override
            public <T> Queryable<T> asQueryable( DataContext dataContext, SchemaPlus schema, String tableName ) {
                final Enumerable<Integer> enumerable = a.select( a0 -> offset + ((Integer) a0[0]) );
                //noinspection unchecked
                return (Queryable) enumerable.asQueryable();
            }
        };
    }


    /**
     * A function that sums the second column of first input cursor, second column of first input and the given int.
     */
    public static QueryableTable processCursors( final int offset, final Enumerable<Object[]> a, final Enumerable<IntString> b ) {
        return new AbstractQueryableTable( Object[].class ) {
            @Override
            public AlgDataType getRowType( AlgDataTypeFactory typeFactory ) {
                return typeFactory.builder()
                        .add( "result", null, PolyType.INTEGER )
                        .build();
            }


            @Override
            public <T> Queryable<T> asQueryable( DataContext dataContext, SchemaPlus schema, String tableName ) {
                final Enumerable<Integer> enumerable = a.zip( b, ( v0, v1 ) -> ((Integer) v0[1]) + v1.n + offset );
                //noinspection unchecked
                return (Queryable) enumerable.asQueryable();
            }
        };
    }


    /**
     * Class with int and String fields.
     */
    public static class IntString {

        public final int n;
        public final String s;


        public IntString( int n, String s ) {
            this.n = n;
            this.s = s;
        }


        public String toString() {
            return "{n=" + n + ", s=" + s + "}";
        }

    }


    /**
     * Example of a UDF with a non-static {@code eval} method, and named parameters.
     */
    public static class MyPlusFunction {

        public static final AtomicInteger INSTANCE_COUNT = new AtomicInteger( 0 );


        // Note: Not marked @Deterministic
        public MyPlusFunction() {
            INSTANCE_COUNT.incrementAndGet();
        }


        public int eval( @Parameter(name = "x") int x, @Parameter(name = "y") int y ) {
            return x + y;
        }

    }


    /**
     * As {@link MyPlusFunction} but declared to be deterministic.
     */
    public static class MyDeterministicPlusFunction {

        public static final AtomicInteger INSTANCE_COUNT = new AtomicInteger( 0 );


        @Deterministic
        public MyDeterministicPlusFunction() {
            INSTANCE_COUNT.incrementAndGet();
        }


        public int eval( @Parameter(name = "x") int x, @Parameter(name = "y") int y ) {
            return x + y;
        }

    }


    /**
     * Example of a UDF with named parameters.
     */
    public static class MyLeftFunction {

        public String eval( @Parameter(name = "s") String s, @Parameter(name = "n") int n ) {
            return s.substring( 0, n );
        }

    }


    /**
     * Example of a UDF with named parameters, some of them optional.
     */
    public static class MyAbcdeFunction {

        public String eval(
                @Parameter(name = "A", optional = false) Integer a,
                @Parameter(name = "B", optional = true) Integer b,
                @Parameter(name = "C", optional = false) Integer c,
                @Parameter(name = "D", optional = true) Integer d,
                @Parameter(name = "E", optional = true) Integer e ) {
            return "{a: " + a + ", b: " + b + ", c: " + c + ", d: " + d + ", e: " + e + "}";
        }

    }


    /**
     * Example of a non-strict UDF. (Does something useful when passed NULL.)
     */
    public static class MyToStringFunction {

        public static String eval( @Parameter(name = "o") Object o ) {
            if ( o == null ) {
                return "<null>";
            }
            return "<" + o.toString() + ">";
        }

    }


    /**
     * Example of a semi-strict UDF. (Returns null if its parameter is null or if its length is 4.)
     */
    public static class Null4Function {

        @SemiStrict
        public static String eval( @Parameter(name = "s") String s ) {
            if ( s == null || s.length() == 4 ) {
                return null;
            }
            return s;
        }

    }


    /**
     * Example of a picky, semi-strict UDF. Throws {@link NullPointerException} if argument is null.
     * Returns null if its argument's length is 8.
     */
    public static class Null8Function {

        @SemiStrict
        public static String eval( @Parameter(name = "s") String s ) {
            if ( s.length() == 8 ) {
                return null;
            }
            return s;
        }

    }


    /**
     * Example of a UDF with a static {@code eval} method. Class is abstract, but code-generator should not need to instantiate it.
     */
    public abstract static class MyDoubleFunction {

        private MyDoubleFunction() {
        }


        public static int eval( int x ) {
            return x * 2;
        }

    }


    /**
     * User-defined function with two arguments.
     */
    public static class MyIncrement {

        public float eval( int x, int y ) {
            return x + x * y / 100;
        }

    }


    /**
     * Example of a UDF that has overloaded UDFs (same name, different args).
     */
    public abstract static class CountArgs0Function {

        private CountArgs0Function() {
        }


        public static int eval() {
            return 0;
        }

    }


    /**
     * See {@link CountArgs0Function}.
     */
    public abstract static class CountArgs1Function {

        private CountArgs1Function() {
        }


        public static int eval( int x ) {
            return 1;
        }

    }


    /**
     * See {@link CountArgs0Function}.
     */
    public abstract static class CountArgs1NullableFunction {

        private CountArgs1NullableFunction() {
        }


        public static int eval( Short x ) {
            return -1;
        }

    }


    /**
     * See {@link CountArgs0Function}.
     */
    public abstract static class CountArgs2Function {

        private CountArgs2Function() {
        }


        public static int eval( int x, int y ) {
            return 2;
        }

    }


    /**
     * Example of a UDF class that needs to be instantiated but cannot be.
     */
    public abstract static class AwkwardFunction {

        private AwkwardFunction() {
        }


        public int eval( int x ) {
            return 0;
        }

    }


    /**
     * UDF class that has multiple methods, some overloaded.
     */
    public static class MultipleFunction {

        private MultipleFunction() {
        }


        // Three overloads
        public static String fun1( String x ) {
            return x.toLowerCase( Locale.ROOT );
        }


        public static int fun1( int x ) {
            return x * 2;
        }


        public static int fun1( int x, int y ) {
            return x + y;
        }


        // Another method
        public static int fun2( int x ) {
            return x * 3;
        }


        // Non-static method cannot be used because constructor is private
        public int nonStatic( int x ) {
            return x * 3;
        }

    }


    /**
     * UDF class that provides user-defined functions for each data type.
     */
    @Deterministic
    public static class AllTypesFunction {

        private AllTypesFunction() {
        }

        // We use SqlFunctions.toLong(Date) ratter than Date.getTime(), and SqlFunctions.internalToTimestamp(long) rather than new Date(long),
        // because the contract of JDBC (also used by UDFs) is to represent date-time values in the LOCAL time zone.


        public static long dateFun( java.sql.Date v ) {
            return v == null ? -1L : Functions.toLong( v );
        }


        public static long timestampFun( java.sql.Timestamp v ) {
            return v == null ? -1L : Functions.toLong( v );
        }


        public static long timeFun( java.sql.Time v ) {
            return v == null ? -1L : Functions.toLong( v );
        }


        /**
         * Overloaded, in a challenging way, with {@link #toDateFun(Long)}.
         */
        public static java.sql.Date toDateFun( int v ) {
            return Functions.internalToDate( v );
        }


        public static java.sql.Date toDateFun( Long v ) {
            return v == null ? null : Functions.internalToDate( v.intValue() );
        }


        public static java.sql.Timestamp toTimestampFun( Long v ) {
            return Functions.internalToTimestamp( v );
        }


        public static java.sql.Time toTimeFun( Long v ) {
            return v == null ? null : Functions.internalToTime( v.intValue() );
        }


        /**
         * for Overloaded user-defined functions that have Double and BigDecimal arguments will goes wrong
         */
        public static double toDouble( BigDecimal var ) {
            return var == null ? null : var.doubleValue();
        }


        public static double toDouble( Double var ) {
            return var == null ? 0.0d : var;
        }


        public static double toDouble( Float var ) {
            return var == null ? 0.0d : Double.parseDouble( var.toString() );
        }


        public static List arrayAppendFun( List v, Integer i ) {
            if ( v == null || i == null ) {
                return null;
            } else {
                v.add( i );
                return v;
            }
        }


        /**
         * Overloaded functions with DATE, TIMESTAMP and TIME arguments.
         */
        public static long toLong( Date date ) {
            return date == null ? 0 : Functions.toLong( date );
        }


        public static long toLong( Timestamp timestamp ) {
            return timestamp == null ? 0 : Functions.toLong( timestamp );
        }


        public static long toLong( Time time ) {
            return time == null ? 0 : Functions.toLong( time );
        }

    }


    /**
     * Example of a user-defined aggregate function (UDAF).
     */
    public static class MySumFunction {

        public MySumFunction() {
        }


        public long init() {
            return 0L;
        }


        public long add( long accumulator, int v ) {
            return accumulator + v;
        }


        public long merge( long accumulator0, long accumulator1 ) {
            return accumulator0 + accumulator1;
        }


        public long result( long accumulator ) {
            return accumulator;
        }

    }


    /**
     * A generic interface for defining user defined aggregate functions
     *
     * @param <A> accumulator type
     * @param <V> value type
     * @param <R> result type
     */
    private interface MyGenericAggFunction<A, V, R> {

        A init();

        A add( A accumulator, V val );

        A merge( A accumulator1, A accumulator2 );

        R result( A accumulator );

    }


    /**
     * Example of a user-defined aggregate function that implements a generic interface.
     */
    public static class MySum3 implements MyGenericAggFunction<Integer, Integer, Integer> {

        @Override
        public Integer init() {
            return 0;
        }


        @Override
        public Integer add( Integer accumulator, Integer val ) {
            return accumulator + val;
        }


        @Override
        public Integer merge( Integer accumulator1, Integer accumulator2 ) {
            return accumulator1 + accumulator2;
        }


        @Override
        public Integer result( Integer accumulator ) {
            return accumulator;
        }

    }


    /**
     * Example of a user-defined aggregate function (UDAF), whose methods are static.
     */
    public static class MyStaticSumFunction {

        public static long init() {
            return 0L;
        }


        public static long add( long accumulator, int v ) {
            return accumulator + v;
        }


        public static long merge( long accumulator0, long accumulator1 ) {
            return accumulator0 + accumulator1;
        }


        public static long result( long accumulator ) {
            return accumulator;
        }

    }


    /**
     * Example of a user-defined aggregate function (UDAF).
     */
    public static class MyTwoParamsSumFunctionFilter1 {

        public MyTwoParamsSumFunctionFilter1() {
        }


        public int init() {
            return 0;
        }


        public int add( int accumulator, int v1, int v2 ) {
            if ( v1 > v2 ) {
                return accumulator + v1;
            }
            return accumulator;
        }


        public int merge( int accumulator0, int accumulator1 ) {
            return accumulator0 + accumulator1;
        }


        public int result( int accumulator ) {
            return accumulator;
        }

    }


    /**
     * Example of a user-defined aggregate function (UDAF).
     */
    public static class MyTwoParamsSumFunctionFilter2 {

        public MyTwoParamsSumFunctionFilter2() {
        }


        public long init() {
            return 0L;
        }


        public long add( long accumulator, int v1, String v2 ) {
            if ( v2.equals( "Eric" ) ) {
                return accumulator + v1;
            }
            return accumulator;
        }


        public long merge( long accumulator0, long accumulator1 ) {
            return accumulator0 + accumulator1;
        }


        public long result( long accumulator ) {
            return accumulator;
        }

    }


    /**
     * Example of a user-defined aggregate function (UDAF), whose methods are static.
     */
    public static class MyThreeParamsSumFunctionWithFilter1 {

        public static long init() {
            return 0L;
        }


        public static long add( long accumulator, int v1, String v2, String v3 ) {
            if ( v2.equals( v3 ) ) {
                return accumulator + v1;
            }
            return accumulator;
        }


        public static long merge( long accumulator0, long accumulator1 ) {
            return accumulator0 + accumulator1;
        }


        public static long result( long accumulator ) {
            return accumulator;
        }

    }


    /**
     * Example of a user-defined aggregate function (UDAF), whose methods are static. olny for validate to get exact function by Polypheny-DB
     */
    public static class MyThreeParamsSumFunctionWithFilter2 {

        public static long init() {
            return 0L;
        }


        public static long add( long accumulator, int v1, int v2, int v3 ) {
            if ( v3 > 250 ) {
                return accumulator + v1 + v2;
            }
            return accumulator;
        }


        public static long merge( long accumulator0, long accumulator1 ) {
            return accumulator0 + accumulator1;
        }


        public static long result( long accumulator ) {
            return accumulator;
        }

    }


    /**
     * User-defined function.
     */
    public static class SumFunctionBadIAdd {

        public long init() {
            return 0L;
        }


        public long add( short accumulator, int v ) {
            return accumulator + v;
        }

    }


    /**
     * A table function that returns a {@link QueryableTable}.
     */
    public static class SimpleTableFunction {

        public QueryableTable eval( Integer s ) {
            return generateStrings( s );
        }

    }


    /**
     * A table function that returns a {@link QueryableTable}.
     */
    public static class MyTableFunction {

        public QueryableTable eval( String s ) {
            return oneThreePlus( s );
        }

    }


    /**
     * A table function that returns a {@link QueryableTable} via a static method.
     */
    public static class TestStaticTableFunction {

        public static QueryableTable eval( String s ) {
            return oneThreePlus( s );
        }

    }


    /**
     * The real MazeTable may be found in example/function. This is a cut-down version to support a test.
     */
    public static class MazeTable extends AbstractTable implements ScannableTable {

        private final String content;


        public MazeTable( String content ) {
            this.content = content;
        }


        public static ScannableTable generate( int width, int height, int seed ) {
            return new MazeTable( String.format( Locale.ROOT, "generate(w=%d, h=%d, s=%d)", width, height, seed ) );
        }


        public static ScannableTable generate2( @Parameter(name = "WIDTH") int width, @Parameter(name = "HEIGHT") int height, @Parameter(name = "SEED", optional = true) Integer seed ) {
            return new MazeTable( String.format( Locale.ROOT, "generate2(w=%d, h=%d, s=%d)", width, height, seed ) );
        }


        public static ScannableTable generate3( @Parameter(name = "FOO") String foo ) {
            return new MazeTable( String.format( Locale.ROOT, "generate3(foo=%s)", foo ) );
        }


        @Override
        public AlgDataType getRowType( AlgDataTypeFactory typeFactory ) {
            return typeFactory.builder()
                    .add( "S", null, PolyType.VARCHAR, 12 )
                    .build();
        }


        @Override
        public Enumerable<Object[]> scan( DataContext root ) {
            Object[][] rows = { { "abcde" }, { "xyz" }, { content } };
            return Linq4j.asEnumerable( rows );
        }

    }


    /**
     * Schema containing a {@code prod} table with a lot of columns.
     */
    public static class WideSaleSchema {

        @Override
        public String toString() {
            return "WideSaleSchema";
        }


        @SuppressWarnings("unused")
        public final WideProductSale[] prod = {
                new WideProductSale( 100, 10 )
        };

    }


    /**
     * Table with a lot of columns.
     */
    @SuppressWarnings("unused")
    public static class WideProductSale {

        public final int prodId;
        public final double sale0;
        public final double sale1 = 10;
        public final double sale2 = 10;
        public final double sale3 = 10;
        public final double sale4 = 10;
        public final double sale5 = 10;
        public final double sale6 = 10;
        public final double sale7 = 10;
        public final double sale8 = 10;
        public final double sale9 = 10;
        public final double sale10 = 10;
        public final double sale11 = 10;
        public final double sale12 = 10;
        public final double sale13 = 10;
        public final double sale14 = 10;
        public final double sale15 = 10;
        public final double sale16 = 10;
        public final double sale17 = 10;
        public final double sale18 = 10;
        public final double sale19 = 10;
        public final double sale20 = 10;
        public final double sale21 = 10;
        public final double sale22 = 10;
        public final double sale23 = 10;
        public final double sale24 = 10;
        public final double sale25 = 10;
        public final double sale26 = 10;
        public final double sale27 = 10;
        public final double sale28 = 10;
        public final double sale29 = 10;
        public final double sale30 = 10;
        public final double sale31 = 10;
        public final double sale32 = 10;
        public final double sale33 = 10;
        public final double sale34 = 10;
        public final double sale35 = 10;
        public final double sale36 = 10;
        public final double sale37 = 10;
        public final double sale38 = 10;
        public final double sale39 = 10;
        public final double sale40 = 10;
        public final double sale41 = 10;
        public final double sale42 = 10;
        public final double sale43 = 10;
        public final double sale44 = 10;
        public final double sale45 = 10;
        public final double sale46 = 10;
        public final double sale47 = 10;
        public final double sale48 = 10;
        public final double sale49 = 10;
        public final double sale50 = 10;
        public final double sale51 = 10;
        public final double sale52 = 10;
        public final double sale53 = 10;
        public final double sale54 = 10;
        public final double sale55 = 10;
        public final double sale56 = 10;
        public final double sale57 = 10;
        public final double sale58 = 10;
        public final double sale59 = 10;
        public final double sale60 = 10;
        public final double sale61 = 10;
        public final double sale62 = 10;
        public final double sale63 = 10;
        public final double sale64 = 10;
        public final double sale65 = 10;
        public final double sale66 = 10;
        public final double sale67 = 10;
        public final double sale68 = 10;
        public final double sale69 = 10;
        public final double sale70 = 10;
        public final double sale71 = 10;
        public final double sale72 = 10;
        public final double sale73 = 10;
        public final double sale74 = 10;
        public final double sale75 = 10;
        public final double sale76 = 10;
        public final double sale77 = 10;
        public final double sale78 = 10;
        public final double sale79 = 10;
        public final double sale80 = 10;
        public final double sale81 = 10;
        public final double sale82 = 10;
        public final double sale83 = 10;
        public final double sale84 = 10;
        public final double sale85 = 10;
        public final double sale86 = 10;
        public final double sale87 = 10;
        public final double sale88 = 10;
        public final double sale89 = 10;
        public final double sale90 = 10;
        public final double sale91 = 10;
        public final double sale92 = 10;
        public final double sale93 = 10;
        public final double sale94 = 10;
        public final double sale95 = 10;
        public final double sale96 = 10;
        public final double sale97 = 10;
        public final double sale98 = 10;
        public final double sale99 = 10;
        public final double sale100 = 10;
        public final double sale101 = 10;
        public final double sale102 = 10;
        public final double sale103 = 10;
        public final double sale104 = 10;
        public final double sale105 = 10;
        public final double sale106 = 10;
        public final double sale107 = 10;
        public final double sale108 = 10;
        public final double sale109 = 10;
        public final double sale110 = 10;
        public final double sale111 = 10;
        public final double sale112 = 10;
        public final double sale113 = 10;
        public final double sale114 = 10;
        public final double sale115 = 10;
        public final double sale116 = 10;
        public final double sale117 = 10;
        public final double sale118 = 10;
        public final double sale119 = 10;
        public final double sale120 = 10;
        public final double sale121 = 10;
        public final double sale122 = 10;
        public final double sale123 = 10;
        public final double sale124 = 10;
        public final double sale125 = 10;
        public final double sale126 = 10;
        public final double sale127 = 10;
        public final double sale128 = 10;
        public final double sale129 = 10;
        public final double sale130 = 10;
        public final double sale131 = 10;
        public final double sale132 = 10;
        public final double sale133 = 10;
        public final double sale134 = 10;
        public final double sale135 = 10;
        public final double sale136 = 10;
        public final double sale137 = 10;
        public final double sale138 = 10;
        public final double sale139 = 10;
        public final double sale140 = 10;
        public final double sale141 = 10;
        public final double sale142 = 10;
        public final double sale143 = 10;
        public final double sale144 = 10;
        public final double sale145 = 10;
        public final double sale146 = 10;
        public final double sale147 = 10;
        public final double sale148 = 10;
        public final double sale149 = 10;
        public final double sale150 = 10;
        public final double sale151 = 10;
        public final double sale152 = 10;
        public final double sale153 = 10;
        public final double sale154 = 10;
        public final double sale155 = 10;
        public final double sale156 = 10;
        public final double sale157 = 10;
        public final double sale158 = 10;
        public final double sale159 = 10;
        public final double sale160 = 10;
        public final double sale161 = 10;
        public final double sale162 = 10;
        public final double sale163 = 10;
        public final double sale164 = 10;
        public final double sale165 = 10;
        public final double sale166 = 10;
        public final double sale167 = 10;
        public final double sale168 = 10;
        public final double sale169 = 10;
        public final double sale170 = 10;
        public final double sale171 = 10;
        public final double sale172 = 10;
        public final double sale173 = 10;
        public final double sale174 = 10;
        public final double sale175 = 10;
        public final double sale176 = 10;
        public final double sale177 = 10;
        public final double sale178 = 10;
        public final double sale179 = 10;
        public final double sale180 = 10;
        public final double sale181 = 10;
        public final double sale182 = 10;
        public final double sale183 = 10;
        public final double sale184 = 10;
        public final double sale185 = 10;
        public final double sale186 = 10;
        public final double sale187 = 10;
        public final double sale188 = 10;
        public final double sale189 = 10;
        public final double sale190 = 10;
        public final double sale191 = 10;
        public final double sale192 = 10;
        public final double sale193 = 10;
        public final double sale194 = 10;
        public final double sale195 = 10;
        public final double sale196 = 10;
        public final double sale197 = 10;
        public final double sale198 = 10;
        public final double sale199 = 10;


        public WideProductSale( int prodId, double sale ) {
            this.prodId = prodId;
            this.sale0 = sale;
        }

    }

}

