package de.ugoe.cs.swe.exercises.exercise;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import de.ugoe.cs.swe.exercises.lecture.Lecture;
import de.ugoe.cs.swe.exercises.misc.Model;
import de.ugoe.cs.swe.exercises.settings.Settings;

public class Exercise extends Model {

	private String title;
	private String exerciseText;
	private String solution;
	private int revisionnumber;
	private int complexity;

	@Override
	protected String getTableName() {
		return "exercise";
	}

	public Exercise() {
		super();
	}

	public Exercise(String title, String exerciseText, String solution,
			int revisionnumber, int complexity) {
		super();
		this.setTitle(title);
		this.setExerciseText(exerciseText);
		this.setSolution(solution);
		this.setRevisionnumber(revisionnumber);
		this.setComplexity(complexity);
	}

	public Exercise(String title, String exerciseText, String solution,
			int complexity) {
		super();
		this.setTitle(title);
		this.setExerciseText(exerciseText);
		this.setSolution(solution);
		this.setRevisionnumber(0);
		this.setComplexity(complexity);
	}

	public Exercise(int id, String title, String exerciseText, String solution,
			int revisionnumber, int complexity) {

		super(id);
		this.setTitle(title);
		this.setExerciseText(exerciseText);
		this.setSolution(solution);
		this.setRevisionnumber(revisionnumber);
		this.setComplexity(complexity);
	}

	public boolean equals(Object other) {
		if (other instanceof Exercise)
			return ((Exercise) other).getId() == getId();
		return super.equals(other);
	}

	private String getRevisionFileName() {
		return ExerciseSVN.SVNpathRev + "/exercises/" + this.getId() + ".tex";
	}

	private String getRevisionSolutionFileName() {
		return ExerciseSVN.SVNpathRev + "/exercises/" + this.getId()
				+ "solution.tex";
	}

	private String getFileName() {

		return ExerciseSVN.SVNpath + "/exercises/" + this.getId() + ".tex";
	}

	private String getSolutionFileName() {
		return ExerciseSVN.SVNpath + "/exercises/" + this.getId()
				+ "solution.tex";
	}

	public void setTitle(String title) {
		this.title = title;

	}

	public String getTitle() {
		return title;
	}

	public void setSolution(String solution) {
		this.solution = solution;
	}

	public String getSolution() {
		return readFromDisk(true);
	}

	public String getSolution(int revision) {
		return readFromDisk(revision, true);
	}

	public void setRevisionnumber(int revisionnumber) {
		this.revisionnumber = revisionnumber;
	}

	public int getRevisionnumber() {
		return revisionnumber;
	}

	public void setExerciseText(String exerciseText) {
		this.exerciseText = exerciseText;
	}

	public String getExerciseText() {

		return readFromDisk(false);

	}

	public String getExerciseText(int revision) {

		return readFromDisk(revision, false);

	}

	public void setComplexity(int complexity) {
		this.complexity = complexity;
	}

	public int getComplexity() {
		return complexity;
	}

	public static Exercise getItem(int id) {
		try {
			return fromStatement(
					prepareStatement("SELECT * FROM exercise WHERE id =" + id))
					.get(0);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	protected void update() throws SQLException {

		update(false);

	}

	protected void update(boolean updateWithoutSaving) throws SQLException {
		if (!updateWithoutSaving)
			this.saveToDisk();
		PreparedStatement statement;
		statement = prepareStatement("UPDATE exercise SET title=?,  revisionnumber=?, complexity=? WHERE id=?");
		statement.setInt(4, this.getId());
		executeSaveStatement(statement);

	}

	protected void executeSaveStatement(PreparedStatement statement)
			throws SQLException {
		statement.setString(1, this.getTitle());
		statement.setInt(2, this.getRevisionnumber());
		statement.setInt(3, this.getComplexity());
		statement.executeUpdate();

	}

	@Override
	protected PreparedStatement insert() throws SQLException {
		PreparedStatement statement;
		statement = prepareStatement("INSERT INTO exercise (title, revisionnumber, complexity) VALUES (?,?,?)");
		executeSaveStatement(statement);

		return statement;
	}

	public static ArrayList<Exercise> all() {
		try {
			return fromStatement(prepareStatement("SELECT * FROM exercise"));
		} catch (SQLException e) {
			System.err.println("Got an exception in Exercise.all()! ");
			System.err.println(e.getMessage());
		}
		return null;
	}

	public static ArrayList<Exercise> allForLecture(int lectureId) {
		try {
			return fromStatement(prepareStatement("SELECT * FROM exercise, lecture_exercise WHERE lecture_exercise.exercise=exercise.id AND lecture_exercise.lecture="
					+ String.valueOf(lectureId) + ";"));
		} catch (SQLException e) {
			System.err.println("Got an exception in Exercise.all()! ");
			System.err.println(e.getMessage());
		}
		return null;
	}

	public static ArrayList<Exercise> fromStatement(PreparedStatement statement)
			throws SQLException {
		ResultSet resultset = statement.executeQuery();
		ArrayList<Exercise> exercises = new ArrayList<Exercise>();
		while (resultset.next()) {
			exercises.add(new Exercise(resultset.getInt("id"), resultset
					.getString("title"), ExerciseSVN.getTextFromSVN(resultset
					.getInt("revisionnumber")), ExerciseSVN
					.getTextFromSVN(resultset.getInt("revisionnumber")),
					resultset.getInt("revisionnumber"), resultset
							.getInt("complexity")));
		}
		return exercises;
	}

	public ArrayList<Lecture> getLectures() {
		try {
			PreparedStatement statement = prepareStatement("SELECT DISTINCT lecture.* FROM exercise, lecture, lecture_exercise WHERE exercise.id = ? AND exercise.id = lecture_exercise.exercise AND lecture.id = lecture_exercise.lecture ORDER BY id");
			statement.setInt(1, this.getId());
			return Lecture.fromStatement(statement);
		} catch (SQLException e) {
			System.err
					.println("Got an exception in ExerciseSheet.getLectureEvents()! ");
			System.err.println(e.getMessage());
		}
		return null;
	}

	public void addLecture(Lecture lecture) {
		if (this.getId() == Model.UNDEFINED)
			throw new Model.DoesNotExistException(
					"Exercise does not exist in the database, yet");
		if (lecture.getId() == Model.UNDEFINED)
			throw new Model.DoesNotExistException(
					"Lecture does not exist in the database, yet");

		PreparedStatement statement;
		try {
			statement = prepareStatement("INSERT INTO lecture_exercise (exercise, lecture) VALUES (?,?)");
			statement.setInt(1, this.getId());
			statement.setInt(2, lecture.getId());
			statement.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public void removeLecture(Lecture lecture) {
		if (this.getId() == Model.UNDEFINED)
			throw new Model.DoesNotExistException(
					"Exercise does not exist in the database, yet");
		if (lecture.getId() == Model.UNDEFINED)
			throw new Model.DoesNotExistException(
					"Lecture does not exist in the database, yet");

		PreparedStatement statement;
		try {
			statement = prepareStatement("DELETE FROM lecture_exercise WHERE exercise=? AND lecture=?");
			statement.setInt(1, this.getId());
			statement.setInt(2, lecture.getId());
			statement.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public void removeExercise(Exercise exercise) {

		PreparedStatement statement;
		try {
			statement = prepareStatement("DELETE FROM exercise WHERE id=?");
			statement.setInt(1, this.getId());
			statement.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void _delete() {

		removeExercise(this);
		DefaultSVNOptions options = SVNWCUtil.createDefaultOptions(false);
		DAVRepositoryFactory.setup();

		SVNClientManager ourClientManager = SVNClientManager.newInstance(
				options, Settings.getInstance().getSvnUsername(), Settings.getInstance().getSvnPassword());

		SVNWCClient ourWCClient = new SVNWCClient(ourClientManager, options);

		File[] file = new File[2];
		file[0] = new File(getFileName());
		file[1] = new File(getSolutionFileName());

		for (int i = 0; i < file.length; i++) {
			try {
				ourWCClient.doDelete(file[i], true, false);
			} catch (SVNException e) {

				System.err.println(e);
			}
		}

		ExerciseSVN.SVNUpdate();
		ExerciseSVN.SVNcommit();

		// System.out.println("filesystem delete");
		// for (int i = 0; i < file.length; i++) {
		//			
		// file[i].delete();
		//			
		// }

	}

	public String readFromDisk(boolean solution) {

		File file;
		if (solution == false) {
			file = new File(this.getFileName());

		} else {
			file = new File(this.getSolutionFileName());
		}
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(file));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		String line;
		String temp = "";
		try {
			while ((line = br.readLine()) != null) {
				temp += line + "\n";
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return temp;

	}

	public String readFromDisk(int revision, boolean solution) {

		File file;
		if (solution == false) {
			file = new File(this.getRevisionFileName());
		} else {
			file = new File(this.getRevisionSolutionFileName());
		}
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(file));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		String line;
		String temp = "";
		try {
			while ((line = br.readLine()) != null) {
				temp += line + "\n";
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return temp;

	}

	public void saveToDisk() {
		File fileFolder = new File(ExerciseSVN.SVNpath + "exercises");
		fileFolder.mkdirs();

		File file = new File(getFileName());
		File solFile = new File(getSolutionFileName());
		BufferedWriter bw;
		BufferedWriter bwSol;
		try {
			bw = new BufferedWriter(new FileWriter(file));
			bwSol = new BufferedWriter(new FileWriter(solFile));
			bw.write(this.exerciseText);
			bw.flush();
			bw.close();
			System.out.println("writing to: " + file.getAbsolutePath());
			bwSol.write(this.solution);
			bwSol.flush();
			bwSol.close();
			System.out.println("writing to: " + solFile.getAbsolutePath());
		} catch (IOException e1) {
			System.out.println("Error while writing to disk");
			e1.printStackTrace();
		}

		File[] fileArray = new File[2];
		fileArray[0] = file;
		fileArray[1] = solFile;

		try {

			this.setRevisionnumber((int) ExerciseSVN.SVNAdd(fileArray));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SVNException e) {
			// TODO Auto-generated catch block
			System.err.println(e);
		}

		// int revNumber = (int) ExerciseSVN.SVNcommit();
		//
		// if (this.getRevisionnumber() != revNumber && revNumber != -1) {
		// this.setRevisionnumber(revNumber);
		//
		// try {
		// this.update(true);
		// } catch (SQLException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// }

		// try {
		// int rev = (int) ExerciseSVN.SVNAdd(solFile);
		// this.setRevisionnumber(rev);
		// } catch (Exception e) {
		//
		// System.out.println("FILE ALREADY EXISTS");
		// }
		//
		// System.out.println("writing to: " + solFile.getAbsolutePath());
		//
		// if (this.getRevisionnumber() != revNumber && revNumber != -1) {
		// this.setRevisionnumber(revNumber);
		// try {
		// this.update(true);
		// } catch (SQLException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// }
		//
		// }
		ExerciseSVN.SVNUpdate();
	}

}
