// 2dfbi, © Du-Ne, 2002
// Enjoy! - Please Note: Give no second parameter for interpreting
//                       Use --debug or --brainfuck for debugging
#include <iostream.h>
#include <fstream.h>

//Editierbare Kosntanten
#define maxc 10000
#define maxx 200
#define maxb 50000
#define maxa 30000

//Interpreter Variablen
#include <stdio.h>
int  p, r, q;
char a[maxa], f[maxb], b, o, *s=f;
int interpret(char *k);

// 2dbf -> bf Teil
 void wdh(char m_zeichen, int m_anzahl);
 int getdirection( int m_predirection,
                           int m_posc,  int m_posx);
char field[maxc][maxx];
char bffield[maxb];
char result[11];

int main(int argc, char *argv[])
{

    if(argc==1)
    {
        cout<<endl
            <<"\t----------------------------------------------------"<<endl
            <<"\t|2dbfi - The origininal 2dbf interpreter by Du-Ne  |"<<endl
            <<"\t|Presented by: www.legacy-project.de and www.gcf.de|"<<endl
            <<"\t----------------------------------------------------"<<endl<<
        endl<<"This program must be started with at least one parameter, the "<<
        endl<<"name of the file which is to interpret. If you give a second "<<
        endl<<"parameter like --debug, your source is converted and then "<<
        endl<<"shown in brainfuck for debugging purposes."<<endl<<"Syntax: "
            <<endl<<argv[0]<<" filename [--debug]";
        return 0;
    }
    ifstream in(argv[1]);
    if(!in){cout<<"file not found"<<endl; return 0;}
     int line = 0;
     int pos  = 0;
     int c    = 0;
     int x    = 0;
     int z    = 0;
     int find = 0;
     int pred = 0;
     bool dorun;
     dorun = true;

    for(c=0; c<maxc; c++)
    {
      for(x=0; x<maxx; x++)
      {
            field[c][x]=char(32);
      }
    }
    for(find=0; find<maxb; find++)
    {
        bffield[find] = char(32);
    }
    find = 0;

    while(!in.eof())
    {
      char ch;
      ch=in.get();
      if(ch==char(10))
      {
            line++;
            pos = 0;
      }else{
            field[line][pos] = ch;
            pos++;
      }
    };
    pos--;
    field[line][pos] = char(32);    //Müll entfernen

    //cout<<getdirection(dpre, dc, dx)<<endl; //DEBUG
    //cout<<field[dc][dx]<<endl; //DEBUG

    c    = 0;
    x    = 0;
    pred = 0;
    int  gotback;
    int  gotdir;
    int  gotcount;
    char giveout;
    find = 0;
    while(dorun)
    {
        gotback   = getdirection(pred, c, x);
        gotdir    = gotback / 10;
        gotcount  = gotback % 10;
        switch(gotdir)
        {
           case 0: // Error
                cout<<endl<<"2dbf Layout Error at ["<<c+1<<"]["<<x+1
                    <<"]... (Line|Letter)"
                    <<endl
                    <<"Debug Information:  gotback = "<<gotback<<"  gotdir = "
                    <<gotdir<<"  gotcount = "<<gotcount<<"  pre-direction = "
                    <<pred
                    <<endl;
                return 0;
                break;
           case 9: // Ende
                if(argc>2)
                {
                 return 0;
                }else{
                 dorun = false;
                }
                break;
           case 1: // Nach Rechts
                x++;
                giveout = char(43); // +
                break;
           case 2: // Nach Links
                x--;
                giveout = char(45); // -
                break;
           case 3: // Nach Rechts-Unten
                x++;
                c++;
                giveout = char(62); // >
                break;
           case 4: // Nach Links-Unten
                x--;
                c++;
                giveout = char(60); // <
                break;
           case 5: // Nach Unten
                c++;
                giveout = char(91); // [
                break;
           case 6: // Nach Oben
                c--;
                giveout = char(93); // ]
                break;
           case 7: // Nach Rechts-Oben
                x++;
                c--;
                giveout = char(46); // .
                break;
           case 8: // Nach Links-Oben
                x--;
                c--;
                giveout = char(44); // ,
                break;


        }

        z = 0;
        wdh(giveout, gotcount);
           while(true) //wdh schleife
           {
               if(result[z]==char(32)) break;
               if(argc>2)
               {
                   cout<<result[z];
               }else{
                   //cout<<"find: "<<find<<"  ";
                   bffield[find] = result[z];
                   //cout<<"result: "<<result[z]<<"  ";
                   //cout<<"bffield: "<<bffield[find]<<endl;
                   find++;
               }
               z++;
           }

        pred      = gotdir;
        //cout<<"x";
    }
    if(argc<3) interpret(bffield);
    //DEBUG: Output bffield
    //cout<<"Dedugging..."<<endl;
    //for(find=0; find<100; find++)
    //{
    //    cout<<bffield[find];
    //}

    return 0;
}

 int getdirection( int m_predirection,
                           int m_posc,  int m_posx)
{
     int m_newdir  = 0;
    int          m_counter = 0;
     int m_c_dirs  = 0;
    char         m_betrchr;
    bool         m_allowed[9];
    bool         keepdir   = false;

    //allowed-array initialisieren in Abhängigkeit der predirection
    m_allowed[0] = false;
    m_allowed[1] = true;
    m_allowed[2] = true;
    m_allowed[3] = true;
    m_allowed[4] = true;
    m_allowed[5] = true;
    m_allowed[6] = true;
    m_allowed[7] = true;
    m_allowed[8] = true;
    switch(m_predirection)
    {
      case 0:
           m_allowed[0] = true;
           break;
      case 1:
           m_allowed[2] = false;
           break;
      case 2:
           m_allowed[1] = false;
           break;
      case 3:
           m_allowed[8] = false;
           break;
      case 8:
           m_allowed[3] = false;
           break;
      case 4:
           m_allowed[7] = false;
           break;
      case 7:
           m_allowed[4] = false;
           break;
      case 5:
           m_allowed[6] = false;
           break;
      case 6:
           m_allowed[5] = false;
           break;
    }

    //Erlaubte Directions bestimmen
    if(m_posx==0)
    {
       m_allowed[2] = false;
       m_allowed[4] = false;
       m_allowed[8] = false;
    }
    if(m_posx==(maxx-1))
    {
       m_allowed[1] = false;
       m_allowed[3] = false;
       m_allowed[7] = false;
    }
    if(m_posc==0)
    {
       m_allowed[6] = false;
       m_allowed[7] = false;
       m_allowed[8] = false;
    }
    if(m_posc==(maxc-1))
    {
       m_allowed[5] = false;
       m_allowed[4] = false;
       m_allowed[3] = false;
    }

    //Direction bestimmen
    if(m_allowed[1] &&  field[m_posc  ][m_posx+1] != char(32) && !keepdir)
    {
       m_newdir = 1;
       m_betrchr = field[m_posc  ][m_posx+1];
       m_c_dirs++;
       if(m_newdir == m_predirection){keepdir=true;}
    }
    if(m_allowed[2] &&  field[m_posc  ][m_posx-1] != char(32) && !keepdir)
    {
       m_newdir = 2;
       m_betrchr = field[m_posc  ][m_posx-1];
       m_c_dirs++;
       if(m_newdir == m_predirection){keepdir=true;}
    }
    if(m_allowed[3] &&  field[m_posc+1][m_posx+1] != char(32) && !keepdir)
    {
       m_newdir = 3;
       m_betrchr = field[m_posc+1][m_posx+1];
       m_c_dirs++;
       if(m_newdir == m_predirection){keepdir=true;}
    }
    if(m_allowed[4] &&  field[m_posc+1][m_posx-1] != char(32) && !keepdir)
    {
       m_newdir = 4;
       m_betrchr = field[m_posc+1][m_posx-1];
       m_c_dirs++;
       if(m_newdir == m_predirection){keepdir=true;}
    }
    if(m_allowed[5] &&  field[m_posc+1][m_posx  ] != char(32) && !keepdir)
    {
       m_newdir = 5;
       m_betrchr = field[m_posc+1][m_posx  ];
       m_c_dirs++;
       if(m_newdir == m_predirection){keepdir=true;}
    }
    if(m_allowed[6] &&  field[m_posc-1][m_posx  ] != char(32) && !keepdir)
    {
       m_newdir = 6;
       m_betrchr = field[m_posc-1][m_posx  ];
       m_c_dirs++;
       if(m_newdir == m_predirection){keepdir=true;}
    }
    if(m_allowed[7] &&  field[m_posc-1][m_posx+1] != char(32) && !keepdir)
    {
       m_newdir = 7;
       m_betrchr = field[m_posc-1][m_posx+1];
       m_c_dirs++;
       if(m_newdir == m_predirection){keepdir=true;}
    }
    if(m_allowed[8] &&  field[m_posc-1][m_posx-1] != char(32) && !keepdir)
    {
       m_newdir = 8;
       m_betrchr = field[m_posc-1][m_posx-1];
       m_c_dirs++;
       if(m_newdir == m_predirection){keepdir=true;}
    }




    //Anzahl bestimmen  und Ausgeben
    m_counter = int(m_betrchr)-48;

    //if(m_posx == 66 & m_posc == 76)
    //{
    //  cout<<"Debug: couter: "<<m_counter<<"  c_dir: "<<m_c_dirs<<
    //        "  keepdir: "<<keepdir<<" newdir: "<<m_newdir
    //      <<endl;
    //}

    if(m_c_dirs==1)
    {
        if((m_counter <10) && (m_counter >=0))
        {
           return (m_newdir * 10) + m_counter;
        }else{
           if(keepdir){return m_newdir * 10;} // Ausnahme bei Arm-Verlängerung
           return m_newdir * 10 + 1;
        }
    }else{
        if(m_c_dirs>1)
        {
           if((m_counter <10) && (m_counter >=0))
           {
              if(keepdir){return (m_newdir * 10) + m_counter;} else {return 0;}
           }else{                                              // 0 = Fehler
              if(keepdir){return (m_newdir * 10)            ;} else {return 0;}
           }
        }else{
           return 90;                                           // 90 = Ende
        }
    }
}

void wdh(char m_zeichen, int m_anzahl)
{
     int m_x = 0;
     int m_y = 0;
     char m_result[11] = "          ";
     if(m_anzahl != 0)
     {
        for(m_x = 0; m_x < m_anzahl; m_x++)
        {
           //cout<<m_zeichen;
           m_result[m_x] = m_zeichen;
           m_y++;
        }
     }
     result = m_result;
}

// INTERPRETER TEIL (taken from bfi)
int interpret(char *k)
{
	char *d;

	r++;
	while( *k ) {
		switch(o=1,*k++) {
		case '<': p--;        break;
		case '>': p++;        break;
		case '+': a[p]++;     break;
		case '-': a[p]--;     break;
		case '.': putchar(a[p]); fflush(stdout); break;
		case ',': a[p]=getchar();fflush(stdout); break;
		case '[':
			for( b=1,d=k; b && *k; k++ )
				b+=*k=='[', b-=*k==']';
			if(!b) {
				k[-1]=0;
				while( a[p] )
					interpret(d);
				k[-1]=']';
				break;
			}
		case ']':
			puts("2dbf Syntax Error: Unbalanced brackets []"), exit(0);
		case '#':
			if(q>2)
				printf("%2d %2d %2d %2d %2d %2d %2d %2d %2d %2d\n%*s\n",
				     *a,a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],3*p+2,"^");
			break;
		default: o=0;
		}
		if( p<0 || p>maxa)
			puts("2dbf Syntax Error: Array-Over/Underflow"), exit(0);
	}
	r--;
	return 0;
}
