package org.bdware.sc.index;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

public class TimeSerialIndex {
    private static final Logger LOGGER = LogManager.getLogger(TimeSerialIndex.class);
    //    static int chunkSize = 10240;
//    public String fileDir;
//    File currentFile;
    RandomAccessFile file;
    long fileSize = 0;

    public TimeSerialIndex(String fileName) {
        try {
            File f = new File(fileName);
            File parent = f.getParentFile();
            if (!parent.exists()) {
                LOGGER.trace("create directory " + parent.getAbsolutePath() + ": " + parent.mkdirs());
            }
            if (!f.exists()) {
                LOGGER.trace("create file " + f.getAbsolutePath() + ": " + f.createNewFile());
            }
            file = new RandomAccessFile(f, "rw");
            fileSize = file.length() / 16L;
            this.file.seek(fileSize * 16L);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void finalize() {
        try {
            file.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public synchronized long index(long hash) {
        long ret = 0;
        try {
            fileSize++;
            ret = System.currentTimeMillis();
            file.writeLong(ret);
            file.writeLong(hash);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return ret;
    }

    public synchronized List<Long> request(long offset, int len) {
        List<Long> ret = new ArrayList<>();
        if (offset < 0) offset = 0;

        if (offset < fileSize) {
            long pos = 0;
            try {
                pos = file.getFilePointer();
                file.seek(2L * offset * 8L);
                for (; offset < fileSize && len > 0; len--) {
                    file.readLong();
                    ret.add(file.readLong());
                    offset++;
                }
                file.seek(pos);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    file.seek(pos);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return ret;
        } else return new ArrayList<>();
    }

    public static class IndexEntry {
        public long key, value;
    }

    public synchronized List<IndexEntry> requestIndexEntry(long offset, int len) {
        List<IndexEntry> ret = new ArrayList<>();
        if (offset < 0) offset = 0;
        if (offset < fileSize) {
            long pos = 0;
            try {
                pos = file.getFilePointer();
                file.seek(2L * offset * 8L);
                for (; offset < fileSize && len > 0; len--) {
                    IndexEntry entry = new IndexEntry();
                    entry.key = file.readLong();
                    entry.value = file.readLong();
                    ret.add(entry);
                    offset++;
                }
                file.seek(pos);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    file.seek(pos);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return ret;
        } else return new ArrayList<>();
    }

    private long getIndex(long offset) {
        if (offset < fileSize) {
            try {
                file.seek(2L * offset * 8L);
                return file.readLong();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        throw new IllegalStateException("Array out of index exception");
    }

    public long size() {
        return fileSize;
    }

    public synchronized List<Long> requestLast(int count) {
        long offset = fileSize - count;
        return request(offset, count);
    }

    public synchronized long findNearest(long timeStamp) {
        try {
            long pos = file.getFilePointer();
            long ret = binarySearch(0L, fileSize, timeStamp);
            file.seek(pos);
            return ret;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return 0;
    }

    private long binarySearch(long start, long end, long timeStamp) {
        if (end <= 0) return end;
        if (start >= end - 1) {
            long key = getIndex(end - 1);
            if (key >= timeStamp) {
                return end - 1;
            } else return end;
        }
        long mid = (start + end) / 2;
        long key = getIndex(mid);
        if (key >= timeStamp) {
            return binarySearch(start, mid, timeStamp);
        } else {
            return binarySearch(mid + 1, end, timeStamp);
        }
    }

    public synchronized void manullyIndex(long date, long key) {
        try {
            fileSize++;
            file.writeLong(date);
            file.writeLong(key);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
