#pragma once
#include "Core/Array.h"
#include "Schema.h"
#include "QueryStr.h"

namespace sql {

	/**
	 * A type that describes migrations to a database in a high-level way. This lets us describe
	 * what needs to be changed in a database-independent way (the syntax and capabilities for alter
	 * table differs quite a bit).
	 */
	class Migration : public Object {
		STORM_CLASS;
	public:
		// Create an empty migration.
		STORM_CTOR Migration();

		/**
		 * Modifications to column attributes.
		 *
		 * Note that modifications to primary keys are handled separately, since it is necessary to
		 * know *all* columns that are a part of the primary key, not just the ones that were
		 * changed.
		 */
		class ColAttrs : public Object {
			STORM_CLASS;
		public:
			// Create.
			STORM_CTOR ColAttrs(Str *name, QueryType type);

			// Name of the column.
			Str *name;

			// Type of the column (for queries).
			QueryType type;

			// Columns in the current version of the column.
			Schema::Attributes currentAttributes;

			// Columns in the desired version of the column.
			Schema::Attributes desiredAttributes;

			// Any current default value.
			MAYBE(Str *) currentDefault;

			// Desired default value.
			MAYBE(Str *) desiredDefault;

			// Any changes?
			Bool STORM_FN any() const;
			Bool STORM_FN empty() const;

			// To string.
			void STORM_FN toS(StrBuf *to) const;

			// Get a column that represents the current version of the column.
			Schema::Column *STORM_FN toSchema() const;
		};

		/**
		 * Modifications to a table.
		 */
		class Table : public Object {
			STORM_CLASS;
		public:
			// Create.
			STORM_CTOR Table(Str *table);

			// Name of the table.
			Str *table;

			// Columns to remove.
			Array<Str *> *colRemove;

			// Modifications to columns.
			Array<ColAttrs *> *colMigrate;

			// Columns to add.
			Array<Schema::Column *> *colAdd;

			// Update the set of primary keys? If true, the array `primaryKeys` contains the new
			// (possibly empty) set of primary keys to apply. Otherwise, the migration should not
			// touch the set of primary keys.
			Bool updatePrimaryKeys;

			// Remove the current primary key? Only valid if `updatePrimaryKeys` is true.
			Bool dropPrimaryKeys;

			// New set of primary keys (or empty, if they should be removed).
			Array<Str *> *primaryKeys;

			// Anything to do?
			Bool STORM_FN any() const;
			Bool STORM_FN empty() const;

			// To string.
			virtual void STORM_FN toS(StrBuf *to) const;
		};

		// Tables to remove.
		Array<Str *> *tableRemove;

		// Tables to modify.
		Array<Table *> *tableMigrate;

		// Tables to add.
		Array<Schema *> *tableAdd;

		/**
		 * Modifications to an index.
		 */
		class Index : public Schema::Index {
			STORM_CLASS;
		public:
			// Create.
			STORM_CTOR Index(Str *table, Schema::Index *index);
			STORM_CTOR Index(Str *table, Str *name, Array<Str *> *columns);

			// The table the index is intended for.
			Str *table;

			// To string.
			virtual void STORM_FN toS(StrBuf *to) const;

			// To SQL.
			virtual void STORM_FN toSQL(QueryStrBuilder *to) const;
			using Schema::Index::toSQL;
		};

		// Indices to remove. Note that the columns may be empty for removed indices, the table name
		// is the important part since e.g. MySQL and MariaDB requires the table name for the index.
		Array<Index *> *indexRemove;

		// Indices to add.
		Array<Index *> *indexAdd;

		// Anything to do?
		Bool STORM_FN any() const;
		Bool STORM_FN empty() const;

		// To string.
		virtual void STORM_FN toS(StrBuf *to) const;
	};

}
