/*
 * Copyright 2008-2009 LinkedIn, Inc
 * 
 * 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 voldemort.store.readonly.checksum;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;

import junit.framework.TestCase;
import voldemort.store.readonly.checksum.CheckSum.CheckSumType;
import voldemort.utils.ByteUtils;

public class CheckSumTests extends TestCase {

    public void testCheckSum() {
        CheckSum instance = CheckSum.getInstance("blah");
        assertNull(instance);

        CheckSum instance2 = CheckSum.getInstance("md5");
        assertNotNull(instance2);

        CheckSum instance3 = CheckSum.getInstance("crc32");
        assertNotNull(instance3);

        CheckSum instance4 = CheckSum.getInstance("adler32");
        assertNotNull(instance4);

        byte[] emptyBytes = new byte[0];
        instance2.update(emptyBytes);
        assertNotNull(instance2.getCheckSum());
        assertEquals(instance2.getCheckSum().length, CheckSum.checkSumLength(CheckSumType.MD5));

        instance3.update(emptyBytes);
        assertNotNull(instance3.getCheckSum());
        assertEquals(instance3.getCheckSum().length, CheckSum.checkSumLength(CheckSumType.CRC32));

        instance4.update(emptyBytes);
        assertNotNull(instance4.getCheckSum());
        assertEquals(instance4.getCheckSum().length, CheckSum.checkSumLength(CheckSumType.ADLER32));

    }

    /**
     * Calculates the checksum of checksums of individual files.
     * 
     * @param files The files whose bytes to read
     * @param checkSumType
     * @return bytes
     */
    public static byte[] calculateCheckSum(File[] files, CheckSumType checkSumType)
            throws Exception {
        CheckSum checkSumGenerator = CheckSum.getInstance(checkSumType);
        CheckSum fileCheckSumGenerator = CheckSum.getInstance(checkSumType);

        int bufferSize = 64 * 1024;
        byte[] buffer = new byte[bufferSize];

        Arrays.sort(files, new Comparator<File>() {

            public int compare(File fs1, File fs2) {
                // directories before files
                if(fs1.isDirectory())
                    return fs2.isDirectory() ? 0 : -1;
                if(fs2.isDirectory())
                    return fs1.isDirectory() ? 0 : 1;

                String f1 = fs1.getName(), f2 = fs2.getName();

                // All checksum files given priority
                if(f1.endsWith("metadata"))
                    return -1;
                if(f2.endsWith("metadata"))
                    return 1;

                // if both same, lexicographically
                if((f1.endsWith(".index") && f2.endsWith(".index"))
                   || (f1.endsWith(".data") && f2.endsWith(".data"))) {
                    return f1.compareToIgnoreCase(f2);
                }

                if(f1.endsWith(".index")) {
                    return 1;
                } else {
                    return -1;
                }
            }
        });

        for(File file: files) {
            if(file.isFile() && !file.getName().startsWith(".")
               && !file.getName().contains("checkSum.txt") && !file.getName().contains("metadata")) {
                DataInputStream is;
                try {
                    is = new DataInputStream(new FileInputStream(file));
                } catch(FileNotFoundException e) {
                    continue;
                }

                try {
                    while(true) {
                        int read = is.read(buffer);
                        if(read < 0)
                            break;
                        else if(read < bufferSize) {
                            buffer = ByteUtils.copy(buffer, 0, read);
                        }
                        fileCheckSumGenerator.update(buffer);
                    }
                    is.close();
                } catch(IOException e) {
                    break;
                }
                checkSumGenerator.update(fileCheckSumGenerator.getCheckSum());
            }
        }
        return checkSumGenerator.getCheckSum();

    }
}
