package com.ctriposs.tsdb.iterator;
import java.io.IOException;
import java.util.Collections;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentSkipListSet;
import com.ctriposs.tsdb.InternalKey;
import com.ctriposs.tsdb.common.IFileIterator;
import com.ctriposs.tsdb.manage.FileManager;
import com.ctriposs.tsdb.storage.CodeItem;
import com.ctriposs.tsdb.util.ByteUtil;
public class MergeFileSeekIterator{
private FileManager fileManager;
private ConcurrentSkipListSet<IFileIterator<InternalKey, byte[]>> itSet;
private Direction direction;
private Entry<InternalKey, byte[]> curEntry;
private IFileIterator<InternalKey, byte[]> curIt;
private InternalKey seekKey = null;
public MergeFileSeekIterator(FileManager fileManager, IFileIterator<InternalKey, byte[]>... its) {
this.fileManager = fileManager;
this.itSet = new ConcurrentSkipListSet<IFileIterator<InternalKey,byte[]>>(fileManager.getFileIteratorComparator());
addIterator(its);
this.direction = Direction.forward;
this.curEntry = null;
this.curIt = null;
}
public void addIterator(IFileIterator<InternalKey, byte[]>... its) {
Collections.addAll(itSet, its);
}
public boolean hasNext() {
boolean result = false;
if(itSet != null&&curIt != null) {
if(curIt.hasNext()){
return true;
}else{
try {
if(curIt.hasNextCode()){
CodeItem codeItem = curIt.nextCode();
if(codeItem.getCode()==curEntry.getKey().getCode()){
curIt.nextCode();
}
curIt.seekToCurrent(true);
}else{
curIt.next();
}
for (IFileIterator<InternalKey, byte[]> it : itSet) {
if(it.hasNext()) {
result = true;
break;
}
}
if(result){
findSmallest();
if(curIt!=null&&curIt.hasNext()){
if(seekKey != null){
if(seekKey.getCode()!=curIt.key().getCode()){
return false;
}
}
result = true;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return result;
}
public boolean hasPrev(){
boolean result = false;
if(itSet != null&&curIt != null) {
if(curIt.hasPrev()){
return true;
}else{
try {
if(curIt.hasPrevCode()){
CodeItem codeItem = curIt.prevCode();
if(codeItem.getCode()==curEntry.getKey().getCode()){
curIt.prevCode();
}
curIt.seekToCurrent(false);
}else{
curIt.prev();
}
for (IFileIterator<InternalKey, byte[]> it : itSet) {
if(it.hasPrev()) {
result = true;
break;
}
}
if(result){
findLargest();
if(curIt!=null&&curIt.hasPrev()){
if(seekKey != null){
if(seekKey.getCode()!=curIt.key().getCode()){
return false;
}
}
result = true;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return result;
}
public Entry<InternalKey, byte[]> next() throws IOException {
if (direction != Direction.forward) {
for (IFileIterator<InternalKey, byte[]> it : itSet) {
if (it != curIt) {
try {
it.seek(curEntry.getKey().getCode(),curEntry.getKey().getTime());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
direction = Direction.forward;
}
curEntry = curIt.next();
findSmallest();
return curEntry;
}
public Entry<InternalKey, byte[]> prev() throws IOException {
if(direction != Direction.reverse){
for(IFileIterator<InternalKey, byte[]> it:itSet){
try {
if(curEntry != null){
it.seek(curEntry.getKey().getCode(),curEntry.getKey().getTime());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
findLargest();
direction = Direction.reverse;
}
curEntry = curIt.prev();
findLargest();
return curEntry;
}
public void seek(String table, String column, long time) throws IOException {
int code = ByteUtil.ToInt(fileManager.getCode(table),fileManager.getCode(column));
seek(code, time);
}
public void seek(int code, long time) throws IOException {
seekKey = new InternalKey(code,time);
if(null != itSet){
for(IFileIterator<InternalKey, byte[]> it:itSet){
it.seek(seekKey.getCode(),time);
}
findSmallest();
direction = Direction.forward;
}
}
public void seekToFirst() throws IOException {
if(null != itSet) {
for(IFileIterator<InternalKey, byte[]> it:itSet){
if(it.hasNextCode()){
CodeItem item = it.nextCode();
if(item != null){
it.seekToCurrent(true);
}
}
}
findSmallest();
direction = Direction.forward;
}
}
public void seekToLast(int code) throws IOException {
if(null != itSet){
for(IFileIterator<InternalKey, byte[]> it:itSet){
it.seek(code,Long.MAX_VALUE);
}
findSmallest();
direction = Direction.forward;
}
}
private void findSmallest() throws IOException{
if(null != itSet){
IFileIterator<InternalKey, byte[]> smallest = null;
for(IFileIterator<InternalKey, byte[]> it : itSet) {
if(it.valid()){
if(smallest == null){
smallest = it;
}else if(fileManager.compare(smallest.key(), it.key())>0){
smallest = it;
}else if(fileManager.compare(smallest.key(), it.key())==0){
while(it.hasNext()){
it.next();
int diff = fileManager.compare(smallest.key(),it.key());
if(0==diff){
continue;
}else{
break;
}
}
}
}else{
if(it.hasNextCode()){
it.nextCode();
it.seekToCurrent(true);
}else{
it.next();
}
if(it.valid()){
if(smallest == null){
smallest = it;
}else if(fileManager.compare(smallest.key(), it.key())>0){
smallest = it;
}else if(fileManager.compare(smallest.key(), it.key())==0){
while(it.hasNext()){
it.next();
int diff = fileManager.compare(smallest.key(),it.key());
if(0==diff){
continue;
}else{
break;
}
}
}
}
}
}
if(smallest != null){
curIt = smallest;
}
}
}
private void findLargest() throws IOException{
if(null != itSet){
IFileIterator<InternalKey, byte[]> largest = null;
for(IFileIterator<InternalKey, byte[]> it:itSet){
if(it.valid()){
if(largest == null){
largest = it;
}else if(fileManager.compare(largest.key(), it.key())<0){
largest = it;
}else if(fileManager.compare(largest.key(), it.key())==0){
while(it.hasPrev()){
it.prev();
int diff = fileManager.compare(largest.key(),it.key());
if(0==diff){
continue;
}else{
break;
}
}
}
}else{
if(it.hasPrevCode()){
it.prevCode();
it.seekToCurrent(false);
}else{
it.prev();
}
if(it.valid()){
if(largest == null){
largest = it;
}else if(fileManager.compare(largest.key(), it.key())<0){
largest = it;
}else if(fileManager.compare(largest.key(), it.key())==0){
while(it.hasPrev()){
it.prev();
int diff = fileManager.compare(largest.key(),it.key());
if(0==diff){
continue;
}else{
break;
}
}
}
}
}
}
if(largest != null){
curIt = largest;
}
}
}
public String table() {
if(curEntry != null){
return fileManager.getName(curEntry.getKey().getTableCode());
}
return null;
}
public String column() {
if(curEntry != null){
return fileManager.getName(curEntry.getKey().getColumnCode());
}
return null;
}
public long time() {
if(curEntry != null){
return curEntry.getKey().getTime();
}
return 0;
}
public byte[] value() throws IOException {
if(curEntry != null){
return curEntry.getValue();
}
return null;
}
public boolean valid() {
if(curEntry==null){
return false;
}else{
return true;
}
}
public void close() throws IOException{
if(null != itSet){
for(IFileIterator<InternalKey, byte[]> it:itSet){
it.close();
}
}
}
public ConcurrentSkipListSet<IFileIterator<InternalKey, byte[]>> getAllFileIterators(){
return itSet;
}
public InternalKey key() {
if(curEntry != null){
return curEntry.getKey();
}
return null;
}
public void remove() {
throw new UnsupportedOperationException("unsupport remove operation!");
}
enum Direction{
forward,reverse
}
}