/*
 * Copyright (c) 2003-2004 imagero Andrei Kouznetsov. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  o Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  o Neither the name of imagero Andrei Kouznetsov nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.imagero.io.uio;

import com.imagero.uio.RandomAccess;
import com.imagero.uio.RandomAccessBuffer;
import com.imagero.uio.RandomAccessBufferRO;
import com.imagero.uio.RandomAccessFactory;
import com.imagero.uio.RandomAccessFileWrapper;
import com.imagero.uio.RandomAccessRO;
import com.imagero.uio.buffer.MutableRAFBufferManager;
import com.imagero.uio.buffer.arrays.IntArrayBufferManager;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author Andrei Kouznetsov
 *         Date: 21.07.2004
 *         Time: 00:03:34
 */
public class IOTest {

    static int repeat = 100;
    static int arraySize = 100 * 1024;
    static long fileSize = 1000 * 1024;

    public static void main(String[] args) throws IOException {

        if(args.length == 0) {
            System.err.println("usage: java IOTest filename [fileSize] [arraySize] [repeat] " +
                    "\n fileSize and arraySize in KB");
        }

        File f = new File(args[0]);
        if(args.length > 1) {
            fileSize = Long.parseLong(args[1]) * 1024;
        }
        if(args.length > 2) {
            arraySize = Integer.parseInt(args[2]) * 1024;
        }
        if(args.length > 3) {
            repeat = Integer.parseInt(args[3]);
        }

        RandomAccessFile raf = new RandomAccessFile(f, "rw");
        RandomAccess ra = new RandomAccessFileWrapper(raf, RandomAccess.BIG_ENDIAN);
        raf.setLength(fileSize);

        MutableRAFBufferManager manager = new MutableRAFBufferManager(raf, 0, (int) raf.length());
        RandomAccessBuffer rads = new RandomAccessBuffer(manager, RandomAccessFactory.BIG_ENDIAN);

        FileChannel channel = raf.getChannel();

        ByteBuffer byteBuffer = ByteBuffer.allocate(arraySize * 4);

        System.out.println("running IOTest.java:");
        System.out.println("filename: " + f.getName() + "\nfile size: " + fileSize + "\narray size: " + arraySize + "\nrepeat: " + repeat);

        int[] intArray = new int[arraySize];
        for(int i = 0; i < intArray.length; i++) {
            intArray[i] = 32;
        }

        byte[] byteArray = new byte[intArray.length * 4];
        for(int i = 0; i < byteArray.length; i++) {
            byteArray[i] = 32;
        }

        /* ******************************************************* */

        System.out.println("\ntest performance of java.io and java.nio (write byte arrays)\n");

        writeByteArray(raf, byteArray);
        writeByteArrayNio(channel, byteBuffer);

        /* ******************************************************* */

        System.out.println("\ntest performance of writing int arrays\n");

        writeInts(raf, intArray);
        writeComby(byteArray, raf, intArray);

        writeIntsUnbuffered(ra, intArray);
        writeIntArray(ra, intArray);

        writeIntArray(rads, intArray, manager);
        writeIntsBuffered(rads, intArray, manager);

        writeNio(byteBuffer, intArray, channel);

        writeComby(byteBuffer, channel, intArray);
    }

    private static void writeIntsBuffered(RandomAccessBuffer rads, int[] intArray, MutableRAFBufferManager manager) throws IOException {
        System.out.print("RandomAccess#writeInt() - (buffered): ");
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100; i++) {
            rads.seek(0);
            for(int j = 0; j < intArray.length; j++) {
                rads.writeInt(intArray[j]);
            }
        }
        manager.flush();
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    private static void writeIntArray(RandomAccessBuffer rads, int[] intArray, MutableRAFBufferManager manager) throws IOException {
        System.out.print("RandomAccess#write(int[]) - (buffered): ");
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100; i++) {
            rads.seek(0);
            rads.write(intArray);
        }
        manager.flush();
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    private static void writeNio(ByteBuffer byteBuffer, int[] intArray, FileChannel channel) throws IOException {
        System.out.print("Using java.nio: ");
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100; i++) {
            byteBuffer.clear();
            for(int j = 0; j < intArray.length; j++) {
                byteBuffer.putInt(intArray[j]);
            }
            channel.write(byteBuffer);
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    private static void writeComby(ByteBuffer byteBuffer, FileChannel channel, int [] intArray) throws IOException {
        RandomAccessRO ro = new RandomAccessBufferRO(new IntArrayBufferManager(intArray, intArray.length), RandomAccess.BIG_ENDIAN);
        ro.setByteOrder(RandomAccess.BIG_ENDIAN);
        System.out.print("Combine Unified I/O with java.nio: ");
        long startTime = System.currentTimeMillis();

        byte [] bb = byteBuffer.array();

        for(int i = 0; i < 100; i++) {
            channel.position(0);
            byteBuffer.position(0);
            ro.seek(0);
            ro.readFully(bb);
            channel.write(byteBuffer);
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    private static void writeIntArray(RandomAccess ra, int[] intArray) throws IOException {
        System.out.print("RandomAccessFileWrapper#write(int[]) (unbuffered): ");
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100; i++) {
            ra.seek(0);
            ra.write(intArray);
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    private static void writeIntsUnbuffered(RandomAccess ra, int[] intArray) throws IOException {
        //RandomAccessFileWrapper#writeInt()
        System.out.print("RandomAccessFileWrapper#writeInt() (unbuffered):");
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100; i++) {
            ra.seek(0);
            for(int j = 0; j < intArray.length; j++) {
                ra.writeInt(intArray[j]);
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    private static void writeInts(RandomAccessFile raf, int[] intArray) throws IOException {
        //RandomAccessFile#writeInt()
        System.out.print("RandomAccessFile#writeInt(): ");
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100; i++) {
            raf.seek(0);
            for(int j = 0; j < intArray.length; j++) {
                raf.writeInt(intArray[j]);
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    private static void writeComby(byte [] bb, RandomAccessFile raf, int[] intArray) throws IOException {
        RandomAccessRO ro = new RandomAccessBufferRO(new IntArrayBufferManager(intArray, intArray.length), RandomAccess.BIG_ENDIAN);
        ro.setByteOrder(RandomAccess.BIG_ENDIAN);
        System.out.print("Combine Unified I/O with java.io: ");

        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100; i++) {
            raf.seek(0);
            ro.seek(0);
            ro.readFully(bb);
            raf.write(bb);
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    private static void writeByteArrayNio(FileChannel channel, ByteBuffer byteBuffer) throws IOException {
        System.out.print("Using java.nio: ");
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100; i++) {
            channel.position(0);
            byteBuffer.position(0);
            channel.write(byteBuffer);
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    private static void writeByteArray(RandomAccessFile raf, byte[] byteArray) throws IOException {
        //RandomAccessFile bulk write byte array
        System.out.print("RandomAccessFile bulk write byte array: ");
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < 100; i++) {
            raf.seek(0);
            raf.write(byteArray);
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }
}
