/* Copyright (c) 2011 Danish Maritime Authority.
 
*
 
* 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 dk.dma.ais.store;

import com.google.common.hash.Hashing;
import com.google.common.primitives.Ints;
import dk.dma.ais.packet.AisPacket;

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

/**
 
* This file contains the schema that is being used to store data in AisStore. It also contains various utility methods.
 
*
 
* @author Kasper Nielsen
 
*/

public class AisStoreSchema {

    
/** Identifiers of the columns in use (names are shared across tables). */
    
public enum Column {
        
/** Common name of column holding time block (i.e. no. of 10 minute blocks since the epoch. */
        
COLUMN_TIMEBLOCK("timeblock"),

        
/** Common name of column holding timestamp with millisecond precision. */
        
COLUMN_TIMESTAMP("time"),

        
/** Common name of column holding cellid (geographical area) */
        
COLUMN_CELLID("cellid"),

        
/** Common name of column holding MMSI no. */
        
COLUMN_MMSI("mmsi"),

        
/** Common name of column holding aisdata message digest (to avoid storing duplicates) */
        
COLUMN_AISDATA_DIGEST("digest"),

        
/** We store the actual AIS message in this column. */
        
COLUMN_AISDATA("aisdata");

        
private final String columnName;
        
private Column(String columnName) {
            
this.columnName = columnName;
        
}
        
public String toString() {
            
return this.columnName;
        
}
    
}

    
/** Identifiers of the different tables in use. */
    
public enum Table {
        
/**
         
* This table holds all packets stored in row with the number of 10 minute blocks since the
         
* epoch as the key. Within each 10 minute blocks, packets are store ordered by timestamp (and a message
         
* digest to avoid duplicates).
         
*/

        
TABLE_PACKETS_TIME("packets_time"),

        
/**
         
* This table holds all packets (with a valid message) stored in row with the MMSI number as the key. The columns
         
* are ordered by timestamp and a message digest to avoid duplicates.
         
*/

        
TABLE_PACKETS_MMSI("packets_mmsi"),

        
/** This table contains AIS packets ordered by timeblock and geographic cells of size 1 degree. */
        
TABLE_PACKETS_AREA_CELL1("packets_area_cell1"),

        
/** This table contains AIS packets ordered by timeblock and geographic cells of size 10 degrees. */
        
TABLE_PACKETS_AREA_CELL10("packets_area_cell10"),

        
/** This table holds AIS packets ordered by MMSI number with an unknown position. */
        
TABLE_PACKETS_AREA_UNKNOWN("packets_area_unknown");

        
private final String tableName;
        
private Table(String tableName) {
            
this.tableName = tableName;
        
}
        
public String toString() {
            
return this.tableName;
        
}
    
}

    
/** True if queries should use packets_area_cell10 when relevant; false if always use packets_area_cell1 */
    
public final static boolean TABLE_PACKETS_AREA_CELL10_ENABLED = false;

    
/**
     
* Converts a milliseconds since epoch to a 10-minute blocks since epoch.
     
*
     
* @param timestamp the timestamp to convert
     
* @return the converted value
     
*/

    
public static final int timeBlock(Table table, Instant timestamp) {
        
int timeblock = -1;
        
switch (table) {
            
case TABLE_PACKETS_TIME:
            
case TABLE_PACKETS_AREA_CELL1:
            
case TABLE_PACKETS_AREA_CELL10:
                
timeblock = getTimeBlock(timestamp, Duration.ofMinutes(10));
                
break;
            
case TABLE_PACKETS_MMSI:
            
case TABLE_PACKETS_AREA_UNKNOWN:
                
timeblock =
  
getTimeBlock(timestamp, Duration.ofDays(30));
                
break;
            
default:
                
throw new IllegalArgumentException(table.toString());
        
}
        
return timeblock;
    
}

    
public static Integer[] timeBlocks(Table table, Instant timeStart, Instant timeStop) {
        
int timeBlockMin = timeBlock(table, timeStart);
        
int timeBlockMax = timeBlock(table, timeStop);

        
List<Integer> timeBlocks = new ArrayList<>(timeBlockMax - timeBlockMin + 1);
        
for (int timeBlock = timeBlockMin; timeBlock <= timeBlockMax; timeBlock++)
            
timeBlocks.add(timeBlock);

        
return timeBlocks.toArray(new Integer[timeBlocks.size()]);
    
}



    
private static final int getTimeBlock(Instant timestamp, Duration unit) {
        
return Ints.checkedCast(timestamp.getEpochSecond()/unit.getSeconds());
    
}

    
/**
     
* Calculates a message digest for the given messages (AisPackets).
     
*/

    
public static final byte[] digest(AisPacket packet) {
        
return Hashing.murmur3_128().hashUnencodedChars(packet.getStringMessage()).asBytes();
    
}
}