/************************************************************************
       -= SEER - C-Scripting engine v 0.93a =-
State      : under implementation
Author     : Przemyslaw Podsiadly
             email: ppodsiad@elka.pw.edu.pl
             WWW:   http://home.elka.pw.edu.pl/~ppodsiad
File:exclassc.c
Desc:same as excpp, but in C: interaction between C and objective script
Note:First see excpp!
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <seer.h>

#define MAN     0
#define WOMAN   1

#define NAMES_NUM       4
struct{
        char *name;
        int sex;
} names[NAMES_NUM]=
{{"Ian",MAN},
{"Iola",WOMAN},
{"Turlin",MAN},
{"Yenefer",WOMAN}};

//our "class"...
typedef struct NPC{
        char name[64];
        int sex;
        int strength, wisdom, hp;
} NPC;

//the "constructor"
void NPCconstruct(NPC *npc){
         int n;
         srand(time(NULL));
         n=rand()%NAMES_NUM;
         strcpy(npc->name,names[n].name);
         npc->sex=names[n].sex;
         //other (un)important stuff follows...
        }
//and methods...
char *get_name(NPC *_this) {return _this->name;}
int get_sex(NPC *_this) {return _this->sex;}

//This one exports _important_ member functions
void NPCexport()
{//NOTE: you use NPC.member_name as a function name,
 //not NPC::member_name;
 scAdd_Member_Symbol("NPC.get_name",get_name);
 scAdd_Member_Symbol("NPC.get_sex",get_sex);
}

char *header=
//This is the internal header.
//Something like 'stdio' internal header in seerc, but smaller
"#ifndef HEADER_INCLUDED                                                \n"
"#include stdlib							\n"
"import int printf(char *,...);                                         \n"
"import int scanf(char *,...);                                          \n"

"#define MAN    0                                                       \n"
"#define WOMAN  1                                                       \n"
// Now some more things : the NPC class
// Hey, but we're in C and have no NPC class, only a struct.
//But we create below our custom dispatcher, which will convert
//script's calls to members to standard C functions with 'this'
//as first parameter. Quite a clean hack :-)
// Our dispatcher will have number 1, so 'import(1)' instead of 'import'
//is required.

//"#define c_import import(1)                                             \n"

"struct NPC{                                                            \n"
"       char name[64];                                                  \n"
"       int sex;                                                        \n"
"       int strength, wisdom, hp;                                       \n"
"       c_import char *get_name();                                      \n"
"       c_import int get_sex();                                         \n"
"};                                                                     \n"

//let's force the script to export these,
//they should be implemented further in script:
"export On_Talk;                                                        \n"
"void On_Talk(NPC *with);                                               \n"
"export Init;                                                           \n"
"void Init(char *name);                                                 \n"

"#define HEADER_INCLUDED                                                \n"
"#endif                                                                 \n"
;

/*
This function tells SeeR addresses of functions and variables it can
use (import). If script imports symbol you don't tell the address SeeR
will complain : unresolved reference.Here we use only printf, but we
can add also scanf. In this way you also add variables.
We add also the header files and export class NPC.
*/
void Init_Externals()
{
 scAdd_External_Symbol("printf",printf);
 scAdd_External_Symbol("scanf",scanf);
//let's export the class
 NPCexport();

 scAdd_Internal_Header("theHeader",header);
}

//It converts any C++ member call to C call.
//The dispatcher work like this:
// when you call any exported function, the required
// dispatcher is called.
// If this is a member function, the first parameter is
// 'this'.
// All parameters are stored in order one by one in params.
// The paramcount is size in bytes of them.
// The options tell us, if this function return double, or
// something 32bit long (int, pointer, char, float ...).
// It tells us also if the function is class-member or not.
// Here we only turn off the class-member flag. EASY :-)
//Now we can cooperate between objective script and
//non-objective C.
void MyDispatcher
(int *result,void *function,int *params,int paramcount,unsigned int options)
{
  if (options&scDispatch_Member)
  {//C++ call - convert to typical C call, so 'this' is the first param.
        options^=scDispatch_Member;
  }
  scStandardDispatcher(result,function,params,paramcount,options);
}

/*** THE MAIN ***************************************/
int main()
{
//scrpt will contain the compiled script
 scScript scrpt;

//and this is the instance of script, which we can call
//it contains all the data from script.
 scInstance *prog;

 NPC npc;

//let's give the script some functions of ours
 Init_Externals();

//let's add the dispatcher as the 1st one (the default one is the 0);
//all functions with "import(1)" will use this one.
 scSeeR_Set(scSeeR_Dispatcher,1,MyDispatcher);

//when in SeeR in debug mode, here it will store all debugging data
 scOpen_Debug("exclassc.dbg");//start debugging

//compile example script : "excpp.sc", this won't need any changes!
 scrpt=scCompile_File("excpp.sc");

//Error?
 if (scErrorNo)
   {
      printf("%s\n> %s\n",scErrorMsg,scErrorLine);exit(scErrorNo);
   }

//create instance of the script
 prog=scCreate_Instance(scrpt,"");

//Error?
 if (!prog)
  {
   printf("Error creating instance!\n%s\n",scErrorMsg);
   return 1;
  }

//now, let's execute the function (here 'main' has no arguments)
 scCall_Instance(prog,scGet_Symbol(prog,"Init"),"Igor the Shopkeeper");
//Error?
 if (scErrorNo)
      printf("%s\n",scErrorMsg);

 NPCconstruct(&npc);//initialize object npc;we've got to do this manually in C

//let's call the script... sorry - instance
 scCall_Instance(prog,scGet_Symbol(prog,"On_Talk"),&npc);
 if (scErrorNo)
      printf("%s\n",scErrorMsg);

//let's delete instance and script
 scFree_Instance(prog);
 free(scrpt);

//close debugging - close the debug file also
 scClose_Debug();

 return 0;
}

