/*
 EGGX を用いたプログラム実行局所性のシミュレーション
*/


#include  <stdio.h>
#include  <stdlib.h>
#include  <unistd.h>
#include  <eggx.h>

#define ITVL 1000 /* interval time (microsec) */
#define LMAX 800  /* max program line number */

void usage() {
  fprintf(stderr, "usage: locsim_pst [-vn][-s speed] source.c log.dat \n");
  fprintf(stderr, "         v : verbose mode.\n");
  fprintf(stderr, "         S : SKIP mode.\n");
  fprintf(stderr, "         s : display interval time (default=%d microsec)\n", ITVL);
  fprintf(stderr, "  source.c : program source file (uncommented) \n");
  fprintf(stderr, "   log.dat : log file \n");
  return;
}

int vmode=0;
int itvl=ITVL;
char pfname[256]; /* ソースプログラムファイル */
char lfname[256]; /* ログファイル */

int pcnt[LMAX]; /* 実行カウント */
int plin[LMAX]; /* 各行の長さ (インデント含む) */
int pind[LMAX]; /* 各行のインデント */
int pmax=0;     /* ソースコード行数 */
int pmw=0;      /* 最大幅バイト数 */
int pmc=0;      /* 最大ヒットカウント数 */

#define DD 50  /* 発火行を白く塗り、その後 DD 回遅延してから該当色に戻す */
int delay[DD];

int win;    /* ウィンドウ番号 */

#define WW 800 /* ウィンドウの幅 */
#define HH 700 /* ウィンドウの高さ */

#define PWW 400.0 /* ソースコード表示領域の幅 */
#define PHH 600.0 /* ソースコード表示領域の高さ */

#define XOFF 40 /* 描画をはじめる X 位置のオフセット */
#define YOFF 40 /* 描画をはじめる Y 位置のオフセット */

/* 
 カウント数 c と最大カウント数 pmc からカウントレベル(0-255)を
 算出し、これに対応する色を設定する
 0 は特色（一度も実行されていない）として灰色、
 1 から青白っぽく、99 が赤っぽく、間は黄色っぽくなるように
*/
int pgcolor(int c)
{
  int r, g, b;
  float lvl;

  if( c == 0 ) { /* 一度も実行されたことがない */
    newrgbcolor(win, 30, 100, 30);
  } else {
    lvl=(float)(c)/(float)(pmc)*255;
    r=lvl*2; if( r > 255 ) r=255; 
    g=255-abs(lvl-128)*2;
    b=(128-lvl)*2; if( b < 0 ) b=0; 
    newrgbcolor(win, r, g, b);
  };

  return 0;
}

/* 
 一行、プログラム行を描く
*/
int do_drawline(int i)
{
  int x, w;

  pgcolor(pcnt[i]);
  x=(float)(pind[i])/(float)pmw*PWW;
  w=(float)(plin[i]-pind[i])/(float)pmw*PWW;
  fillrect(win, XOFF+x, HH-YOFF-PHH/pmax*i, w, PHH/pmax);

  return 0;
}

/* 
 一行、プログラム行を描く（遅延つき）
*/
int do_drawline_d(int i)
{
  int l, x, w;

  newpen(win, 1); /* まず白で描画 */
  x=(float)(pind[i])/(float)pmw*PWW;
  w=(float)(plin[i]-pind[i])/(float)pmw*PWW;
  fillrect(win, XOFF+x, HH-YOFF-PHH/pmax*i, w, PHH/pmax);

  l=delay[DD-1]; /* 遅延して本来の色で再描画するべき行を取り出す */
  if( l != -1 ) { /* 未蓄積な状況でないことを確認 */
    pgcolor(pcnt[l]);
    x=(float)(pind[l])/(float)pmw*PWW;
    w=(float)(plin[l]-pind[l])/(float)pmw*PWW;
    fillrect(win, XOFF+x, HH-YOFF-PHH/pmax*l, w, PHH/pmax);
  };

  /* 遅延カウンタに現在白で描いた行を蓄積 (Ring buffer でやれよ！) */
  for(l=DD-1; l>0; l--) {
    delay[l]=delay[l-1];
  };
  delay[0]=i; 

  return 0;
}

/* 
 背景描画
 */
int show_mat()
{
  int i;
  
  gclr(win);

  /* カウント色見本 
   カウントゼロのラベルを描く */
  newpen(win, 1); 
  drawstr(win, WW-XOFF-150, YOFF+50, 16, 0.0, "0");
  pgcolor(0); 
  fillrect(win, WW-XOFF-100, YOFF+50, 100, 2);
  /* カウント 1 から最大(pmc)までのラベルを描く */
  for(i=1; i<100; i++) { 
    pgcolor( (int)( 0.01*(float)(i)*(float)(pmc)) );
    fillrect(win, WW-XOFF-100, YOFF+50+i*5, 100, 2);
  };
  newpen(win, 1); 
  drawstr(win, WW-XOFF-150, YOFF+50+i*5, 16, 0.0, "%d", pmc);
  drawstr(win, WW-XOFF-150, YOFF+50+i*5+20, 16, 0.0, "count");

  /* ソースコードイメージを描く */
  for(i=0; i<pmax; i++) {
    if( pind[i] != -1 ) { /* 描画対象（空白行以外） */
      do_drawline(i); 
    };
  };

  copylayer(win, 1, 0);  

  return 0;
}

 
/*
 最初の空白・タブ以外の文字の位置を返す
 先頭なら 0
 全部空白・タブなら -1
*/
int checkline(char *s)
{
  int i, l;
   
  l=strlen(s);
  for(i=0; i<l; i++) {
    if( (s[i]!=' ' )&&( s[i]!='\t' )) break;
  };
  if(i==l) i= -1;  
 
  return i;
}

/* 
 ソースを読んでメモリに情報抽出
 pmw も一緒に取得
*/
int do_pread()
{
  char buf[1024];
  FILE *pfp;
  int i;

  if( ( pfp=fopen(pfname, "r") ) == 0 ) {
    fprintf(stderr, "locsim ; cannot open program file (%s)\n", pfname);
  };

  fgets(buf, sizeof(buf), pfp);
  while( ! feof(pfp) ) {
    pcnt[pmax]=0;
    if(strlen(buf)>4) { /* XXX か空白インデントあり */
      plin[pmax]=strlen(&buf[4]);
      pind[pmax]=checkline(&buf[4]); /* -1 なら空白行 */
    } else { /* おそらくただの空白行 */
      plin[pmax]=0;
      pind[pmax]=-1;
    };
    pmax++;
    fgets(buf, sizeof(buf), pfp);
  };

  fclose(pfp);

  for(i=0; i<pmax; i++) {
    if(plin[i]>pmw) pmw=plin[i]; /* 最大幅をチェック */
  };

  if(vmode) printf("source code line number = %d\n",pmw);

  return 0;	
}

/* 
 ログを読んで最大カウントを pmc に取得
*/
int do_lcount()
{
  char buf[1024];
  FILE *lfp;
  int i;

  if( ( lfp=fopen(lfname, "r") ) == 0 ) {
    fprintf(stderr, "locsim ; cannot open log file (%s)\n", lfname);
  };
  
  fgets(buf, sizeof(buf), lfp);
  while( ! feof(lfp) ) {
    if( strncmp(buf, "@@@ ", 4) != 0 ) { /* 何か違うぞ？ */
      /* 無視 */
    } else {
      sscanf(&buf[4], "%d", &i);
      pcnt[i]++;
    };
    fgets(buf, sizeof(buf), lfp);
  }; 
  
  fclose(lfp);
  
  for(i=0; i<pmax; i++) {
    if(pcnt[i]>pmc) pmc=pcnt[i]; /* 最大カウントをチェック */
    pcnt[i]=0; /* ゼロリセットしておく */
  };

  if(vmode) printf("the most frequent called number = %d\n",pmc);
  
  return 0;	
}

/* 
 ログを読んで対象部分を描画
*/
int do_ldisp()
{
  char buf[1024];
  FILE *lfp;
  int i;

  for(i=0;i<DD;i++) delay[i]= -1;

  if( ( lfp=fopen(lfname, "r") ) == 0 ) {
    fprintf(stderr, "locsim ; cannot open log file (%s)\n", lfname);
  };
  
  fgets(buf, sizeof(buf), lfp);
  while( ! feof(lfp) ) {
    if( strncmp(buf, "@@@ ", 4) != 0 ) { /* 何か違うぞ？ */
      /* 無視 */
    } else {
      sscanf(&buf[4], "%d", &i);
      pcnt[i]++;
      do_drawline_d(i);
      if(itvl != 0) usleep(itvl);
    };
    fgets(buf, sizeof(buf), lfp);
  }; 
  
  fclose(lfp);
  
  return 0;	
}

/*
 入り口
 */
int main(int argc, char *argv[])
{
  int ch;
  extern char *optarg;
  extern int optind;

  /* option recognise */
  while ((ch = getopt(argc, argv, "vns:")) != EOF) {
    switch((char)ch) {
    case 'v': /* verbose mode */
      vmode=1;
      break;
    case 'n': /* quiet mode */
      vmode=0;
      break;
    case 's': /* interval time */
      itvl=atoi(optarg);
      break;
    case '?': /* Help message */
    default:
      usage();
      fprintf(stderr,"locsim : unrecognized parameter exist.\n");
      exit(1);
    };
  };
  /* Skip the position of argc,argv[] */
  argc -= optind;
  argv += optind;
  
  if(argc!=2) {
    usage();
    fprintf(stderr,"locsim : 2 parameter is required.\n");
    exit(1);
  };
  strcpy(pfname, argv[0]);
  strcpy(lfname, argv[1]);

  win=gopen(WW,HH);
  winname(win, "Program localitiy simulation");
  layer(win, 0, 1);

  do_pread(); /* ソースコードを読んで背景情報を得る */
  do_lcount(); /* ログファイルを一度読んで最大カウント数を得る */

  show_mat(); /* 背景描画 */

  /* READY to GO を表示、キー入力待ち */
  newpen(win, 1);
  fillrect(win, WW-145, 20, 20, 20);
  drawstr(win, WW-120, 25, 16, 0.0, "READY to GO");
  copylayer(win, 1, 0);
  ggetch(win); 

  show_mat(); /* 背景描画 */

  layer(win, 1, 1);

  do_ldisp(); /* 実行位置描画 */
  
  show_mat(); /* 実行終了後の再描画 */

  /* 終了表示とキー入力待ち */
  newpen(win, 1);
  fillrect(win, WW-145, 20, 20, 20);
  drawstr(win, WW-120, 25, 16, 0.0, "END of DATA");
  copylayer(win, 1, 0);
  ggetch(win);
  gclose(win); 

  return(0);

}
  
