/***************************************************************************

  gbx_class.h

  (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  MA 02110-1301, USA.

***************************************************************************/

#ifndef __GBX_CLASS_H
#define __GBX_CLASS_H

#include "gb_error.h"
#include "gbx_type.h"
#include "gb_table.h"
#include "gbx_class_desc.h"
#include "gbx_component.h"

#ifndef __GBX_CLASS_C
EXTERN bool CLASS_exiting;
#endif

typedef
	int CLASS_ID;

typedef
	struct {
		CTYPE type;
		int pos;
		}
	CLASS_VAR;

typedef
	struct _CLASS *CLASS_REF;

typedef
	char *CLASS_UNKNOWN;

typedef
	union {
		int type;
		struct { int type; double value; } PACKED _float;
		struct { int type; float value; } PACKED _single;
		struct { int type; int value; } PACKED _integer;
		struct { int type; int64_t value; } PACKED _long;
		struct { int type; char *addr; int len; } PACKED _string;
		struct { int type; int val[2]; } PACKED _swap;
		}
	PACKED
	CLASS_CONST;

typedef
	struct {
		CTYPE type;
		}
	CLASS_LOCAL;

typedef
	struct {
		TYPE type;
		}
	CLASS_PARAM;

typedef
	struct {
		char *name;
		int len;
	}
	PACKED
	DEBUG_SYMBOL;
	
typedef
	struct {
		DEBUG_SYMBOL sym;
		int value;
		}
	LOCAL_SYMBOL;

typedef
	struct {
		unsigned short line;
		unsigned short nline;
		unsigned short *pos;
		char *name;
		LOCAL_SYMBOL *local;
		short n_local;
		unsigned short index; // Function index in its class. Used by the profiler
	}
	PACKED
	FUNC_DEBUG;

typedef
	struct {
		TYPE type;
		uchar n_param;
		uchar npmin;
		uchar vararg;
		unsigned fast : 1;
		unsigned unsafe : 1;
		unsigned fast_linked : 1;
		unsigned optional : 1;
		unsigned use_is_missing : 1;
		unsigned is_static : 1;
		unsigned _reserved : 2;
		uchar n_local;
		uchar n_ctrl;
		short n_label; // Number of label for indirect jumps
		short stack_usage;
		short error;
		unsigned short *code;
		CLASS_PARAM *param;
		CLASS_LOCAL *local;
		FUNC_DEBUG *debug;
		}
	PACKED
	FUNCTION;

typedef
	struct {
		TYPE type;
		char n_param;
		char npmin;
		char vararg;
		unsigned char flag;
		short n_local;
		short n_ctrl;
		}
	PACKED
	FUNCTION_LOAD;

typedef
	struct {
		TYPE type;
		short n_param;
		short _reserved;
		CLASS_PARAM *param;
		char *name;
		}
	PACKED
	CLASS_EVENT;

typedef
	struct {
		TYPE type;
		short n_param;
		bool vararg;
		unsigned loaded : 1;
		unsigned _reserved : 7;
		CLASS_PARAM *param;
		char *alias;
		char *library;
		}
	PACKED
	CLASS_EXTERN;

typedef
	struct {
		uint magic;
		uint version;
		uint endian;
		uint flag;
		}
	PACKED
	CLASS_HEADER;

typedef
	struct {
		short parent;
		short flag;
		int s_static;
		int s_dynamic;
		short nstruct;
		short _reserved;
		}
	PACKED
	CLASS_INFO;

typedef
	struct {
		CTYPE ctype;
		int dim[0];
		}
	CLASS_ARRAY;

typedef
	CLASS_ARRAY *CLASS_ARRAY_P;

struct _CLASS;

typedef
	struct {
		DEBUG_SYMBOL sym;
		CTYPE ctype;
		int value;
		}
	GLOBAL_SYMBOL;

typedef
	struct {
		int nfield;
		int *desc;
	}
	CLASS_STRUCT;

typedef
	struct {
		short n_cst;
		short n_stat;
		short n_dyn;
		short n_func;

		CLASS_CONST *cst;
		CLASS_VAR *stat;
		CLASS_VAR *dyn;
		FUNCTION *func;
		CLASS_EVENT *event;
		CLASS_EXTERN *ext;
		CLASS_ARRAY **array;

		CLASS_REF *class_ref;
		char **unknown;

		GLOBAL_SYMBOL *global;
		ushort *sort;
		short n_global;
		short n_ext;
		uint *prof; // Shortcut indexes attributed during profiling. The first one is for the class, the other ones for the functions
		#ifdef OS_64BITS
		CLASS_DESC *desc;
		CLASS_PARAM *local;
		FUNC_DEBUG *debug;
		#endif
		}
	PACKED
	CLASS_LOAD;

enum {
	CO_EQUAL, CO_EQUALF, CO_EQUALO,
	CO_COMP, CO_COMPF, CO_COMPO,
	CO_ADD, CO_ADDF, CO_ADDO,
	CO_SUB, CO_SUBF, CO_SUBO,
	CO_MUL, CO_MULF, CO_MULO,
	CO_DIV, CO_DIVF, CO_DIVO,
	CO_POW, CO_POWF, CO_POWO,
	CO_NEG, CO_ABS, CO_FABS, CO_SGN,
	CO_STRENGTH
};

typedef
	struct _CLASS {                     // 32b 64b
		struct _CLASS *class;             //   4   8  Points at the 'Class' class !
		int ref;                          //   8  12  Reference count
		int count;                        //  12  16  Number of instanciated objects
		struct _CLASS *parent;            //  16  24  Inherited class
		char *name;                       //  20  32  Class name

		unsigned loaded : 1;              //          Class is loaded
		unsigned ready : 1;               //          Class is loaded and ready
		unsigned debug : 1;               //          Debugging information ?
		unsigned swap : 1;                //          Class endianness was swapped
		unsigned free_event : 1;          //          Must free class->event
		unsigned in_load : 1;             //          Class being loaded
		unsigned exit : 1;                //          Marker used by CLASS_exit
		unsigned auto_create : 1;         //          Automatically instanciated

		unsigned quick_array : 2;         //          Array accessor optimization type
		unsigned no_create : 1;           //          Cannot instanciate this class
		unsigned is_virtual : 1;          //          Virtual class (name beginning with a dot)
		unsigned enum_static : 1;         //          If class enumeration is static
		unsigned is_stream : 1;           //          If the class inherits stream
		unsigned global : 1;              //          If the class is in the global table
		unsigned error : 1;               //          Loading or registering the class has failed

		unsigned is_native : 1;           //          If the class is native (i.e. written in C/C++)
		unsigned is_observer : 1;         //          This is the Observer class
		unsigned is_struct : 1;           //          This class is a structure
		unsigned is_array : 1;            //          This class is an array
		unsigned is_array_of_struct : 1;  //          This class is an array of struct
		unsigned init_dynamic : 1;        //          If there is a special function to call at instanciation
		unsigned must_check : 1;          //          The class has a check function
		unsigned has_child : 1;           //          The class has an inherited child class

		unsigned unknown_static : 1;      //          If _unknown is static
		unsigned property_static : 1;     //          If _property is static
		unsigned has_convert : 1;         //          If the _convert interface is implemented
		unsigned has_operators : 1;       //          If the _operators interface is implemented
		unsigned is_simple : 1;           //          Class has no parent, no child, is not virtual, and has no 'check' function.
		unsigned has_free : 1;            //          The class has a free function
		unsigned is_test : 1;             //          The class is a test module
		unsigned less_than_3_18 : 1;      //  24  36  If bytecode version is strictly older than 3.18

		short n_desc;                     //  26  38  number of descriptions
		short n_event;                    //  28  40  number of events

		CLASS_DESC_SYMBOL *table;         //  32  48  class description
		ushort *sort;                     //  36  56  table sort

		CLASS_EVENT *event;               //  40  64  event description

		int (*check)();                   //  44  72  method for checking that an object is valid

		char *data;                       //  48  80  class file data for loaded class
		                                  //          or generated description for native class
		                                  //          or generated description for structures

		CLASS_LOAD *load;                 //  52  88  information about loaded class

		char *stat;                       //  56  96  static class data
		TYPE *signature;                  //  60 104  signatures address
		char *string;                     //  64 112  strings address

		uint size_stat;                   //  68 116  static class size
		uint size;                        //  72 120  dynamic class size
		uint off_event;                   //  76 124  offset of OBJECT_EVENT structure in the object

		unsigned new_method : 1;          //          if there is a special method to call when creating a new instance
		unsigned ready_method : 1;        //          if there is a ready method to call
		unsigned is_collection : 1;       //          if the class inherits Collection
		unsigned _reserved2 : 30;         //  80 128

		short special[16];                // 112 160  special functions index (_new, _free, ...)

		TYPE array_type;                  // 116 168  datatype of the contents if this class is an array class of objects
		struct _CLASS *array_class;       // 120 176  array of class
		struct _CLASS *astruct_class;     // 124 184  array of struct class

		void *instance;                   // 128 192  automatically created instance
		void **operators;                 // 132 200  arithmetic interface
		bool (*convert)();                // 136 208  convert method

		COMPONENT *component;             // 140 216  The component the class belongs to

		struct _CLASS *next;              // 148 224  next class
		}
	CLASS;

typedef
	struct {
		SYMBOL sym;
		CLASS *class;
		}
	PACKED
	CLASS_SYMBOL;

typedef
	enum {
		SPEC_NEW,
		SPEC_FREE,
		SPEC_GET,
		SPEC_PUT,
		SPEC_FIRST,
		SPEC_NEXT,
		SPEC_CALL,
		SPEC_UNKNOWN,
		SPEC_PROPERTY,
		SPEC_COMPARE,
		SPEC_ATTACH,
		SPEC_READY,
		SPEC_READ,
		SPEC_WRITE,
		SPEC_INVALID
		}
	CLASS_SPECIAL;

typedef
	enum {
		CQA_NONE = 0,
		CQA_ARRAY = 1,
		CQA_COLLECTION = 2,
		CQA_STRING = 3
		}
	CLASS_QUICK_ARRAY;

typedef
	enum
	{
		CF_DEBUG = 1
	}
	CLASS_FLAG;

typedef
	enum
	{
		CI_EXPORTED = 1,
		CI_AUTO_CREATE = 2,
		CI_OPTIONAL = 4,
		CI_NO_CREATE = 8,
		CI_HAS_FAST = 16,
		CI_TEST = 32
	}
	CLASS_INFO_FLAG;

#ifndef __GBX_CLASS_INIT_C
EXTERN CLASS *CLASS_Class;
EXTERN CLASS *CLASS_Enum;
EXTERN CLASS *CLASS_Symbol;

EXTERN CLASS *CLASS_Array;
EXTERN CLASS *CLASS_Collection;
EXTERN CLASS *CLASS_Stream;
EXTERN CLASS *CLASS_File;
EXTERN CLASS *CLASS_Stat;
EXTERN CLASS *CLASS_AppArgs;
EXTERN CLASS *CLASS_AppEnv;
EXTERN CLASS *CLASS_Application;
EXTERN CLASS *CLASS_Process;
EXTERN CLASS *CLASS_Component;
EXTERN CLASS *CLASS_Observer;
EXTERN CLASS *CLASS_Timer;
EXTERN CLASS *CLASS_BoxedString;

EXTERN CLASS *CLASS_BooleanArray;
EXTERN CLASS *CLASS_ByteArray;
EXTERN CLASS *CLASS_ShortArray;
EXTERN CLASS *CLASS_IntegerArray;
EXTERN CLASS *CLASS_SingleArray;
EXTERN CLASS *CLASS_FloatArray;
EXTERN CLASS *CLASS_DateArray;
EXTERN CLASS *CLASS_StringArray;
EXTERN CLASS *CLASS_ObjectArray;
EXTERN CLASS *CLASS_VariantArray;
EXTERN CLASS *CLASS_LongArray;
EXTERN CLASS *CLASS_PointerArray;
#endif


#define DO_ERROR ((void (*)()) -1)

#define CLASS_is_native(_class) ((_class)->is_native)
#define CLASS_is_struct(_class) ((_class)->is_struct)
#define CLASS_is_array(_class) ((_class)->is_array)
#define CLASS_is_array_of_struct(_class) ((_class)->is_array_of_struct)
#define CLASS_is_collection(_class) ((_class)->is_collection)

#define FUNCTION_is_static(func) ((func)->type & TF_STATIC)
//#define FUNCTION_is_native(_desc) (((uintptr_t)(_desc)->exec >> 16) != 0)
#define FUNCTION_is_native(_desc) ((_desc)->native)

#define FUNC_INIT_STATIC   0
#define FUNC_INIT_DYNAMIC  1


/* class.c */

void CLASS_init(void);
void CLASS_clean_up(bool silent);
void CLASS_exit(void);
CLASS *CLASS_get_list(void);

CLASS *CLASS_get(const char *name);
int CLASS_count(void);

char *CLASS_get_name(CLASS *class);

CLASS_DESC_SYMBOL *CLASS_get_symbol(CLASS *class, const char *name);
CLASS_DESC *CLASS_get_symbol_desc(CLASS *class, const char *name);

//short CLASS_get_symbol_index_kind(CLASS *class, const char *name, int kind, int kind2);
CLASS_DESC *CLASS_get_symbol_desc_kind(CLASS *class, const char *name, int kind, int kind2, TYPE type);
CLASS_DESC_METHOD *CLASS_get_special_desc(CLASS *class, int spec);

#define CLASS_get_desc(_class, _index) (((_class)->table[_index].desc))

CLASS_DESC_EVENT *CLASS_get_event_desc(CLASS *class, const char *name);

#define CLASS_find_symbol(_class, _name) SYMBOL_find((_class)->table, (_class)->sort, (_class)->n_desc, sizeof(CLASS_DESC_SYMBOL), TF_IGNORE_CASE, (_name), strlen((_name)), NULL)

int CLASS_find_symbol_with_prefix(CLASS *class, const char *name, const char *prefix);

CLASS *CLASS_look_do(const char *name, int len, bool global);
CLASS *CLASS_find_do(const char *name, bool global);

#define CLASS_look(_name, _len) CLASS_look_do(_name, _len, FALSE)
#define CLASS_find(_name) CLASS_find_do(_name, FALSE)

#define CLASS_look_global(_name, _len) CLASS_look_do(_name, _len, TRUE)
#define CLASS_find_global(_name) CLASS_find_do(_name, TRUE)

TABLE *CLASS_get_table(void);

bool CLASS_inherits(const CLASS *class, const CLASS *parent);

CLASS *CLASS_replace_global(const char *name);
CLASS *CLASS_check_global(CLASS *class);
CLASS *CLASS_find_export(const char *name, const char *global);

void CLASS_ref(void *object);
bool CLASS_unref(void *object, bool can_free);
void CLASS_free(void *object);

int CLASS_get_inheritance(CLASS *class, CLASS **her);

void CLASS_do_nothing();
int CLASS_return_zero();

void CLASS_sort(CLASS *class);

void CLASS_inheritance(CLASS *class, CLASS *parent);
void CLASS_make_description(CLASS *class, const CLASS_DESC *desc, int n_desc, int *first);
void CLASS_make_event(CLASS *class, int *first);
void CLASS_calc_info(CLASS *class, int n_event, int size_dynamic, bool all, int size_static);

void *CLASS_auto_create(CLASS *class, int nparam);

void CLASS_search_special(CLASS *class);

CLASS_DESC_SYMBOL *CLASS_get_next_sorted_symbol(CLASS *class, int *index);

int CLASS_can_be_used_like_an_array(CLASS *class);

void CLASS_create_array_class(CLASS *class);
CLASS *CLASS_get_array_class(CLASS *class);

void CLASS_create_array_of_struct_class(CLASS *class);
CLASS *CLASS_get_array_of_struct_class(CLASS *class);

int CLASS_sizeof(CLASS *class);

CLASS *CLASS_find_load_from(const char *name, const char *from);

void CLASS_translation_must_be_reloaded(void);

/* class_init.c */

void CLASS_init_native(void);
void CLASS_update_global(const CLASS *old_class, CLASS *new_class);

/* class_load.c */

TYPE CLASS_ctype_to_type(CLASS *class, CTYPE ctype);
int CLASS_sizeof_ctype(CLASS *class, CTYPE ctype);

void CLASS_load_real(CLASS *class);
#define CLASS_load(_class) \
({ \
	if (!((_class)->ready)) \
		CLASS_load_real(_class); \
})
void CLASS_run_inits(CLASS *class);
void CLASS_load_without_init(CLASS *class);

/* class_native.c */

CLASS *CLASS_register_class(GB_DESC *desc, CLASS *class);
CLASS *CLASS_register(GB_DESC *desc);

#define SET_IF_NULL(ptr, val)  if ((ptr) == NULL) (ptr) = (val)
/*
#define DO_NOTHING(ptr)  if ((ptr) == NULL) (ptr) = CLASS_do_nothing
#define RETURN_ZERO(ptr)  if ((ptr) == NULL) (ptr) = CLASS_return_zero
*/

#define CLASS_is_virtual(class)  (class->is_virtual)

#define CLASS_has_operator(_class, _op) (((void **)(_class)->operators)[_op] != NULL)
#define CLASS_get_operator_strength(_class) (((intptr_t *)(_class)->operators)[CO_STRENGTH])
#define CLASS_set_operator_strength(_class, _strength) (((intptr_t *)(_class)->operators)[CO_STRENGTH] = (_strength))

#define CLASS_is_loaded(_class) ((_class)->loaded)
#define CLASS_is_ready(_class) ((_class)->ready)

#endif /* _CLASS_H */
