#include "allobjects.h"
#include "modsupport.h"
#include "struc/starpasr.h"
#include "struc/datablk.h"
#include "struc/pasrmain.h"
#include "struc/iterator.h"

/***************************************
  star object
****************************************/

typedef struct {
  OB_HEAD
  id _object;
  object *_attr;
} starobject;

/******************************************************
  starobj_get & starobj_getfrom behave as function
  return value are expected from these two methods
*******************************************************/

static object*
starobj_get(so, args)
     starobject *so;
     object *args;
{
  id anObject;
  char *message, *arg1, *arg2; 
  if (!getargs(args, "(sss)", &message, &arg1, &arg2))
    return NULL;
}

static object*
starobj_getfrom(so, args)
     starobject *so;
     object *args;
{
}

/********************************************************
  starobj_receive & starobj_send behave as procedure
  no return value will be expected from these two methods
*********************************************************/
  
static object*
starobj_receive(so, args)
     starobject *so;
     object *args;
{
  INCREF(None);
  return None;  
}

static object*
starobj_send(so, args)
     starobject *so;
     object *args;
{
  INCREF(None);
  return None;  
}

static struct methodlist starobj_methods[] = {
  {"get", starobj_get},
  {"getfrom", starobj_getfrom},
  {"receive", starobj_receive},
  {"send", starobj_send},
  {NULL, NULL}
};

static void
starobj_dealloc(so)
     starobject* so;
{
  DEL(so);
}

static object*
starobj_getattr(so, name)
     starobject* so;
     char* name;
{
  object *v = NULL;
  if (so->_object == NULL) {
    return NULL;
  }
  if (strcmp(name, "__dict__") == 0) {
    v = so->_attr;
    if (v == NULL)
      v = None;
  }
  else if (so->_attr != NULL) {
    v = dictlookup(so->_attr, name);
  }
  if (v != NULL) {
    INCREF(v);
    return v;
  }
  return findmethod(starobj_methods, (object *)so, name);
}

static int
starobj_setattr(so, name, v)
     starobject* so;
     char* name;
     object* v;
{
  if (so->_attr == NULL) {
    so->_attr = newdictobject();
    if (so->_attr == NULL)
      return -1;
  }
  if (v == NULL) {
    int rv = dictremove(so->_attr, name);
    if (rv < 0)      
	 err_setstr(AttributeError,
		 "delete non-existing text object attribute");
    return rv;
  }
  else
    return dictinsert(so->_attr, name, v);
}


static typeobject startype = {
  OB_HEAD_INIT(&Typetype)
  0,
  "star",
  sizeof(starobject),
  0,
  (destructor)starobj_dealloc,
  0,
  (getattrfunc)starobj_getattr,
  (setattrfunc)starobj_setattr,
  0,
  0,
};

static starobject *newstarobject(obj)
     id obj;
{
  starobject *so=NEWOBJ(starobject, &startype);
  if (so) {
    so->_object=obj;
    so->_attr=NULL;
  }
  return so;
}


/****************************************
  stardictionary object
*****************************************/

typedef struct {
  OB_HEAD
  id _dictionary;
  object *_attr;
} stardictionaryobject;

void listCateHierarchy(id dictionary, const char* category, 
		       int level, const char* relation)
{
  id list, iter;
  int i;
  for (i=0; i<level; i++) printf("    ");
  printf("%s\n", category);
  if (list=[dictionary getAllCategoriesAre:relation 
	    ofCategory :category]) {
    level++;
    iter=[[WDCListIterator alloc] initFrom: list];
    while ([iter goNext]){
      listCateHierarchy(dictionary, [[[[iter getCurrentObject] getContent] 
				     getValue] cStringValue], 
		       level, relation);
    }
    [iter free];
    [list free];
  }
}

static object*
stardictionary_hier(sf, args)
     stardictionaryobject* sf;
     object* args;
{
  char *currentcate, *relation1, *relation2;
  id dictionary=sf->_dictionary, categories, iter=0;
  if (!getargs(args, "s", &relation1))
    return NULL;
  if (categories=[dictionary getAllUnique:"_category"])
    iter=[[WDCListIterator alloc] initFrom:[categories sortContents]];
  if (!strcmp(relation1, "_list_link_parent"))
    relation2="_list_link_child";
  else
    relation2="_list_link_parent";
  while ([iter goNext]){
    currentcate=(char*)[[iter getCurrentObject] cStringValue];
    if (![dictionary getAllDataItems: relation2 ofCategory :currentcate])
      listCateHierarchy(dictionary, currentcate, 0, relation1);
  }
  [categories free];
  [iter free];
  INCREF(None);
  return None;  
}


void verifyCateHierarchy(id dictionary, id categoryobj, int level, 
			 const char* rel1, BOOL reversable, const char* rel2)
{
  id list, templist, iter, currentcate;
  const char* category=[[categoryobj getValue] cStringValue];
  int i;
  for (i=0; i<level; i++) printf("    ");
  printf("%s %s\n", category, reversable?"":"----------?");
  if (list=[dictionary getAllCategoriesAre:rel1 ofCategory :category]) {
    level++;
    iter=[[WDCListIterator alloc] initFrom: list];
    while ([iter goNext]){
      currentcate=[[iter getCurrentObject] getContent];
      templist=[dictionary getAllCategoriesAre:rel2 
		ofCategory :[[currentcate getValue] cStringValue]];
      reversable=[templist isExist:categoryobj];
      [templist free];
      verifyCateHierarchy(dictionary, currentcate, 
			  level, rel1, reversable, rel2);
    }
    [iter free];
    [list free];
  }
}

static object*
stardictionary_verify(sf, args)
     stardictionaryobject* sf;
     object* args;
{
  char *relation1, *relation2;
  id dictionary=sf->_dictionary, categories, iter=0, currentcate;
  if (!getargs(args, "s", &relation1))
    return NULL;
  if (categories=[dictionary getAllUnique:"_category"])
    iter=[[WDCListIterator alloc] initFrom:[categories sortContents]];
  if (!strcmp(relation1, "_list_link_parent"))
    relation2="_list_link_child";
  else
    relation2="_list_link_parent";
  currentcate=[[WDCStarItemAssocValue alloc] setName:"_category"]; 
  while ([iter goNext]){
    [currentcate setValue:[iter getCurrentObject]];
    if (![dictionary getAllDataItems: relation2 
	  ofCategory :[[currentcate getValue] cStringValue]])
      verifyCateHierarchy(dictionary, currentcate, 0, relation1, 1, relation2);
  }
  [[currentcate setValue:0] free];
  [categories free];
  [iter free];
  INCREF(None);
  return None;  
}

static object*
stardictionary_cateverify(sf, args)
     stardictionaryobject* sf;
     object* args;
{
  char *relation1, *relation2; 
  const char* catestring;
  id dictionary=sf->_dictionary, cate1, cate2, cate3, iter1=0, iter2, 
  currentcate;
  if (!getargs(args, "s", &relation1))
    return NULL;
  if (cate1=[dictionary getAllUnique:"_category"])
    iter1=[[WDCListIterator alloc] initFrom:[cate1 sortContents]];
  iter2=[WDCListIterator alloc];
  if (!strcmp(relation1, "_list_link_parent"))
    relation2="_list_link_child";
  else
    relation2="_list_link_parent";
  currentcate=[[WDCStarItemAssocValue alloc] setName:"_category"]; 
  while ([iter1 goNext]){
    [currentcate setValue:[iter1 getCurrentObject]];
    catestring=[[currentcate getValue] cStringValue];
    printf("%s\n", catestring);
    if (cate2=[dictionary getAllCategoriesAre: relation1 
	       ofCategory :catestring]){
      [iter2 initFrom:cate2];
      while ([iter2 goNext]){
	catestring=[[[[iter2 getCurrentObject] 
		      getContent] getValue] cStringValue];
	cate3=[dictionary getAllCategoriesAre:relation2 
	       ofCategory :catestring];
	printf("    %s %s\n", catestring, 
	       [cate3 isExist: currentcate]?"":"--------------?");
	[cate3 free];
      }
      [cate2 free];
    }
  }
  [[currentcate setValue:0] free];
  [cate1 free];
  [iter1 free];
  [iter2 free];
  INCREF(None);
  return None;  
}

static struct methodlist stardictionary_methods[] = {
  {"hierarchy", stardictionary_hier},
  {"verifyhierarchy", stardictionary_verify},
  {"verifycategory", stardictionary_cateverify},
  {NULL, NULL}
};

static void
stardictionary_dealloc(sf)
     stardictionaryobject* sf;
{
  DEL(sf);
}

static object*
stardictionary_getattr(sf, name)
     stardictionaryobject* sf;
     char* name;
{
  object *v = NULL;
  if (sf->_dictionary == NULL) {
    return NULL;
  }
  if (strcmp(name, "__dict__") == 0) {
    v = sf->_attr;
    if (v == NULL)
      v = None;
  }
  else if (sf->_attr != NULL) {
    v = dictlookup(sf->_attr, name);
  }
  if (v != NULL) {
    INCREF(v);
    return v;
  }
  return findmethod(stardictionary_methods, (object *)sf, name);
}

static int
stardictionary_setattr(sf, name, v)
     stardictionaryobject* sf;
     char* name;
     object* v;
{
  if (sf->_attr == NULL) {
    sf->_attr = newdictobject();
    if (sf->_attr == NULL)
      return -1;
  }
  if (v == NULL) {
    int rv = dictremove(sf->_attr, name);
    if (rv < 0)
	 err_setstr(AttributeError,
		 "delete non-existing text object attribute");
    return rv;
  }
  else
    return dictinsert(sf->_attr, name, v);
}


static typeobject stardictionarytype = {
  OB_HEAD_INIT(&Typetype)
  0,
  "stardictionary",
  sizeof(stardictionaryobject),
  0,
  (destructor)stardictionary_dealloc,
  0,
  (getattrfunc)stardictionary_getattr,
  (setattrfunc)stardictionary_setattr,
  0,
  0,
};

static stardictionaryobject *newstardictionaryobject(sf)
     id sf;
{
  stardictionaryobject *so=NEWOBJ(stardictionaryobject, &stardictionarytype);
  if (so) {
    so->_dictionary=sf;
    so->_attr=NULL;
  }
  return so;
}


/****************************************
  starfile object
*****************************************/

typedef struct {
  OB_HEAD
  id _file;
  object *_attr;
} starfileobject;

static object*
starfile_grep(sf, args)
     starfileobject* sf;
     object* args;
{
  id list, iter, iter1, current;
  char *dataname, *itemname;  
  if (!getargs(args, "(ss)", &dataname, &itemname))
    return NULL;
  if (list=[[(sf->_file) getDataItem:dataname] getAllDataItems :itemname]){
    iter=[[WDCListIterator alloc] initFrom: list];
    iter1=[WDCListIterator alloc];
    while ([iter goNext]){
      current=[[[iter getCurrentObject] getContent] getValue];
      if ([current isMemberOfClassNamed:"WDCString"])
	printf("%s\n", [current cStringValue]);
      else if ([current isKindOfClassNamed:"WDCString"]){
	[iter1 initFrom: current];
	while ([iter1 goNext])
	  printf("%s\n", [[iter1 getCurrentObject] cStringValue]);
      }
    }
    [list free];
    [iter free];
    [iter1 free];
  }
  else
    printf("Can not locate such information\n");
  INCREF(None);
  return None;  
}

static object*
starfile_name(sf, args)
     starfileobject* sf;
     object* args;
{
  printf("%s\n", [sf->_file getName]);
  INCREF(None);
  return None;  
}

static object*
starfile_listdatablocks(sf, args)
     starfileobject* sf;
     object* args;
{
  id datablock;
  for (datablock=[sf->_file getValue];
       datablock;
       datablock=[datablock nextLink])
    printf("%s\n", [datablock getName]);
  INCREF(None);
  return None;  
}

void starfilelistitem(id listofitem, int level)
{
  int i;
  while (listofitem){
    if (![[listofitem getNameString] compareCString:"loop_" ignoreCase:YES])
      starfilelistitem([[listofitem getValue] getContent], level+1);
    else {
      for (i=1; i<level; i++) printf("    ");
      printf("%s\n", [listofitem getName]);
    }
    listofitem=[listofitem nextLink];
  }
}

static object*
starfile_listdataitems(sf, args)
     starfileobject* sf;
     object* args;
{
  char* dataname;
  if (!getargs(args, "s", &dataname))
    return NULL;
  starfilelistitem([[sf->_file getDataItem:dataname] getValue], 1);
  INCREF(None);
  return None;  
}

void starfilelistitemvalue(id item, char** name, int level, char* line){
  id list=[item getAllItemsNamed:name[0]], iter, current;
  char newline[150];
  level--;
  if (list){
    iter=[[WDCListIterator alloc] initFrom:list];
    while ([iter goNext]){
      current=[[iter getCurrentObject] getContent];
      sprintf(newline, "%s %s", line, [[current getValue] cStringValue]);
      if (!level) printf("%s\n", newline);
      else starfilelistitemvalue(current, &name[1], level, newline);
    }
    [list free];
    [iter free];
  }
  else
    printf("%s\n", line);
}

static object*
starfile_tuple(sf, args)
     starfileobject* sf;
     object* args;
{
  char *dataname, *name[6];
  int numofcol;
  
  if (getargs(args, "(sssssss)", 
	      &dataname, &name[0], &name[1], &name[2], 
	      &name[3], &name[4], &name[5])){
    numofcol=6;
  }
  else {
    err_clear();
    if (getargs(args, "(ssssss)", 
		&dataname, &name[0], &name[1], 
		&name[2], &name[3], &name[4])){
      numofcol=5;
    }
    else {
      err_clear();
      if (getargs(args, "(sssss)", 
		  &dataname, &name[0], &name[1], &name[2], &name[3])){
	numofcol=4;
      }
      else {
	err_clear();
	if (getargs(args, "(ssss)", &dataname, &name[0], &name[1], &name[2])){
	  numofcol=3;
	}
	else {
	  err_clear();
	  if (getargs(args, "(sss)", &dataname, &name[0], &name[1])){
	    numofcol=2;
	  }
	  else
	    return NULL;
	}
      }
    }
  }
  starfilelistitemvalue([[sf->_file getDataItem:dataname] getValue], 
			name, numofcol, "");
  INCREF(None);
  return None;  
}

static struct methodlist starfile_methods[] = {
  {"grep", (method)starfile_grep},
  {"name", (method)starfile_name},
  {"listdatablocks", (method)starfile_listdatablocks},
  {"listdataitems", (method)starfile_listdataitems},
  {"tuple", (method)starfile_tuple},
  {NULL, NULL}
};

static void
starfile_dealloc(sf)
     starfileobject* sf;
{
  DEL(sf);
}

static object*
starfile_getattr(sf, name)
     starfileobject* sf;
     char* name;
{
  object *v = NULL;
  if (sf->_file == NULL) {
    return NULL;
  }
  if (strcmp(name, "__dict__") == 0) {
    v = sf->_attr;
    if (v == NULL)
      v = None;
  }
  else if (sf->_attr != NULL) {
    v = dictlookup(sf->_attr, name);
  }
  if (v != NULL) {
    INCREF(v);
    return v;
  }
  return findmethod(starfile_methods, (object *)sf, name);
}

static int
starfile_setattr(sf, name, v)
     starfileobject* sf;
     char* name;
     object* v;
{
  if (sf->_attr == NULL) {
    sf->_attr = newdictobject();
    if (sf->_attr == NULL)
      return -1;
  }
  if (v == NULL) {
    int rv = dictremove(sf->_attr, name);
    if (rv < 0)
	 err_setstr(AttributeError,
		 "delete non-existing text object attribute");
    return rv;
  }
  else
    return dictinsert(sf->_attr, name, v);
}


static typeobject starfiletype = {
  OB_HEAD_INIT(&Typetype)
  0,
  "starfile",
  sizeof(starfileobject),
  0,
  (destructor)starfile_dealloc,
  0,
  (getattrfunc)starfile_getattr,
  (setattrfunc)starfile_setattr,
  0,
  0,
};

static starfileobject *newstarfileobject(sf)
     id sf;
{
  starfileobject *so=NEWOBJ(starfileobject, &starfiletype);
  if (so) {
    so->_file=sf;
    so->_attr=NULL;
  }
  return so;
}

/* STAR module */

static object *
star_fileimport(self, args)
     object *self;
     object *args;
{
  char* filename;
  if (!getargs(args, "s", &filename))
    return NULL;
  [starParser parseStarFile: filename];
  INCREF(None);
  return None;
}

static object *
star_dicimport(self, args)
     object *self;
     object *args;
{
  char* filename;
  if (!getargs(args, "s", &filename))
    return NULL;
  [starParser parseDictionary: filename];
  INCREF(None);
  return None;
}

static object *
star_getdic(self, args)
     object *self;
     object *args;
{
  char* filename;
  id dictionary;
  stardictionaryobject * so=NULL;
  if (getargs(args, "s", &filename)){
    if (dictionary=[starParser getDictionaryNamed: filename]) {
      so=newstardictionaryobject(dictionary);
    }
    else
      err_setstr(NameError, "No such dictionary");
  }
  return (object*)so;
}

static object *
star_getfile(self, args)
     object *self;
     object *args;
{
  char* filename;
  id starfile;
  starfileobject * so=NULL;
  if (getargs(args, "s", &filename)){
    if (starfile=[starParser getStarFileNamed: filename]){
      so=newstarfileobject(starfile);
    }
    else
      err_setstr(NameError, "No such file\n");
  }
  return (object*)so;  
}

static object *
star_listfiles(self, args)
     object *self;
     object *args;
{
  id list=[starParser getStarFileList];
  id iter=[[WDCListIterator alloc] initFrom:list];
  while ([iter goNext])
    printf("%s\n", [[[iter getCurrentObject] getContent] getName]);
  [list free];
  [iter free];
  INCREF(None);
  return None;
}

static object *
star_grep(self, args)
     object *self;
     object *args;
{
  id list, iter, iter1, current;
  char *filename, *dataname, *itemname;
  object *file=NULL, *filenameobj, *datanameobj, *itemnameobj, *tuple;
  if (!getargs(args, "(sss)", &filename, &dataname, &itemname))
    return NULL;
  filenameobj=newstringobject(filename);
  if (file=star_getfile(0, filenameobj)){
    datanameobj=newstringobject(dataname);
    itemnameobj=newstringobject(itemname);
    tuple=newtupleobject(2);
    settupleitem(tuple, 0, datanameobj);
    settupleitem(tuple, 1, itemnameobj);
    starfile_grep(file, tuple);
    DECREF(file);
    DECREF(datanameobj);
    DECREF(itemnameobj);
    DECREF(tuple);
  }
  else
    printf("Can not locate such file\n");
  DECREF(filenameobj);
  INCREF(None);
  return None;
} 

static struct methodlist star_methods[]={
  {"fileimport", star_fileimport},
  {"dictionaryimport", star_dicimport},
  {"grep", star_grep},
  {"getfile", star_getfile},
  {"getdictionary", star_getdic},
  {"listfiles", star_listfiles},
  {NULL, NULL}
};

void
initstar()
{
  starParser=[WDCStarParser alloc];
  (void) initmodule("star", star_methods);
}

