package simpledb.test;

import simpledb.*;
import java.util.*;
import java.io.*;

/**
 * Tests running concurrent transactions.
 * You do not need to pass this test until lab3.
 */
public class TransactionTest extends Test {
    private int NTHREADS = 50;

    public boolean runTest(String args[]) {
	NTHREADS = Integer.parseInt(args[1]);

	Thread[] list = new Thread[NTHREADS];
	for(int i=0; i<NTHREADS; i++) {
	    list[i] = new Thread(new XactionTester(i));
	    list[i].start();
	}

	boolean waiting = true;
	while(waiting) {
	    waiting = false;
	    for(int i=0; i<NTHREADS; i++)
		if(list[i].isAlive())
		    waiting = true;
	}

	try {
	    dump(file[0][0]);
	} catch (TransactionAbortedException te) {
	    te.printStackTrace();
	    return false;
	} catch (IOException e) {
	    e.printStackTrace();
	    return false;
	}
	return true;
    }

    class XactionTester implements Runnable {
	int id;
	final Random random = new Random();

	public XactionTester(int id) {
	    this.id = id;
	}

	public void run() {

	    while(true) {
		TransactionId tid = new TransactionId();				
		SeqScan ss = new SeqScan(tid, file[0][0].id());

		Type typeAr[] = new Type[1];
		typeAr[0] = Type.INT_TYPE;
		TupleDesc td = new TupleDesc(typeAr);

		Query q = new Query(ss, tid);

		try {
		    // read the value out of the table
		    try {
			q.start();
			Tuple tup = q.getNext();
			
			IntField intf = (IntField) tup.getField(0);
			int i = Integer.parseInt(intf.toString());

			// create a TupleIterator so that Insert can insert this new value
			// into the table.
			Tuple t = new Tuple(td);
			t.setField(0, new IntField(new Integer(i+1)));

			// sleep thread
			try {
			    Thread.currentThread().sleep(NTHREADS + random.nextInt(NTHREADS));
			} catch (InterruptedException e) {
			}

			q.close();

			// delete old values (i.e., just one row) from table
			Delete delOp = new Delete(tid, ss);
			
			q = new Query(delOp, tid);
			
			q.start();
			q.getNext();
			q.close();

			// set up a Set with a tuple that is one higher than the old one.
			HashSet hs = new HashSet();
			hs.add(t);
			TupleIterator ti = new TupleIterator(td, hs);

			// sleep thread
			try {
			    Thread.currentThread().sleep(NTHREADS + random.nextInt(NTHREADS));
			} catch (InterruptedException e) {
			}

			// insert this new tuple into the table
			Insert insOp = new Insert(tid, ti, file[0][0].id());
			q = new Query(insOp, tid);
			q.start();
			q.getNext();
			q.close();

			break;

		    } catch (DbException dbe) {
			dbe.printStackTrace();
			break;

		    } catch (TransactionAbortedException te) {
			// System.out.println("thread " + id + " killed");
			// give someone else a chance
			try {
			    Thread.currentThread().sleep(NTHREADS * 100 + random.nextInt(NTHREADS * 20));
			} catch (InterruptedException e) {
			}

			continue;
		    }
		} catch (IOException e) {
		    e.printStackTrace();
		    break;
		}
	    }
	    // System.out.println("thread " + id + " done");
	}
    }
}
