/*
 * 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
 *
 *     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.apache.cassandra.io.compress;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.junit.Test;

import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.utils.ByteBufferUtil;

import static org.junit.Assert.assertEquals;

public class CompressedSequentialWriterReopenTest extends CQLTester
{
    @Test
    public void badCompressor1() throws IOException
    {
        BadCompressor bad = new BadCompressor();
        byte [] test = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
        byte [] out = new byte[10];
        bad.uncompress(test, 0, 20, out, 0);
        for (int i = 0; i < 10; i++)
            assertEquals(out[i], (byte)i);
    }

    @Test
    public void badCompressor2() throws IOException
    {
        BadCompressor bad = new BadCompressor();
        ByteBuffer input = ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19});
        ByteBuffer output = ByteBuffer.allocate(40);
        bad.compress(input, output);
        for (int i = 0; i < 40; i++)
            assertEquals(i % 20, output.get(i));
    }

    @Test
    public void badCompressor3() throws IOException
    {
        BadCompressor bad = new BadCompressor();
        ByteBuffer input = ByteBuffer.wrap(new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19});
        ByteBuffer output = ByteBuffer.allocate(10);
        bad.uncompress(input, output);
        for (int i = 0; i < 10; i++)
            assertEquals(i, output.get(i));
    }

    @Test
    public void compressionEnabled() throws Throwable
    {
        createTable("create table %s (id int primary key, t blob) with compression = {'class':'org.apache.cassandra.io.compress.CompressedSequentialWriterReopenTest$BadCompressor'}");
        byte [] blob = new byte[1000];
        (new Random()).nextBytes(blob);
        getCurrentColumnFamilyStore().disableAutoCompaction();
        for (int i = 0; i < 10000; i++)
        {
            execute("insert into %s (id, t) values (?, ?)", i, ByteBuffer.wrap(blob));
        }
        getCurrentColumnFamilyStore().forceBlockingFlush();
        for (int i = 0; i < 10000; i++)
        {
            execute("insert into %s (id, t) values (?, ?)", i, ByteBuffer.wrap(blob));
        }
        getCurrentColumnFamilyStore().forceBlockingFlush();
        DatabaseDescriptor.setSSTablePreemptiveOpenIntervalInMB(1);
        getCurrentColumnFamilyStore().forceMajorCompaction();
    }

    public static class BadCompressor implements ICompressor
    {
        public static ICompressor create(Map<String, String> options)
        {
            return new BadCompressor();
        }

        @Override
        public int initialCompressedBufferLength(int chunkLength)
        {
            return chunkLength * 2;
        }

        @Override
        public int uncompress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset) throws IOException
        {
            System.arraycopy(input, inputOffset, output, outputOffset, inputLength / 2);
            return inputLength / 2;
        }

        @Override
        public void compress(ByteBuffer input, ByteBuffer output) throws IOException
        {
            int len = input.remaining();
            byte [] arr = ByteBufferUtil.getArray(input);
            output.put(arr);
            output.put(arr);
            input.position(len);
        }

        @Override
        public void uncompress(ByteBuffer input, ByteBuffer output) throws IOException
        {
            byte [] arr = ByteBufferUtil.getArray(input);
            output.put(arr, 0, arr.length / 2);
            input.position(arr.length);
        }

        @Override
        public BufferType preferredBufferType()
        {
            return BufferType.ON_HEAP;
        }

        @Override
        public boolean supports(BufferType bufferType)
        {
            return true;
        }

        @Override
        public Set<String> supportedOptions()
        {
            return null;
        }
    }
}
