//
// 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
//

#include "videos.h"

void videos::getlastvideopartinlevel(int level,cachetree &tree,int&lastvideo,int&lastpart) {
  int *cacheids,c1;
  int firstcache;
  t_videopart* video=videolist;
  char notfound=1;

  if (FILLALGORITHM!=WEIGHTEDPARTS) {
    cerr<<"Reallocation works only with a weightedParts filling algorithm"<<endl;
    exit(-2);
  }

  // get a cacheid from level
  cacheids=tree.getcachesinlevel(level);
  c1=0;
  while (cacheids[c1]!=-1)
    c1++;
  if (c1==0) {
    cerr<<"No caches in level "<<level<<endl;
    exit(-2);
  } 
  else
    firstcache=cacheids[c1-1];
  free(cacheids);
  // look for videos located at cacheid
  c1=1;
  while ((video!=NULL)&&(notfound)) {
    if (video->location->nodeid!=firstcache){
      video=video->nextvideo;
      c1++;
    }
    else
      notfound=0;
  }
  if (notfound) {
    cerr<<"No videos found in level "<<level<<" of the tree"<<endl;
    exit(-2);
  }
  notfound=1;
  while ((video->nextvideo!=NULL)&&(notfound)) {
    if (video->nextvideo->location->nodeid==firstcache){
      video=video->nextvideo;
      c1++;
    }
    else
      notfound=0;
  }
  // now we know las video in level is c1
  lastvideo=c1;
  // test the part
  notfound=1;
  c1=1;
  while ((video->nextpart!=NULL)&&(notfound)) {
    if (video->nextvideo->location->nodeid==firstcache){
      video=video->nextpart;
      c1++;
    }
    else
      notfound=0;
  }
  lastpart=c1;
}

int *videos::getlocation(int video,int part) {
  t_videopart *aux;
  int c1=0,*result;
  t_place *loc;

  aux=getvideopart(video,part);
  loc=aux->location;
  while (loc!=NULL) {
    c1++;
    loc=loc->next;
  }
  //cerr<<c1<<" locations found"<<endl;
  result=(int*)calloc(c1+1,sizeof(int));
  if (result==NULL) {
    cerr<<"Not enough memory for getting locations"<<endl;
    exit(-1);
  }
  loc=aux->location;
  c1=0;
  while (loc!=NULL) {
    result[c1]=loc->nodeid;
    //cout<<result[c1]<<endl;
    loc=loc->next;
    c1++;
  }
  result[c1]=-1;
  return result;
}


double videos::getprobofrequest(int video){ //]0,1]
  t_videopart *aux;
  aux=getvideopart(video,1);
  return aux->popularity;
}

int videos::getduration(int video,int part) {
  t_videopart *aux;

  aux=getvideopart(video,part);
  return aux->duration;
}

long videos::getrate(int video,int part) {
  t_videopart *aux;

  aux=getvideopart(video,part);
  return aux->rate;
}

int videos::getnumparts(int video) {
  t_videopart *aux;
  int c1=0;

  aux=getvideopart(video,1);
  while (aux!=NULL) {
    aux=aux->nextpart;
    c1++;
  }
  return c1;
}

int videos::getvideobypopularity(double pop) {
  t_videopart *aux=videolist;
  int c1=1;
  double cummpop;

  //cerr<<pop<<endl;
  cummpop=aux->popularity;
  while ((pop>cummpop)&&(aux!=NULL)) {
    aux=aux->nextvideo;
    cummpop+=aux->popularity;
    c1++;
  }
  //cerr<<c1<<"-"<<cummpop<<endl;
  if (pop<cummpop)
    return c1;
  else {
    cerr<<"error trying to get video with popularity "<<1-pop<<endl;
    exit(-2);
  }
}

t_videopart *videos::getvideopart(int video,int part) {
  t_videopart *aux=videolist;
  int nvideo=1,npart=1;
  
  //cerr<<"vid "<<video<<" part "<<part<<endl;
  while ((nvideo<video)&&(aux!=NULL)) {
    nvideo++;
    aux=aux->nextvideo;
  }
  if (aux==NULL) {
    cerr<<"No such video ("<<video<<")"<<endl;
    exit(-2);
  }
  while ((npart<part)&&(aux!=NULL)) {
    npart++;
    aux=aux->nextpart;
  }
  if (aux==NULL) {
    cerr<<"No such part ("<<part<<" in video "<<video<<endl;
    exit(-2);
  }
  return aux;
}

int videos::getpartsize(t_videopart *part) {
  t_videopart *aux=part;
  int size=0;

  if (FILLALGORITHM==WEIGHTEDVIDEOS) {//(fullvideos) {
    while (aux!=NULL) {
      size+=(aux->rate/8)*aux->duration/1024;
      aux=aux->nextpart;
    }
    return size;
  }
  else
    return (part->rate/8)*part->duration/1024;
}

void videos::addlevel(t_videopart *video,int level,cachetree &tree) {
  int *cacheids,c1;
  t_place *aux;
  t_videopart *auxvideo;

  cacheids=tree.getcachesinlevel(level);
  c1=0;
  while (cacheids[c1]!=-1) {
    if (FILLALGORITHM==WEIGHTEDVIDEOS) {//(fullvideos) {
      auxvideo=video;
      while (auxvideo!=NULL) {
	aux=new(t_place);
	aux->nodeid=cacheids[c1];
	aux->next=auxvideo->location;
	auxvideo->location=aux;
	auxvideo=auxvideo->nextpart;
      }
    }
    else {
      aux=new(t_place);
      aux->nodeid=cacheids[c1];
      aux->next=video->location;
      video->location=aux;
    }
    c1++;
  }
  free(cacheids);
}

int videos::distribute(cachetree &tree) {
  t_videopart *aux=weightedlist;
  int level=1,partsize;
  double wmodifier,//weight modifier for reserving space depending on weight
    maxweight; // needed for normalize
  char fit,c1;
  long available[tree.getdepth()],total[tree.getdepth()];
  double k;
  int meanvidsize=1500;//Mb

  cerr<<"Distributing videos... ";
  fflush(stderr);
  for (c1=0;c1<tree.getdepth();c1++) {
    total[c1]=sizefunction(tree.getdepth(),c1+1);
    available[c1]=total[c1];
  }
  if (FILLALGORITHM==WEIGHTEDRESERVE2) {
    maxweight=aux->weight;
    while (aux!=NULL) {
      partsize=getpartsize(aux);
      k=0;
      for (c1=0;c1<level;c1++)
	k+=total[c1+1];
      k/=meanvidsize;
      wmodifier=1<aux->weight*k/maxweight?1:aux->weight*k/maxweight;
      if (level!=tree.getdepth())
	fit=((total[level-1]-available[level-1])+partsize)<=(total[level-1]*wmodifier);
      else
	fit=(available[level-1]>=partsize);
      if (fit) {
	storedparts[level-1]++;
	available[level-1]-=partsize;
	addlevel(aux,level,tree);
	aux=aux->nextweight;
	level=1;
      }
      else {
	level++;
	if (level>tree.getdepth()) {
	  cerr<<"Not enough space for storing videos"<<endl;
	  exit(-2);
	}
      }
    }
  }
  else { // NOT WEIGHTEDRESERVE2
    while (aux!=NULL) {
      partsize=getpartsize(aux);
      if (available[level-1]>=partsize) {
	storedparts[level-1]++;
	available[level-1]-=partsize;
	addlevel(aux,level,tree);
	aux=aux->nextweight;
      }
      else {
	level++;
	if (level>tree.getdepth()) {
	  cerr<<"Not enough space for storing videos"<<endl;
	  exit(-2);
	}
      }
    }
  }
  for (c1=0;c1<tree.getdepth();c1++)
    storedspace[c1]=total[c1]-available[c1];
  cerr<<"done"<<endl;
  return 1;
}

void videos::sortvideos() {
  t_videopart *video,*part,*index,*previndex;

  cerr<<"Sorting videos..."<<endl;
  video=videolist;
  part=video;
  weightedlist=part; // put an element as a first weighted element
  drawpoints(maxvideos);
  if (FILLALGORITHM==WEIGHTEDVIDEOS) //(fullvideos) 
    part=part->nextvideo;
  else {
    part=part->nextpart;
    if (part==NULL) {
      video=video->nextvideo;
      part=video;
    }    
  }
  index=weightedlist;
  previndex=index;
  while (part!=NULL) {
    /*** in many distributions with this special case the speed increases */
    /*** begin of special case of last element ****/
    if ((index->nextweight!=NULL) &&
	((index->nextweight->nextweight==NULL)&&(index->nextweight->weight>=part->weight))) {
      previndex=index;
      index=index->nextweight;
    }
    else {  /*** end of special case of last element ****/
      index=weightedlist;
      previndex=index;
      while ((index->nextweight!=NULL)&&(index->weight>part->weight)) {
	previndex=index;
	index=index->nextweight;
      }
    }/*** close brace because of special case */
    if (index->nextweight==NULL)
      index->nextweight=part;
    else
      if (previndex==index) { // first element
	part->nextweight=index;
	weightedlist=part;
      }
      else {
	part->nextweight=index;
	previndex->nextweight=part;
      }
    if (FILLALGORITHM==WEIGHTEDVIDEOS) {
      drawpoints(maxvideos);
      part=part->nextvideo;
    }
    else {
      part=part->nextpart;
      if (part==NULL) {
	drawpoints(maxvideos);
	video=video->nextvideo;
	part=video;
      }    
    } 
  }
}


t_videopart *videos::readvideoinfo(char *filename,double pop) {
  FILE *trace;
  int result,numpart=0;
  unsigned int durval,rateval;
  t_videopart *video=NULL,*lastpart=NULL,*aux;
  double cummweight=0;

  trace=fopen(filename,"r");
  if (trace==NULL) {
    cerr<<filename<<": "<<strerror(errno);
    exit(-2);
  }
  result=fscanf(trace,"%u %u",&durval,&rateval);
  while (result==2) {
    aux=new(t_videopart);
    if (aux==NULL) {
      cerr<<"Not enough memory to create video list"<<endl;
      exit(-1);
    }
    numpart++;
    aux->duration=durval*60;
    aux->rate=rateval*24*8/1000;//kbps
    aux->popularity=pop;
    //aux->weight=floor(weightfunction(aux->popularity,aux->duration,aux->rate));
    aux->weight=weightfunction(aux->popularity,aux->duration,aux->rate);
    if (FILLALGORITHM==WEIGHTEDRESERVE)
      aux->weight=-aux->weight;
    cummweight+=aux->weight;
    aux->nextvideo=NULL;
    aux->nextweight=NULL;
    aux->location=NULL;
    if (video==NULL)
      video=aux;
    else
      lastpart->nextpart=aux;
    lastpart=aux;
    result=fscanf(trace,"%u %u",&durval,&rateval);
  }
  if (FILLALGORITHM==WEIGHTEDVIDEOS) //(fullvideos)
    video->weight=cummweight/numpart;
  lastpart->nextpart=NULL;
  fclose(trace);
  return video;
}

videos::videos(long numvideos) {
  long c1;
  //int c2;
  t_videopart *video,*myvideolist=NULL,*lastvideo=NULL;
  char filename[256];
  double zipfk,pop;
  int videoduration,videoparts;
  double videobw;

  meanparts=0;
  meanduration=0;
  meanrate=0;
  cerr<<"Reading "<<numvideos<<" videos using "<<MAXTRACES<<" trace(s)... "<<endl;
  zipfk=0.0;
  for (c1=1;c1<=numvideos;c1++)
    zipfk+=1.0/c1;

  maxvideos=numvideos;
  c1=1;
  while (c1<=numvideos) {
    //c2=1;
    //    while (c1<=numvideos && c2<=MAXTRACES) {
    pop=1.0/(c1*zipfk);
      //snprintf(filename,255,VIDPATH,c2);
    snprintf(filename,255,VIDPATH,((c1-1)%MAXTRACES)+1);
    drawpoints(numvideos);
    video=readvideoinfo(filename,pop);
    if (myvideolist==NULL)
      myvideolist=video;
    else
      lastvideo->nextvideo=video;
    lastvideo=video;
    videoduration=0;
    videobw=0;
    videoparts=0;
    while (video!=NULL) {
      videoparts++;
      videobw=((videobw*videoduration)+(video->rate*video->duration))/(videoduration+video->duration);
      videoduration+=video->duration;
      video=video->nextpart;
    }
    meanrate=(long)((meanrate*(c1-1))+videobw)/c1;
    meanduration=((meanduration*(c1-1))+videoduration)/c1;
    meanparts=((meanparts*(c1-1))+videoparts)/c1;
    c1++;
      //c2++;
      //}
  }
  lastvideo->nextvideo=NULL;
  videolist=myvideolist;
  weightedlist=NULL;
  //cout<<"Mean Video Duration: "<<meanduration<<" seconds"<<endl;
  //cout<<"Mean Video Rate: "<<meanrate<<" kbps"<<endl;
  //cout<<"Mean Fragments in each Video: "<<meanparts<<" fragments"<<endl;
  sortvideos();
}

long videos::getmeanrate() {
  return meanrate;
}
int videos::getmeanduration() {
  return meanduration;
}
double videos::getmeanparts() {
  return meanparts;
}
//int videos::getmeanpartduration() {
//  return meanpartduration;
//}

/* void videos::printvideos() { */
/*   t_videopart *video,*part; */

/*   cout<<"first weight "<<weightedlist->weight<<endl; */
/*   video=videolist; */
/*   while (video!=NULL) { */
/*     part=video; */
/*     while (part!=NULL) { */
/*       cout<<"-----"<<endl; */
/*       cout<<" weight "<<part->weight<<endl; */
/*       if (part->nextweight!=NULL) */
/* 	cout<<" nextweight "<<part->nextweight->weight<<endl; */
/*       else */
/* 	cout<<" nextweight NULL"<<endl; */
/*       part=part->nextpart; */
/*     } */
/*     video=video->nextvideo; */
/*   } */
/* } */

void videos::printvideos() {
  t_videopart *video=weightedlist;
  t_place *myloc;

  while (video!=NULL) {
    cout<<"wei: "<<video->weight<<" size: "<<getpartsize(video)<<" Mb rate: "
	<<video->rate<<" kbps"<<endl;
    cout<<" loc: ";
    myloc=video->location;
    while (myloc!=NULL) {
      cout<<myloc->nodeid<<" ";
      myloc=myloc->next;
    }
    cout<<endl;
    video=video->nextweight;
  }
}

/*int main() {
  videos videorep(3);
  //cout<<"printing..."<<endl;
  videorep.printvideos();
  }*/
