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) {
		Transaction tr = new Transaction();
		tr.start();
		SeqScan ss1 = new SeqScan(tr.tid(), file[0][0].id());
		SeqScan ss2 = new SeqScan(tr.tid(), file[0][0].id());

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

		Query q1, q2, q3;

		try {
		    try {
			// read the value out of the table
			q1 = new Query(ss1, tr.tid());
			q1.start();
			Tuple tup = q1.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) {
			}
			
			q1.close();
			
			// delete old values (i.e., just one row) from table
			Delete delOp = new Delete(tr.tid(), ss2);
			
			q2 = new Query(delOp, tr.tid());
			
			q2.start();
			q2.getNext();
			q2.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(tr.tid(), ti, file[0][0].id());
			q3 = new Query(insOp, tr.tid());
			q3.start();
			q3.getNext();
			q3.close();

			tr.commit();
			
			break;
			
		    } catch (DbException dbe) {
			tr.transactionComplete(true);
			dbe.printStackTrace();
			break;

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

			continue;
		    } catch (NoSuchElementException e) {
			System.out.println("seems not possible in this case");
		       
		    }
		} catch (IOException e) {
		    System.out.println("IOException trying to commit/abort;  quitting.");
		    e.printStackTrace();
		    System.exit(1);

		}
	    }
	    //System.out.println("thread " + id + " done");
	}
    }
}
