#pragma once
#include "SQL.h"

namespace sql {

	class SequentialStatement;

	/**
	 * Wrapper over the connection interface for database connections that communicate with the
	 * database over a sequential connection (e.g. a socket). This type of connections typically
	 * typically disallow performing any other operations while the results from a prepared
	 * statement are fetched. As such, the implementation adapts the standard interface to a
	 * "sequential interface" and introduces buffering and delays as necessary.
	 *
	 * The class does two things to make operations sequential:
	 * - Store the results from a query in RAM if the user of the interface attempts to do
	 *   anything before results are fetched to completion.
	 * - Delay finalization of prepared statements if results are being fetched.
	 */
	class SequentialConnection : public DBConnection {
		STORM_ABSTRACT_CLASS;
	public:
		// Create.
		STORM_CTOR SequentialConnection();

	protected:
		// May be called at any time to ensure that no fetches are underway. This causes us to store
		// the remaining rows in RAM until they are either consumed or discarded.
		void clearFetch();

		// Same as 'clearFetch', but does nothing if we are the statement currently fetching data.
		void clearFetch(SequentialStatement *stmt);

		// Is the specified statement free to fetch at the moment?
		Bool isFetching(SequentialStatement *stmt);

		/**
		 * Additional interface that should be implemented by derived classes.
		 */

		// Called to finalize a prepared statement. This logic needs to be implemented here since
		// vtables don't work after the destructor of SequentialStatement have been executed.
		virtual void STORM_FN finalizeStmt(SequentialStatement *stmt) ABSTRACT;

	private:
		// Interface intended to be used from SequentialStatement.
		friend class SequentialStatement;

		// Called by Stmt when we start fetching data.
		void startFetch(SequentialStatement *stmt);

		// Called by Stmt when it is done fetching data.
		void doneFetch(SequentialStatement *stmt);

		// Called by Stmt when it wishes to be finalize.
		void onFinalize(SequentialStatement *stmt);

	private:
		// Queue of statements that should be finalized when the opportunity arises, forming a
		// linked list.
		SequentialStatement *toFinalize;

		// Statement that is currently fetching data, if any.
		SequentialStatement *currentFetching;

		// Keep track of active statements

		// Run finalizers.
		void finalizeAll();
	};


	/**
	 * Custom statement for sequential connections.
	 *
	 * Most operations call back into SequentialConnection. Each database connection will however
	 * need to handle binding parameters since that is expected to be possible without communicating
	 * to the database itself.
	 */
	class SequentialStatement : public Statement {
		STORM_ABSTRACT_CLASS;
	public:
		// Create.
		STORM_CTOR SequentialStatement(SequentialConnection *connection);

		// Destroy, automatically call finalize if necessary.
		virtual ~SequentialStatement();

		// Execute the statement. Derived classes should override 'executeSeq' instead.
		Statement::Result STORM_FN execute() override;

		// Finalize the statement. Derived classes should override 'finalizeStmt' in the base class instead.
		void STORM_FN finalize() override;

	protected:
		/**
		 * New functionality for derived classes.
		 */

		// Get owning connection.
		SequentialConnection *connection() const { return owner; }

		// Derived classes should call this function when an execute call is known to not produce
		// any data. Otherwise returning null from 'nextRowSeq' is enough.
		void STORM_FN fetchDone();


		/**
		 * Interception of existing interface.
		 */

		// Called when the result should be disposed.
		void STORM_FN disposeResult() override;

		// Called to get the next result. Override 'nextRowSeq' instead.
		Maybe<Row> STORM_FN nextRow() override;


		/**
		 * New interface.
		 */

		// Called when the statement should be executed. Returns 'true' if there may be something to
		// get through 'nextRowSeq'.
		virtual Bool STORM_FN executeSeq() ABSTRACT;

		// Called when any remaining results should be disposed.
		virtual void STORM_FN disposeResultSeq() ABSTRACT;

		// Called to get the next row.
		virtual Maybe<Row> STORM_FN nextRowSeq() ABSTRACT;

	private:
		friend class SequentialConnection;

		// Linked list of statements that the connection should finalize. Note that the objects in
		// here might be destroyed, and virtual functions may not work as expected.
		SequentialStatement *toFinalize;

		// Associated connection.
		SequentialConnection *owner;

		// Buffered rows if necessary.
		Array<Row> *buffer;

		// Reached the end of the actual DB connection's stream?
		Bool atEnd;

		// Buffer all elements. Called by SequentialConnection.
		void fetchAll();
	};

}
