
#ifndef __BASIC_H
#define __BASIC_H

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/*
 * DEBUG_ASSERT - allow debug_assert macro
 * ASSERT - allow assert macro
 * MEM_CHECK - allow control of allocation and deallocation of memory
 * ERR_MSGS - allow printing of error messages, based on condition testing
 */

//#define DEBUG_ASSERT
#define ASSERT
//#define MEM_CHECK
#define ERR_MSGS

/*
 * c_idx_not_exist - constant which represent not existing index
 * c_array_add - number constant which is used as cumulative value of dynamic array size
 */

const unsigned c_idx_not_exist = 0xffffffff;
const unsigned c_array_add = 64;

/*
 * macro debug_assert is used for testing in critical sections of code
 */
#ifdef DEBUG_ASSERT
#define debug_assert(COND)\
{\
   if (!(COND)) {\
      fprintf(stderr,"DEBUG_ASSERT: %s, line: %d\n",__FILE__,__LINE__);\
      throw;\
   }\
}
#else
#define debug_assert(COND)
#endif

/*
 * macro assert is used as temporary replacement for error testing statement
 */
#ifdef ASSERT
#define assert(COND)\
{\
   if (!(COND)) {\
      fprintf(stderr,"ASSERT: %s, line: %d\n",__FILE__,__LINE__);\
      throw;\
   }\
}
#else
#define assert(COND)
#endif

/*
 * setting of macros cmalloc and cfree, based on MEM_CHECK value
 */
#ifdef MEM_CHECK

/* --- definition of structure mce_struct_s ---
 * structure which stores report of every allocated block of memory, and 
 * watch over its proper release
 */

/*
 * record describing block of memory
 */
struct mce_block_s
{
   void *location;
   unsigned size;
   unsigned next_idx;
};

/*
 * structure which contain list of memory block records
 */
struct mce_struct_s
{
   unsigned size;
   unsigned data_start;
   unsigned empty_start;
   mce_block_s *data;

   inline void init();
   inline void clear(bool silent);
   void check(bool silent);
   void copy_resize(unsigned a_size);
   void *create_block(unsigned a_size);
   void delete_block(void *a_location);
   void print_hash(bool set);
};

/*
 * initialization of variables of memory control structure
 */
inline void mce_struct_s::init() 
{
   size = 0;
   data_start = c_idx_not_exist;
   empty_start = c_idx_not_exist;
   data = NULL;
}

/*
 * freeing of structure variables from memory, before free is
 * checked if every block of memory is freed
 */
inline void mce_struct_s::clear(bool silent)
{
   check(silent);

   if (data != NULL) {
      free(data);
   }

   init();
}

#define mc_init() mce.init();
#define mc_clear(SILENT) mce.clear(SILENT);

#define cmalloc(SIZE) mce.create_block(SIZE)
#define cfree(LOCATION) mce.delete_block(LOCATION)

extern mce_struct_s mce;

#else

#define mc_init()
#define mc_clear(SILENT)

#define cmalloc(SIZE) malloc(SIZE)
#define cfree(LOCATION) free(LOCATION)

#endif

/*
 * macro err_test setting based on value of ERR_MSGS
 */

#ifdef ERR_MSGS

/*
 * list of error messages types
 */

enum {
   c_ERR_NOT_ENOUGH_CL_PARAMETERS,
   c_ERR_CANNOT_OPEN_SOURCE_FILE,

   c_DP_ERR_FUN_NAME_EXCEED_MAX_SIZE,
   c_DP_UNIMPLEMENTED_CONST_NOTATION,
   c_P_ERR_IDENTIFIER_ALREADY_EXIST,
   c_P_ERR_CONTINUE_BREAK_OUTSIDE_OF_LOOP,
   c_P_ERR_RETURN_OUTSIDE_OF_FUNCTION,
   c_P_ERR_X_NOT_DEFINED,
   c_P_ERR_WRONG_ESCAPE_CODE,

   c_P_ERR_UNRECOGNIZABLE_TERMINAL,
   c_P_ERR_WRONG_PROGRAM_SYNTAX,

   c_DR_ERR_MUTEXT_OPERATION_UNIMPLEMENTED,
   c_DR_ERR_MUTEX_OPERATION_FAILED,
   c_DR_ERR_OPERATION_NOT_IMPLEMENTED,
   c_DR_ERR_EXCEED_MAX_STRING_SIZE,

   c_R_ERR_LOAD_BUFFER_OVERFLOW,
   c_R_ERR_EXPECTED_NUMBER_EXPRESSION,
   c_R_ERR_STRUCT_X_NOT_DEFINED,
   c_R_ERR_CANNOT_CREATE_THIS_OBJECT,
   c_R_ERR_NOT_ALLOWED_MULTITHREADING,
   c_R_ERR_NOT_COMPATIBLE_ARRAY_ELEMENT,
   c_R_ERR_WRONG_PARAM_EXPECTED_PARAM,
   c_R_ERR_UNKNOWN_X_OPERATION_CONSTANT,
   c_R_ERR_EXPECTED_WRITABLE,
   c_R_ERR_WRONG_PARAMETER,
   c_R_ERR_CANNOT_ASSING_TO_INDEXED,
   c_R_ERR_IDX_EXCEED_ARRAY_STRING_SIZE,

   c_R_ERR_LOCKED_MUTEX_DESTROY,
   c_R_ERR_USED_CONDITION_DESTROY,
   c_R_ERR_THREAD_FUNCTION_RETURN,
   c_R_ERR_IMAGE_OPERATION_RETURN,
};

/*
 * declaration of function which print error message
 */

void err_message(unsigned a_type,unsigned *parm_list);

#define err_test(COND,TYPE)\
{\
   if (!(COND)) {\
      err_message(TYPE,NULL);\
      mc_clear(true);\
      exit(0);\
   }\
}

#define err_test1(COND,TYPE,PARM1)\
{\
   if (!(COND)) {\
      unsigned parm_list[1];\
      parm_list[0] = PARM1;\
\
      err_message(TYPE,parm_list);\
      mc_clear(true);\
      exit(0);\
   }\
}

#define err_test2(COND,TYPE,PARM1,PARM2)\
{\
   if (!(COND)) {\
      unsigned parm_list[2];\
      parm_list[0] = PARM1;\
      parm_list[1] = PARM2;\
\
      err_message(TYPE,parm_list);\
      mc_clear(true);\
      exit(0);\
   }\
}

#define err_test3(COND,TYPE,PARM1,PARM2,PARM3)\
{\
   if (!(COND)) {\
      unsigned parm_list[3];\
      parm_list[0] = PARM1;\
      parm_list[1] = PARM2;\
      parm_list[2] = PARM3;\
\
      err_message(TYPE,parm_list);\
      mc_clear(true);\
      exit(0);\
   }\
}

#define err_test4(COND,TYPE,PARM1,PARM2,PARM3,PARM4)\
{\
   if (!(COND)) {\
      unsigned parm_list[4];\
      parm_list[0] = PARM1;\
      parm_list[1] = PARM2;\
      parm_list[2] = PARM3;\
      parm_list[3] = PARM4;\
\
      err_message(TYPE,parm_list);\
      mc_clear(true);\
      exit(0);\
   }\
}

#else

#define err_test(COND,TYPE) assert(COND);
#define err_test1(COND,TYPE,PARM1) assert(COND);
#define err_test2(COND,TYPE,PARM1,PARM2) assert(COND);
#define err_test3(COND,TYPE,PARM1,PARM2,PARM3) assert(COND);
#define err_test4(COND,TYPE,PARM1,PARM2,PARM3,PARM4) assert(COND);

#endif

#endif

