package de.insignificance;

/*
 Copyright 2012,2013 Sebastian Lederer

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import de.dassit.dbwrapper.Entry;

public class PackageList extends Entry {
	int id;
	String name;
	String description;
	int origin;
	int priority;
	boolean approvalRequired;
	boolean approved;
	boolean atomic;
	boolean autoUpdate;
	String owner;

	public PackageList() {
		keyName = "id";
		tableName = "package_lists";
	}

	@Override
	public void validate() throws SQLException {
		verifySqlString(name);
		verifySqlString(owner);
		verifySqlString(description);
	}

	/**
	 * Copy packages from another package list
	 * 
	 * @param origin
	 *            the ID of the origin PackageList object
	 * @throws SQLException
	 */

	// "INSERT INTO packages VALUES(DEFAULT,'" + name + "','" + version
	// + "','" + description + "'," + install + ",'" + arch + "',"
	// + packageListId + ")";

	public void copyFrom(int origin) throws SQLException {
		if (origin == 0)
			return;

		Statement s = getStatement();
		copyFromWithStatement(origin, s);
		releaseStatement(s);
	}

	void copyFromWithStatement(int origin, Statement s) throws SQLException {
		if (origin == 0)
			return;
		
		Connection c = s.getConnection();
		boolean doTransaction = c.getAutoCommit();
		if (doTransaction) {
			c.setAutoCommit(false);
		}
		String sql = "DELETE FROM packages WHERE list_id="
				+ id
				+ ";"
				+ "INSERT INTO packages (name,version,description,install,arch,list_id)"
				+ "SELECT name,version,description,install,arch," + id
				+ " FROM packages WHERE list_id=" + origin;

		s.execute(sql);
		s.execute("UPDATE package_lists SET approved=false WHERE id=" + id);
		if (doTransaction) {
			c.commit();
			c.setAutoCommit(true);
		}
	}

	/**
	 * Compare the packages from this package list to its origin and return all
	 * packages from the origin list which are different. A package is
	 * considered different if the name and arch fields are the same, but the
	 * version field is different. Packages which exist only in one of the lists
	 * are ignored.
	 * 
	 * @param origin
	 *            the ID of the origin PackageList object
	 * @return a (possibly empty) list of Package objects from the origin list
	 *         which are different
	 * @throws SQLException
	 */
	public List<Entry> diffPackages(int origin) throws SQLException {
		// String sql =
		// "SELECT DISTINCT o.id,o.name,o.version,o.description,o.install,o.arch,o.list_id "
		// +
		// "FROM packages AS p JOIN packages AS o ON p.name=o.name AND p.arch=o.arch "
		// + "WHERE p.version <> o.version AND o.list_id="
		// + origin
		// + " AND p.list_id=" + id + " ORDER BY o.name";

		String sql = "SELECT DISTINCT o.id,o.name,o.version,o.description,o.install,o.arch,p.list_id"
				+ " FROM packages AS o LEFT JOIN packages AS p ON p.name=o.name AND p.arch=o.arch"
				+ " AND o.list_id="
				+ origin
				+ " AND p.list_id="
				+ id
				+ " WHERE o.install=true AND o.list_id="
				+ origin
				+ " AND (o.version<>p.version OR p.name IS NULL)"
				+ " ORDER BY o.name";

		Package factory = new Package();
		return factory.query(sql);
	}

	/**
	 * Compare the packages from this package list to its origin and return all
	 * packages from the origin list which are not in this list.
	 * 
	 * @param origin
	 *            the ID of the origin PackageList object
	 * @return a (possibly empty) list of Package objects from the origin list
	 *         which are not present in this list
	 * @throws SQLException
	 */
	public List<Entry> extraPackages(int origin) throws SQLException {
		String sql = "SELECT DISTINCT o.id,o.name,o.version,o.description,o.install,o.arch,o.list_id "
				+ "FROM packages AS o LEFT JOIN packages AS p ON p.name=o.name AND p.arch=o.arch "
				+ "AND o.list_id="
				+ origin
				+ " AND p.list_id="
				+ id
				+ " WHERE o.install=true AND p.name IS NULL AND o.list_id="
				+ origin + " ORDER BY o.name";

		Package factory = new Package();
		return factory.query(sql);
	}

	public Package getPackageToUpdate(long originPackageId) throws SQLException {
		Package originPackage = new Package();
		originPackage.load(Long.toString(originPackageId));

		Package factory = new Package();
		List<Entry> results = factory.fetch("WHERE list_id=" + id + " AND "
				+ sqlStringValue(originPackage.name, "name") + " AND "
				+ sqlStringValue(originPackage.arch, "arch"));

		if (results.isEmpty()) {
			Package newPackage = new Package();
			newPackage.packageListId = this.id;
			newPackage.name = originPackage.name;
			newPackage.arch = originPackage.arch;
			return newPackage;
		}
		return (Package) results.get(0);
	}

	public boolean checkApproval() throws SQLException {
		if (origin == 0)
			return true;
		PackageList pl = getOriginPackageList();
		if (pl == null)
			return false;
		return !(pl.approvalRequired && !pl.approved);
	}

	@Override
	public String getSelectStatement() {
		return "SELECT * FROM package_lists";
	}

	@Override
	public String getWhereClause(String key) {
		return "WHERE name='" + key + "'";
	}

	@Override
	public String getInsertStatement() {
		return "INSERT INTO package_lists VALUES(DEFAULT,"
				+ sqlStringValueComma(name) + (origin) + "," + priority + ","
				+ approvalRequired + "," + approved + "," + atomic + ","
				+ sqlStringValueComma(owner) + autoUpdate + ","
				+ sqlStringValue(description) + ")";
	}

	@Override
	public String getUpdateStatement() {
		return "UPDATE package_lists SET " + sqlStringValueComma(name, "name")
				+ "origin=" + origin + "," + "priority=" + priority
				+ ",approvalrequired=" + approvalRequired + ",approved="
				+ approved + ",atomic=" + atomic + ","
				+ sqlStringValueComma(owner, "owner") + "autoupdate="
				+ autoUpdate + "," + sqlStringValue(description, "description")
				+ " WHERE id=" + id;
	}

	@Override
	public String getDeleteStatement() {
		return "DELETE FROM package_lists WHERE id=" + id;
	}

	@Override
	public String getRenameStatement(String newName) {
		return "UPDATE package_lists SET " + sqlStringValue(newName, "name")
				+ " WHERE id=" + id;
	}

	@Override
	public String getCreateStatement() {
		return "DROP TABLE package_lists;CREATE TABLE package_lists (id INTEGER AUTO_INCREMENT, name VARCHAR(80),"
				+ "origin INTEGER, priority INTEGER,"
				+ "approvalrequired BOOLEAN, approved BOOLEAN, atomic BOOLEAN,"
				+ "owner VARCHAR(80), autoupdate BOOLEAN, description VARCHAR(128);";
	}

	@Override
	public void initFromQuery(ResultSet queryResult) throws SQLException {
		id = queryResult.getInt(1);
		name = queryResult.getString(2);
		origin = queryResult.getInt(3);
		priority = queryResult.getInt(4);
		approvalRequired = queryResult.getBoolean(5);
		approved = queryResult.getBoolean(6);
		atomic = queryResult.getBoolean(7);
		owner = queryResult.getString(8);
		autoUpdate = queryResult.getBoolean(9);
		description = queryResult.getString(10);
	}

	@Override
	public Entry newFromQuery(ResultSet rs) throws SQLException {
		PackageList u = new PackageList();
		u.initFromQuery(rs);
		return (Entry) u;
	}

	public void addPackage(Package aPackage) throws SQLException {
		aPackage.packageListId = this.id;
		aPackage.save();
	}

	public void addRepository(Repository aRepo) throws SQLException {
		RepoListMembership m = new RepoListMembership();
		m.listId = this.id;
		m.repoId = aRepo.id;
		m.save();
	}

	public void removeRepository(Repository aRepo) throws SQLException {
		RepoListMembership m = new RepoListMembership();
		m.listId = this.id;
		m.repoId = aRepo.id;
		m.delete();
	}

	public List<Entry> getPackages() throws SQLException {
		Package factory = new Package();
		List<Entry> result = new ArrayList<Entry>();
		result = factory.fetch("WHERE list_id=" + id);
		return result;
	}

	public List<Entry> getInstalledPackages() throws SQLException {
		Package factory = new Package();
		List<Entry> result = new ArrayList<Entry>();
		result = factory.fetch("WHERE install=true AND list_id=" + id);
		return result;
	}

	@Override
	public void delete() throws SQLException {
		deletePackages();
		for (Entry r : getRepositories())
			removeRepository((Repository) r);
		List<Entry> successors = fetch("WHERE origin=" + id);
		for (Entry e : successors) {
			PackageList p = (PackageList) e;
			p.setOrigin(0);
			p.save();
		}
		super.delete();
	}

	public void deletePackages() throws SQLException {
		Package factory = new Package();
		factory.deleteAllFrom(this.id);
	}

	public void printPackages() throws SQLException {
		List<Entry> packages = getPackages();

		for (Entry e : packages) {
			Package p = (Package) e;
			System.out.println(p.id + "\t" + p.name + "\t" + p.arch + "\t"
					+ p.version);
		}

	}

	public Package findPackage(String name) throws SQLException {
		Package factory = new Package();
		List<Entry> result = new ArrayList<Entry>();
		result = factory.fetch("WHERE name='" + name + "' AND list_id=" + id);
		if (result.isEmpty())
			return null;
		return (Package) result.get(0);
	}

	public PackageList getOriginPackageList() throws SQLException {
		PackageList p = new PackageList();
		List<Entry> result = new ArrayList<Entry>();
		result = p.fetch("WHERE id=" + origin);
		if (result.isEmpty())
			return null;
		return (PackageList) result.get(0);
	}

	public void removeApproval() throws SQLException {
		approved = false;
		save();
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the origin
	 */
	public int getOrigin() {
		return origin;
	}

	/**
	 * @param origin
	 *            the origin to set
	 */
	public void setOrigin(int origin) {
		this.origin = origin;
	}

	/**
	 * @return the id
	 */
	public int getId() {
		return id;
	}

	/**
	 * @return the priority
	 */
	public int getPriority() {
		return priority;
	}

	/**
	 * @param priority
	 *            the priority to set
	 */
	public void setPriority(int priority) {
		this.priority = priority;
	}

	/**
	 * @return the approvalRequired
	 */
	public boolean isApprovalRequired() {
		return approvalRequired;
	}

	/**
	 * @param approvalRequired
	 *            the approvalRequired to set
	 */
	public void setApprovalRequired(boolean approvalRequired) {
		this.approvalRequired = approvalRequired;
	}

	/**
	 * @return the approved
	 */
	public boolean isApproved() {
		return approved;
	}

	/**
	 * @param approved
	 *            the approved to set
	 */
	public void setApproved(boolean approved) {
		this.approved = approved;
	}

	/**
	 * @return the atomic
	 */
	public boolean isAtomic() {
		return atomic;
	}

	/**
	 * @param atomic
	 *            the atomic to set
	 */
	public void setAtomic(boolean atomic) {
		this.atomic = atomic;
	}

	/**
	 * @return the owner
	 */
	public String getOwner() {
		return owner;
	}

	/**
	 * @param owner
	 *            the owner to set
	 */
	public void setOwner(String owner) {
		this.owner = owner;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public boolean isAutoUpdate() {
		return autoUpdate;
	}

	public void setAutoUpdate(boolean autoUpdate) {
		this.autoUpdate = autoUpdate;
	}

	public void setId(int id) {
		this.id = id;
	}

	public List<Entry> getRepositories() throws SQLException {
		List<Entry> result = new ArrayList<Entry>();

		RepoListMembership fac = new RepoListMembership();
		List<Entry> memberships = fac.getMembersForList(this);
		for (Entry e : memberships) {
			RepoListMembership m = (RepoListMembership) e;
			Repository repo = new Repository();
			Entry r = repo.fetch("WHERE id=" + m.repoId + " ORDER BY name")
					.get(0);
			result.add(r);
		}
		return result;
	}

	public static void main(String[] args) throws SQLException {
	}
}
