/*
 * 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
 *
 *   https://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.
 *
 *   SPDX-License-Identifier: Apache-2.0
 */

package org.apache.jena.tdb1.base.record;

import static java.lang.String.format;

import java.util.Arrays;

import org.apache.jena.atlas.lib.Bytes ;
import org.apache.jena.tdb1.sys.SystemTDB;

/** A record is pair of key and value.  It may be all key, in which case value is null.
 */

final
public class Record //implements Comparable<Record>
{
    /*
     * Records of fixed size (controlled by the factory).
     */
    public static final Record NO_REC = null ;

    final private byte[] key ;
    final private byte[] value ;

    public Record(byte[] key, byte[] value)
    {
        this.key = key ;
        this.value = value ;
        if ( SystemTDB.Checking )
        {
            if ( value != null && value.length == 0 )
                throw new RecordException("Zero length value") ;
        }
    }

    public byte[] getKey()          { return key ; }
    public byte[] getValue()        { return value ; }

//    public boolean eq(Record record)
//    {
//        return eq(this, record) ;
//    }
//
//    public boolean lt(Record record)
//    {
//        return lt(this, record) ;
//    }
//
//    public boolean le(Record record)
//    {
//        return le(this, record) ;
//    }
//
//    public boolean ge(Record record)
//    {
//        return ge(this, record) ;
//    }
//
//    public boolean gt(Record record)
//    {
//        return gt(this, record) ;
//    }

    public boolean hasSeparateValue() { return value!=null ; }

    @Override
    public int hashCode()
    {
        return Arrays.hashCode(key) ^ Arrays.hashCode(value) ;
    }

    @Override
    public boolean equals(Object other)
    {
        if ( this == other )
            return true;
        if ( !(other instanceof Record r) )
            return false;
        return compareByKeyValue(this, r) == 0;
    }

    @Override
    public String toString()
    {
        if ( value == null )
            return str(key) ;
        return str(key)+":"+str(value) ;
    }

    /** Is the key of record1 == key of record2 */
    public static boolean keyEQ(Record record1, Record record2)
    {
        int x = compareByKey(record1, record2) ;
        return x == 0 ;
    }

    /** Is the key of record1 != key of record2 */
    public static boolean keyNE(Record record1, Record record2)
    {
        int x = compareByKey(record1, record2) ;
        return x != 0 ;
    }

    /** Is the key of record1 {@literal <} key of record2 */
    public static boolean keyLT(Record record1, Record record2)
    {
        int x = compareByKey(record1, record2) ;
        return x < 0 ;
    }

    /** Is the key of record1 {@literal <=} key of record2 */
    public static boolean keyLE(Record record1, Record record2)
    {
        int x = compareByKey(record1, record2) ;
        return x <= 0 ;
    }

    /** Is the key of record1 {@literal >=} key of record2 */
    public static boolean keyGE(Record record1, Record record2)
    {
        int x = compareByKey(record1, record2) ;
        return x >= 0 ;
    }

    /** Is the key of record1 {@literal >} key of record2 */
    public static boolean keyGT(Record record1, Record record2)
    {
        int x = compareByKey(record1, record2) ;
        return x > 0 ;
    }

    /** Is (key, value) of record1 equal to (key,value) of record2 */
    public static boolean equals(Record record1, Record record2)
    {
        int x = compareByKeyValue(record1, record2) ;
        return x == 0 ;
    }



    static public String str(byte[] b)
    {
        StringBuilder str = new StringBuilder() ;
        for ( byte aB : b )
        {
            str.append( format( "%02x", aB ) );
        }
        return str.toString() ;
    }

    public static int compareByKey(Record record1, Record record2)
    {
        checkKeyCompatible(record1, record2) ;
        return Bytes.compare(record1.key, record2.key) ;
    }

    public static int compareByKeyValue(Record record1, Record record2)
    {
        checkCompatible(record1, record2) ;
        int x = Bytes.compare(record1.key, record2.key) ;
        if ( x == 0 )
        {
            if ( record1.value != null )
                x = Bytes.compare(record1.value, record2.value) ;
        }
        return x ;
    }

    static void checkCompatible(Record record1, Record record2)
    {
        if ( ! compatible(record1, record2, true) )
            throw new RecordException(format("Incompatible: %s, %s", record1, record2)) ;
    }

    static void checkKeyCompatible(Record record1, Record record2)
    {
        if ( ! compatible(record1, record2, false) )
            throw new RecordException(format("Incompatible: %s, %s", record1, record2)) ;
    }

    static boolean compatible(Record record1, Record record2, boolean checkValue)
    {
        if ( record1.key.length != record2.key.length )
            return false ;

        if ( checkValue )
        {
            if ( record1.value == null && record2.value == null )
                return true ;
            if ( record1.value == null )
                return false ;
            if ( record2.value == null )
                return false ;
            if ( record1.value.length != record2.value.length )
                return false;
        }
        return true ;
    }

//    private static int compare(byte[] x1, byte[] x2)
//    {
//        for ( int i = 0 ; i < x1.length ; i++ )
//        {
//            byte b1 = x1[i] ;
//            byte b2 = x2[i] ;
//            if ( b1 == b2 )
//                continue ;
//            // Treat as unsigned values in the bytes.
//            return (b1&0xFF) - (b2&0xFF) ;
//        }
//        return  0 ;
//    }

}
