//
// Copyright 2003, Juan Segarra Flor
//
// This is part of videocache
//
// Videocache 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 of the License, or
// (at your option) any later version.
//
// Videocache 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 Foobar; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//

#ifndef CACHE_TREE
#include "cachetree.h"
#endif

int cachetree::getlinknodeparent(int nid) {
  t_node* node;

  node=searchnode(nid);
  return node->linkid;
}

int cachetree::getnodedepth(int node) {
  t_node *hor,*ver;
  int level=0;
  int found=0;

  hor=children;
  while ((!found)&&(hor!=NULL)) {
    ver=hor;
    level=0;
    while ((!found)&&(ver!=NULL)) {
      if (ver->id==node)
	found=1;
      else {
	ver=ver->parent;
	level++;
      }
    }
    hor=hor->nextchild;
  }
  if (found)
    return level;
  else {
    cerr<<"getnodedepth: No such node "<<node<<endl;
    exit(-2);
  }
}


int cachetree::getlinkdepth(int link) {
  t_node *hor,*ver;
  int level=0;
  int found=0;

  hor=children;
  while ((!found)&&(hor!=NULL)) {
    ver=hor;
    level=0;
    if (ver->userlinkid==link)
      found=1;
    else {
      while ((!found)&&(ver!=NULL)) {
	level++;
	if (ver->linkid==link)
	  found=1;
	else
	  ver=ver->parent;
      }
    }
    hor=hor->nextchild;
  }
  if (found)
    return level;
  else {
    cerr<<"getlinkdepth: No such link "<<link<<endl;
    exit(-2);
  }
}

int *cachetree::getbestroute(int *nodelist,long user) {
  t_node *leafcache,*aux;
  int c1,links,found,*result,leafid;

  leafid=getnodeid(1,(user/users_each_node)+1);
  leafcache=searchnode(leafid);
  aux=leafcache;
  found=0;
  links=1;
  while (!found) {
    c1=0;
    while ((nodelist[c1]!=aux->id)&&(nodelist[c1]!=-1))
      c1++;
    if (nodelist[c1]==aux->id)
      found=1;
    else {
      links++;
      aux=aux->parent;
      if (aux==NULL) {
	cerr<<"getbestroute: Not found route from node "<<leafid<<" to any node in list"<<endl;
	c1=0;
	while (nodelist[c1]!=-1) {
	  cerr<<nodelist[c1]<<endl;
	  c1++;
	}
	printtree();
	exit(-2);
      }
    }
  }
  //cerr<<"node found"<<endl;
  if (found) {
    result=(int*)calloc(links+1,sizeof(int));
    if (result==NULL) {
      cerr<<"getbestroute: Not enough memory for getting route"<<endl;
      exit(-1);
    }
    aux=leafcache;
    result[0]=aux->userlinkid;
    for (c1=1;c1<links;c1++) {
      result[c1]=aux->linkid;
      aux=aux->parent;
    }
    result[links]=-1;
    return result;
  }
  else {
    cerr<<"getbestroute: Route not found"<<endl;
    exit(-2);
  }
}

int *cachetree::getcachesinlevel(int ndepth) {
  int *result;
  int numcaches,c1;
  t_node *aux;

  numcaches=getnodesindepth(ndepth);
  if (ndepth<1 || ndepth>depth) {
    cerr<<"getcachesinlevel: No such deph level"<<endl;
    exit(-2);
  }
  aux=children;
  for (c1=1;c1<ndepth;c1++)
    aux=aux->parent;
  result=(int*)calloc(numcaches+1,sizeof(int));
  if (result==NULL) {
    cerr<<"getcachesinlevel: Not enough memory for getting cache ids"<<endl;
    exit(-1);
  }
  for (c1=0;c1<numcaches;c1++) {
    result[c1]=aux->id;
    aux=aux->nextchild;
  }
  result[numcaches]=-1;
  return result;
}
  

t_node *cachetree::searchnode(int nid) {
  t_node *hor,*ver=NULL;
  int found=0;

  hor=children;
  while ((!found)&&(hor!=NULL)) {
    ver=hor;
    while ((!found)&&(ver!=NULL)) {
      if (ver->id==nid)
	found=1;
      else
	ver=ver->parent;
    }
    hor=hor->nextchild;
  }
  if (found)
    return ver;
  else {
    cerr<<"searchnode: No such node "<<nid<<endl;
    exit(-2);
  }
}


int cachetree::getnodesindepth(int ndepth) {
  int treedepth;
  
  treedepth=depth-ndepth+1;
  return (int)(pow(nodechildren,treedepth-1));
}

int cachetree::getnodeid(int ndepth,int numnode) {
  t_node *aux;
  int c1;

  if (ndepth<1 || ndepth>depth) {
    cerr<<"getnodeid: No such deph level"<<endl;
    exit(-2);
  }
  aux=children;
  for (c1=1;c1<ndepth;c1++)
    aux=aux->parent;
  if (numnode<1 || numnode>getnodesindepth(ndepth)) {
    //cerr<<"getnodeid: No such node "<<numnode<<endl;
    //exit(-2);
    return -1;
  }
  for (c1=1;c1<numnode;c1++)
    aux=aux->nextchild;
  return aux->id;
}

/* recursive tree creator */
void cachetree::createson(t_node* parent,int nchild,int* id,int* linkid,
		     int ndepth,t_node**nextson) {
  t_node *aux;
  int c1;

  //cerr<<"creating child "<<*id<<endl;
  aux=new(t_node);
  if (aux==NULL) {
    cerr<<"createson: Not enough memory to create tree."<<endl;
    exit(-1);
  }
  children=aux;
  aux->id=(*id)++;
  aux->linkid=(*linkid)++;
  aux->parent=parent;
  aux->nextchild=nextson[depth-ndepth];
  nextson[depth-ndepth]=aux;
  if (ndepth!=1) {// cache node not next to users
    aux->userlinkid=-1;
    for (c1=0;c1<nchild;c1++) {
      createson(aux,nchild,id,linkid,ndepth-1,nextson);
    }
  }
  else
    aux->userlinkid=(*linkid)++;
}
    
/* constructor: creates the tree */
cachetree::cachetree(int ndepth,int nchild,int nusers) {
  int id=1,
    linkid=0,
    c1;
  t_node *previous[ndepth];

  cerr<<"Creating cache tree... "<<flush;
  depth=ndepth;
  users_each_node=nusers;
  for (c1=0;c1<depth;c1++)
    previous[c1]=NULL;
  nodechildren=nchild;
  parent=new(t_node);
  if (parent==NULL) {
    cerr<<"cachetree: Not enough memory to create tree."<<endl;
    exit(-1);
  }
  parent->id=id++;
  parent->linkid=-1;
  parent->parent=NULL;
  parent->nextchild=NULL;
  if (depth>1) {
    parent->userlinkid=-1;
    for (c1=0;c1<nchild;c1++) {
      createson(parent,nodechildren,&id,&linkid,depth-1,previous);
    }
  }
  else if (depth==1)
    parent->userlinkid=linkid++;
  else {
    cerr<<"cachetree: Trying to create tree with depth "<<depth<<endl;
    exit(-2);
  }
  maxlinks=linkid;
  cerr<<"done"<<endl;
}


void cachetree::printtree() {
  t_node *hor,*ver;
  int found=0,nid=-1;

  hor=children;
  while ((!found)&&(hor!=NULL)) {
    ver=hor;
    while ((!found)&&(ver!=NULL)) {
      if (ver->id==nid)
	found=1;
      else {
	cout<<"-"<<ver->id;
	ver=ver->parent;
      }
    }
    cout<<endl;
    hor=hor->nextchild;
  }
}
void cachetree::printlinks() {
  t_node *hor,*ver;

  hor=children;
  while (hor!=NULL) {
    ver=hor;
    while (ver!=NULL) {
      if (ver->userlinkid!=-1)
	cout<<">-"<<ver->userlinkid;
      cout<<".-"<<ver->linkid;
      ver=ver->parent;
    }
    cout<<endl;
    hor=hor->nextchild;
  }
}  

/*int main() {
  cachetree hola(5,2);
  
  hola.printtree();
  }*/
