package com.basho.riak.client.core.query.timeseries;

import java.util.*;

/**
 * Holds a definition for a Table in Time Series Riak.
 * Immutable once created.
 * @author Alex Moore <amoore at basho dot com>
 * @since 2.0.4
 */
public class TableDefinition
{
    private final String tableName;
    private final LinkedHashMap<String, FullColumnDescription> fullColumnDescriptions = new LinkedHashMap<>();
    private final ArrayList<FullColumnDescription> partitionKeys = new ArrayList<>();
    private final ArrayList<FullColumnDescription> localKeys = new ArrayList<>();

    /**
     * Create a new Table Definition
     * @param tableName a Table Name, required.
     * @param fullColumnDescriptions a list of FullColumnDescription that represents the columns in the table.
     */
    public TableDefinition(String tableName, Iterable<FullColumnDescription> fullColumnDescriptions)
    {
        checkObjectInput(tableName, fullColumnDescriptions);

        this.tableName = tableName;

        for (FullColumnDescription col : fullColumnDescriptions)
        {
            this.fullColumnDescriptions.put(col.getName(), col);

            if (col.isPartitionKeyMember())
            {
                this.partitionKeys.add(col);
            }

            if (col.isLocalKeyMember())
            {
                this.localKeys.add(col);
            }
        }

        Collections.sort(this.localKeys, LocalKeyComparator.INSTANCE);
        Collections.sort(this.partitionKeys, PartitionKeyComparator.INSTANCE);
    }

    /**
     * Get the table name for this definition.
     * @return a String table name.
     */
    public String getTableName()
    {
        return tableName;
    }

    /**
     * Get the ordered list of full column descriptions for this definition.
     * @return a List of the FullColumnDescription in this definition.
     */
    public Collection<FullColumnDescription> getFullColumnDescriptions()
    {
        return fullColumnDescriptions.values();
    }

    /**
     * Look up a FullColumnDescription in this TableDefinition by it's column name.
     * @param columnName the column name to look up.
     * @return the matching FullColumnDescription. Returns <b>null</b> if no column with that name exists.
     */
    public FullColumnDescription getDescriptionByColumnName(String columnName)
    {
        return this.fullColumnDescriptions.get(columnName);
    }

    /**
     * Get the collection of Partition Key FullColumnDescriptions, in order.
     * @return an iterable collection.
     */
    public Collection<FullColumnDescription> getPartitionKeyColumnDescriptions()
    {
        return this.partitionKeys;
    }

    /**
     * Get the collection of Local Key FullColumnDescriptions, in order.
     * @return an iterable collection.
     */
    public Collection<FullColumnDescription> getLocalKeyColumnDescriptions()
    {
        return this.localKeys;
    }

    private void checkObjectInput(String tableName, Iterable<FullColumnDescription> fullColumnDescriptions)
    {
        if (tableName == null || tableName.isEmpty())
        {
            throw new IllegalArgumentException("tableName must not be null or empty.");
        }

        if (fullColumnDescriptions == null || !fullColumnDescriptions.iterator().hasNext())
        {
            throw new IllegalArgumentException("fullColumnDescriptions must not be null or empty.");
        }
    }

    /**
     * Compare LocalKeys for sorting. If there are any non-key columns they will be placed at the end.
     */
    private static class LocalKeyComparator implements Comparator<FullColumnDescription>
    {
        static final LocalKeyComparator INSTANCE = new LocalKeyComparator();
        @Override
        public int compare(FullColumnDescription o1, FullColumnDescription o2)
        {
            if (!o1.isLocalKeyMember() && !o2.isLocalKeyMember())
            {
                return 0;
            }
            else if (!o1.isLocalKeyMember() && o2.isLocalKeyMember())
            {
                return 1;
            }
            else if (o1.isLocalKeyMember() && !o2.isLocalKeyMember())
            {
                return -1;
            }
            else
            {
                return o1.getLocalKeyOrdinal() - o2.getLocalKeyOrdinal();
            }
        }
    }

    /**
     * Compare LocalKeys for sorting. If there are any non-key columns they will be placed at the end.
     */
    private static class PartitionKeyComparator implements Comparator<FullColumnDescription>
    {
        static final LocalKeyComparator INSTANCE = new LocalKeyComparator();
        @Override
        public int compare(FullColumnDescription o1, FullColumnDescription o2)
        {
            if (!o1.isPartitionKeyMember() && !o2.isPartitionKeyMember())
            {
                return 0;
            }
            else if (!o1.isPartitionKeyMember() && o2.isPartitionKeyMember())
            {
                return 1;
            }
            else if (o1.isPartitionKeyMember() && !o2.isPartitionKeyMember())
            {
                return -1;
            }
            else
            {
                return o1.getPartitionKeyOrdinal() - o2.getPartitionKeyOrdinal();
            }
        }
    }
}


@@ -408,35 +408,34 @@
     private 
-final Array
+transient 
 List%3CFullCol
@@ -463,36 +463,16 @@
 tionKeys
- = new ArrayList%3C%3E()
 ;%0A    pr
@@ -481,19 +481,18 @@
 ate 
-final Array
+transient 
 List
@@ -528,28 +528,8 @@
 Keys
- = new ArrayList%3C%3E()
 ;%0A%0A 
@@ -1089,396 +1089,17 @@
 l);%0A
-%0A            if (col.isPartitionKeyMember())%0A            %7B%0A                this.partitionKeys.add(col);%0A            %7D%0A%0A            if (col.isLocalKeyMember())%0A            %7B%0A                this.localKeys.add(col);%0A            %7D%0A        %7D%0A%0A        Collections.sort(this.localKeys, LocalKeyComparator.INSTANCE);%0A        Collections.sort(this.partitionKeys, PartitionKeyComparator.INSTANCE);
+        %7D
 %0A   
@@ -2214,252 +2214,1084 @@
     
-return this.partitionKeys;%0A    %7D%0A%0A    /**%0A     * Get the collection of Local Key FullColumnDescriptions, in order.%0A     * @return an iterable collection.%0A     */%0A    public Collection%3CFullColumnDescription%3E getLocalKeyColumnDescriptions()%0A    %7B
+if (partitionKeys == null)%0A        %7B%0A            partitionKeys = new LinkedList%3C%3E();%0A%0A            for (FullColumnDescription col : fullColumnDescriptions.values())%0A            %7B%0A                if (col.isPartitionKeyMember())%0A                %7B%0A                    this.partitionKeys.add(col);%0A                %7D%0A            %7D%0A%0A            Collections.sort(this.partitionKeys, PartitionKeyComparator.INSTANCE);%0A        %7D%0A%0A        return this.partitionKeys;%0A    %7D%0A%0A    /**%0A     * Get the collection of Local Key FullColumnDescriptions, in order.%0A     * @return an iterable collection.%0A     */%0A    public Collection%3CFullColumnDescription%3E getLocalKeyColumnDescriptions()%0A    %7B%0A        if (localKeys == null)%0A        %7B%0A            localKeys = new LinkedList%3C%3E();%0A%0A            for (FullColumnDescription col : fullColumnDescriptions.values())%0A            %7B%0A                if (col.isLocalKeyMember())%0A                %7B%0A                    this.localKeys.add(col);%0A                %7D%0A            %7D%0A%0A            Collections.sort(this.localKeys, LocalKeyComparator.INSTANCE);%0A        %7D%0A
 %0A   

Make partitionKeys and localKeys to be calculated on demand