#include "stdafx.h"
#include "Migration.h"
#include "Core/Join.h"

namespace sql {

	Migration::Migration() {
		tableAdd = new (this) Array<Schema *>();
		tableMigrate = new (this) Array<Table *>();
		tableRemove = new (this) Array<Str *>();
		indexAdd = new (this) Array<Index *>();
		indexRemove = new (this) Array<Index *>();
	}

	Bool Migration::any() const {
		if (tableAdd->any() || tableRemove->any() || indexAdd->any() || indexRemove->any())
			return true;

		for (Nat i = 0; i < tableMigrate->count(); i++)
			if (tableMigrate->at(i)->any())
				return true;

		return false;
	}

	Bool Migration::empty() const {
		return !any();
	}

	void Migration::toS(StrBuf *to) const {
		*to << S("{");
		{
			storm::Indent z(to);
			if (tableRemove->any())
				*to << S("\nremove tables: ") << join(tableRemove, S(", "));
			if (tableMigrate->any()) {
				*to << S("\nmigrate tables:\n");
				storm::Indent w(to);
				*to << join(tableMigrate, S("\n"));
			}
			if (tableAdd->any()) {
				*to << S("\nadd tables:\n");
				storm::Indent w(to);
				*to << join(tableAdd, S("\n"));
			}
			if (indexRemove->any())
				*to << S("\nremove index: ") << join(indexRemove, S(", "));
			if (indexAdd->any()) {
				*to << S("\nadd index:\n");
				storm::Indent w(to);
				*to << join(indexAdd, S("\n"));
			}
		}
		*to << S("\n}");
	}

	Migration::ColAttrs::ColAttrs(Str *name, QueryType type) : name(name), type(type) {}

	Bool Migration::ColAttrs::any() const {
		if (currentAttributes != desiredAttributes)
			return true;

		if ((currentDefault != null) != (desiredDefault != null))
			return true;

		if (currentDefault && desiredDefault)
			return *currentDefault != *desiredDefault;

		return false;
	}

	Bool Migration::ColAttrs::empty() const {
		return !any();
	}

	void Migration::ColAttrs::toS(StrBuf *to) const {
		*to << name << S(" ") << type << S(" => current:");
		sql::toSQL(to, currentAttributes);
		if (currentDefault)
			*to << S(" DEFAULT ") << currentDefault;
		*to << S(", desired:");
		sql::toSQL(to, desiredAttributes);
		if (desiredDefault)
			*to << S(" DEFAULT ") << desiredDefault;
	}

	Schema::Column *Migration::ColAttrs::toSchema() const {
		Schema::Column *col = new (this) Schema::Column(name, type, desiredAttributes);
		col->defaultValue = desiredDefault;
		return col;
	}


	Migration::Table::Table(Str *table) : table(table) {
		colRemove = new (this) Array<Str *>();
		colMigrate = new (this) Array<ColAttrs *>();
		colAdd = new (this) Array<Schema::Column *>();
		updatePrimaryKeys = false;
		primaryKeys = new (this) Array<Str *>();
	}

	Bool Migration::Table::any() const {
		return colRemove->any()
			|| colMigrate->any()
			|| colAdd->any()
			|| updatePrimaryKeys;
	}

	Bool Migration::Table::empty() const {
		return !any();
	}

	void Migration::Table::toS(StrBuf *to) const {
		*to << table << S(" : {");
		{
			storm::Indent z(to);
			if (colRemove->any())
				*to << S("\nremove columns: ") << join(colRemove, S(", "));
			if (colMigrate->any()) {
				*to << S("\nmigrate columns:\n");
				storm::Indent w(to);
				*to << join(colMigrate, S("\n"));
			}
			if (colAdd->any())
				*to << S("\nadd columns: ") << join(colAdd, S(", "));
		}
		*to << S("\n}");
	}


	Migration::Index::Index(Str *table, Schema::Index *index)
		: Schema::Index(*index), table(table) {}

	Migration::Index::Index(Str *table, Str *name, Array<Str *> *columns)
		: Schema::Index(name, columns), table(table) {}

	void Migration::Index::toS(StrBuf *to) const {
		*to << S("INDEX ") << name << S(" ON ") << table << S("(") << join(columns, S(", ")) << S(")");
	}

	void Migration::Index::toSQL(QueryStrBuilder *to) const {
		Schema::Index::toSQL(to, table);
	}

}
