// Copyright (c) 2007, Patryk Pomykalski
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Aegisub Group nor the names of its contributors
//     may be used to endorse or promote products derived from this software
//     without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------------
//
// AEGISUB
//
// Website: http://aegisub.cellosoft.com
// Contact: mailto:pomyk@go2.pl
//

#pragma once

#ifndef _AUTO4_RUBY_H
#define _AUTO4_RUBY_H

#ifdef _MSC_VER
#pragma warning(disable: 4311 4312)
#endif

#include "auto4_base.h"
#include <wx/thread.h>
#include <wx/event.h>
#include <ruby.h>

class wxWindow;

namespace Automation4 {

	
	// Provides access to an AssFile object (and all lines contained) for a Ruby script
	class RubyAssFile {
	private:
		AssFile *ass;

		bool can_modify;
		bool can_set_undo;

		// keep a cursor of last accessed item to avoid walking over the entire file on every access
		std::list<AssEntry*>::iterator last_entry_ptr;
		int last_entry_id;

		static int RubyParseTagData();
		static int RubyUnparseTagData();
		static int RubySetUndoPoint();

	public:
		void RubyUpdateAssFile(VALUE subtitles);
		static VALUE AssEntryToRuby(AssEntry *e); // makes a Ruby representation of AssEntry
		static AssEntry *RubyToAssEntry(VALUE ass_entry); // creates an AssEntry object from a Ruby representation
		RubyAssFile(AssFile *_ass, bool _can_modify, bool _can_set_undo);
		~RubyAssFile();

		static RubyAssFile *raf;
		VALUE rbAssFile;

	};


	// Provides progress UI and control functions for a Ruby script
	class RubyProgressSink : public ProgressSink {
	private:

	public:
		RubyProgressSink(wxWindow *parent, bool allow_config_dialog = true);
		virtual ~RubyProgressSink();
		static RubyProgressSink* inst;
		static VALUE RubySetProgress(VALUE self, VALUE progress);
		static VALUE RubySetTask(VALUE self, VALUE task);
		static VALUE RubySetTitle(VALUE self, VALUE title);
		static VALUE RubyGetCancelled(VALUE self);
		static VALUE RubyDebugOut(int argc, VALUE *args, VALUE self);
		static VALUE RubyDisplayDialog(VALUE self, VALUE cfg, VALUE buttons);
	};


	// Provides Config UI functions for a Ruby script
	class RubyConfigDialogControl {
	public:
		wxControl *cw; // control window
		wxString name, hint;
		int x, y, width, height;
		VALUE name_sym;

		virtual wxControl *Create(wxWindow *parent) = 0;
		virtual void ControlReadBack() = 0;
		virtual VALUE RubyReadBack() = 0;

		RubyConfigDialogControl();
		RubyConfigDialogControl(VALUE opts);
		virtual ~RubyConfigDialogControl() { }
	};

	class RubyConfigDialog : public ScriptConfigDialog {
	private:
		std::vector<RubyConfigDialogControl*> controls;
		std::vector<wxString> buttons;
		bool use_buttons;

		class ButtonEventHandler : public wxEvtHandler {
		public:
			int *button_pushed;
			void OnButtonPush(wxCommandEvent &evt);
		};

		ButtonEventHandler *button_event;
		int button_pushed;

	protected:
		wxWindow* CreateWindow(wxWindow *parent);

	public:
		RubyConfigDialog(VALUE cfg, VALUE buttons, bool show_buttons);
		virtual ~RubyConfigDialog();
		VALUE RubyReadBack(); // read back internal structure to Ruby hash

		void ReadBack(); // from auto4 base
	};


	// Second base-class for Ruby implemented Features
	class RubyFeature : public virtual Feature {
	protected:
		
		int myid;

		RubyFeature(ScriptFeatureClass _featureclass, const wxString &_name);

		void RegisterFeature();
		VALUE CreateIntegerArray(const std::vector<int> &ints);
		void ThrowError();
	};


	// Class of Ruby scripts
	class RubyScript : public Script {
		friend class RubyFeature;
		friend class RubyProgressSink;

	private:

		void Create(); // load script and create internal structures etc.
		void Destroy(); // destroy internal structures, unreg features and delete environment

		static RubyScript* GetScriptObject();
	public:
		static VALUE RubyTextExtents(VALUE self, VALUE style, VALUE text);
		static VALUE RubyFrameToTime(VALUE self, VALUE frame);
		static VALUE RubyTimeToFrame(VALUE self, VALUE time);
		static VALUE RubyKeyFrames(VALUE self);
		static VALUE backtrace_hook(VALUE self, VALUE backtr);
		
		RubyScript(const wxString &filename);
		static void RubyError();
		static wxString GetError();
		virtual ~RubyScript();
		virtual void Reload();
		static RubyScript* inst;
	};


	// Implementation of the Macro Feature for Ruby scripts
	class RubyFeatureMacro : public FeatureMacro, RubyFeature {
	private:
		bool no_validate;
		VALUE macro_fun;
		VALUE validation_fun;

	protected:
		RubyFeatureMacro(const wxString &_name, const wxString &_description, VALUE macro_function, VALUE validate_function);
	public:
		static VALUE RubyRegister(VALUE self, VALUE name, VALUE description, VALUE macro_function, VALUE validate_function);
		virtual ~RubyFeatureMacro() { }

		virtual bool Validate(AssFile *subs, const std::vector<int> &selected, int active);
		virtual void Process(AssFile *subs, std::vector<int> &selected, int active, wxWindow * const progress_parent);
	};


	// Implementation of the Export Filter Feature for Ruby scripts
	class RubyFeatureFilter : public FeatureFilter, RubyFeature {
	private:
		bool has_config;
		RubyConfigDialog *config_dialog;
		VALUE filter_fun;
		VALUE dialog_fun;

	protected:
		RubyFeatureFilter(const wxString &_name, const wxString &_description, int merit, VALUE function, VALUE dialog);

		ScriptConfigDialog* GenerateConfigDialog(wxWindow *parent);

		void Init();
	public:
		static VALUE RubyRegister(VALUE self, VALUE name, VALUE description, VALUE merit, VALUE macro_function, VALUE validate_function);

		void ProcessSubs(AssFile *subs, wxWindow *export_dialog);
	};

	// class for registering ruby objects created in c++
	// so garbage collector doesn't destroy them
	class RubyObjects {
	private:
		VALUE objects;
		static RubyObjects *inst;
		RubyObjects();
	public:
		~RubyObjects();
		static RubyObjects *Get();
		void Register(VALUE object);
		void Unregister(VALUE object);
	};

	// stuff for safe calling of ruby functions
	struct RubyCallArguments {
		VALUE recv;
		ID id;
		int n;
		VALUE *argv;
		RubyCallArguments(VALUE _recv, ID _id, int _n, VALUE *_argv);
	};

	// Separate thread for ruby interpreter
	class RubyThread : public wxThread {
	private:
		enum {NOTHING, CALL_FUNCTION, LOAD_FILE};
		int action;
		int status;
		RubyCallArguments *args;
		const char* file;
		VALUE *result;
		void InitRuby();
		wxPathList include_path;
	public:
		RubyThread(wxPathList include_path);
		void CallFunction(RubyCallArguments* arg, VALUE *res);
		void LoadFile(const char* file);
		int GetStatus() {return status;};
		virtual ExitCode Entry();
	};

	VALUE rbCallWrapper(VALUE arg);
	VALUE rbExecWrapper(VALUE arg);
	VALUE rbLoadWrapper(VALUE arg);
	VALUE rbGcWrapper(VALUE arg);
	VALUE rbAss2RbWrapper(VALUE arg);
	VALUE rb2AssWrapper(VALUE arg);
	VALUE rbError(VALUE arg);
	typedef VALUE (*RB_HOOK)(...);
	typedef VALUE (*RB_HOOK2)(VALUE);
	
#define STR2SYM(x) ID2SYM(rb_intern(x))
};

#endif
