/************************************************************************
                             -------------------
    begin                : Fri Dec 8 2000
    copyright            : (C) 2000 by Waldemar Baraldi
    email                : baraldi@lacasilla.com.ar
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 ***************************************************************************/

%{
#include "level.h"
#include "pipe.h"
#include "elbowupleft.h"
#include "elbowupright.h"
#include "elbowdownleft.h"
#include "elbowdownright.h"
#include "vertical.h"
#include "horizontal.h"
#include "verticalbowl.h"
#include "horizontalbowl.h"
#include "cross.h"
#include "entry.h"
#include "exit.h"

#define NUMBER									1
#define LEVEL										2
#define SPEED										3
#define GRAPH_DIR								4
#define PIPENIGHTDREAMS					5
#define ENTRY										6
#define EXIT										7
#define ROW											8
#define COLUMN									9
#define SIDE										10
#define O_BLOCK									11
#define C_BLOCK									12
#define DOUBLE_COUTE						13
#define EQUAL										15
#define SEMICOLON								16
#define EOFILE									17
#define NORTH										18
#define SOUTH										19
#define WEST										20
#define EAST										21
#define STRING									22
#define START_DELAY							23
#define RESTRICT_COEF   				24
#define FIXED_COEF							25
#define PIPE										26
#define REQUIRED								150

#define TYPE										27
// Possible values for TYPE
#define ELBOW_NW								28
#define ELBOW_NE								29
#define ELBOW_SW								30
#define ELBOW_SE								31
#define BOWL_H									32
#define BOWL_V									33
#define HORIZONTAL    					34
#define VERTICAL								35
#define CROSS										36

#define RESTRICTED_OUTPUT				37

#define BONUS										38
// Possible values for BONUS
#define NORMAL									40
#define SUPER										41
#define ULTRA										42
#define HYPER										43
#define EXTRA_LIFE							44
#define EXTRA_TIME							45

#define FIXED										46

#define TRUE										47
#define FALSE										48

#define BACKGROUND              49
//Posibles valores para background
#define WALLPAPER								50
#define MOSAIC									51


#define INVALID									100

int nline;
%}

%option noyywrap
%option nounput

DIGIT	[0-9]
SEP	[ \t]+

%%

#.+\n		                        nline++;
\n			                				nline++;
{DIGIT}+												return NUMBER;
level														return LEVEL;
speed														return SPEED;
entry														return ENTRY;
exit														return EXIT;
row															return ROW;
column													return COLUMN;
side														return SIDE;
graph_dir												return GRAPH_DIR;
pipenightdreams 								return PIPENIGHTDREAMS;
start_delay											return START_DELAY;
restrict_coef   								return RESTRICT_COEF;
fixed_coef											return FIXED_COEF;
=																return EQUAL;
\{															return O_BLOCK;
\}															return C_BLOCK;
\"															return DOUBLE_COUTE;
;																return SEMICOLON;
<<EOF>>													return EOF;
north														return NORTH;
south														return SOUTH;
west														return WEST;
east														return EAST;
pipe														return PIPE;
type														return TYPE;
elbow_nw												return ELBOW_NW;
elbow_ne												return ELBOW_NE;
elbow_sw												return ELBOW_SW;
elbow_se												return ELBOW_SE;
bowl_h													return BOWL_H;
bowl_v													return BOWL_V;
horizontal											return HORIZONTAL;
vertical												return VERTICAL;
cross														return CROSS;
restricted_output       				return RESTRICTED_OUTPUT;
bonus														return BONUS;
normal													return NORMAL;
super														return SUPER;
ultra                   				return ULTRA;
hyper														return HYPER;
life														return EXTRA_LIFE;
time														return EXTRA_TIME;
fixed                   				return FIXED;
background											return BACKGROUND;
wallpaper												return WALLPAPER;
mosaic													return MOSAIC;
required												return REQUIRED;
yes															return TRUE;
true														return TRUE;
no															return FALSE;
false														return FALSE;
\".+\"													return STRING;
{SEP}		
.																return INVALID;
%%

Level::Level(){
  level_number=0;
  error=false;
  entry_ready=false;
  npipes=0;
  start_delay=-1;
  restrict_coef=-1;
  fixed_coef=-1;
  speed=1;
  required=0;
  graph_dirs=NULL;
  background=MosaicBackground;
}

Level::~Level(){
  if (graph_dirs){
    graph_dirs->empty(true);
    delete graph_dirs;
  }
}

void Level::printError(Str * s){
  error=true;
  printf("Level error:%i:%s\n", nline, s->get());
  delete s;
}

CardinalPoint Level::sideAssignment(){
  int aux_token;

  if (yylex()==EQUAL){
    aux_token=yylex();
    token=yylex();
    switch (aux_token){
      case NORTH: return North;
      case SOUTH: return South;
      case WEST:  return West;
      case EAST:  return East;
      default: printError(new Str("Invalid side"));
    }
  }else
    printError(new Str("'=' expected"));
  return Void;
}

int Level::numberAssignment(){
  int aux;

  if (yylex()==EQUAL)
    if (yylex()==NUMBER){
      aux=atoi(yytext);
      token=yylex();
      return aux;
    }else printError(new Str("number expected"));
  else printError(new Str("'=' expected"));
  return 0;
}

Str * Level::stringAssignment(){
  Str * s;

  if (yylex()==EQUAL)
    if (yylex()==STRING){
      s=new Str(yytext);
      token=yylex();
      return s;
    }else printError(new Str("string expected"));
  else printError(new Str("'=' expected"));
  return NULL;
}

Level::PipeType Level::pipeTypeAssignment(){
  int aux_token;
  if (yylex()==EQUAL){
    aux_token=yylex();
    token=yylex();
    switch (aux_token){
      case ELBOW_NW  : return ElbowNW;
      case ELBOW_NE  : return ElbowNE;
      case ELBOW_SW  : return ElbowSW;
      case ELBOW_SE  : return ElbowSE;
      case BOWL_H    : return BowlH;
      case BOWL_V    : return BowlV;
      case VERTICAL  : return Vert;
      case HORIZONTAL: return Horiz;
      case CROSS		 : return CrossPipe;
      default:{
        printError(new Str("pipe type not valid"));
      }
    }
  }else printError(new Str("'=' expected"));
  return ElbowNW;
}

Bonus Level::bonusAssignment(){
  int aux_token;
  if (yylex()==EQUAL){
    aux_token=yylex();
    token=yylex();
    switch (aux_token){
      case NORMAL    : return NormalBonus;
      case SUPER     : return SuperBonus;
      case ULTRA     : return UltraBonus;
      case HYPER     : return HyperBonus;
      case EXTRA_LIFE: return LifeBonus;
      case EXTRA_TIME: return TimeBonus;
      default:{
        printError(new Str("bonus type not valid"));
        break;
      }
    }
  }else printError(new Str("'=' expected"));
  return NormalBonus;
}

bool Level::boolAssignment(){
  bool aux;
  if (yylex()==EQUAL){
    switch (yylex()){
      case TRUE      : {aux=true;break;}
      case FALSE     : {aux=false;break;}
      default:{
        printError(new Str("not boolean type"));
        break;
      }
    }
    token=yylex();
    return aux;
  }else printError(new Str("'=' expected"));
  return false;
}

BackgroundType Level::backgroundAssignment(){
  int aux_token;
  if (yylex()==EQUAL){
    aux_token=yylex();
    token=yylex();
    switch (aux_token){
      case WALLPAPER  : return WallpaperBackground;
      case MOSAIC     : return MosaicBackground;
      default:break;
    }
  }else printError(new Str("'=' expected"));
  return MosaicBackground;
}


Entry * Level::getEntry(int& row, int& column){
  Entry * entry= new Entry(pipetable[0].restricted_output1);
  row=pipetable[0].row;
  column=pipetable[0].column;
  return entry;
}

Exit * Level::getExit(int& row, int& column){
  Exit * exit= new Exit(pipetable[1].restricted_output1);
  row=pipetable[1].row;
  column=pipetable[1].column;
  return exit;
}

int Level::getFixedCoef(){
  return fixed_coef;
}

int Level::getRestrictionCoef(){
  return restrict_coef;
}

int Level::getStartDelay(){
  return start_delay;
}

int Level::getSpeed(){
  return speed;
}

int Level::getRequired(){
  return required;
}

BackgroundType Level::getBackgroundType(){
  return background;
}

List * Level::getGraphDirs(){
  List * list=new List();
  Index * ind=graph_dirs->getFirst();
  while (!graph_dirs->isEnd(ind)){
    list->insert(list->getEnd(), new Str((Str*)(graph_dirs->getObject(ind))));
    ind=graph_dirs->getNext(ind);
  }
  return list;
}

int Level::getPipeIndex(){
  return 2;
}

Pipe * Level::getPipe(int& i, int& row, int& column){
  Pipe * pipe;

  if (i < npipes && i > 1){
    switch (pipetable[i].type){
      case ElbowNW:{
        pipe=new ElbowUpLeft();
        break;
      }
      case ElbowNE:{
        pipe=new ElbowUpRight();
        break;
      }
      case ElbowSW:{
        pipe=new ElbowDownLeft();
        break;
      }
      case ElbowSE:{
        pipe=new ElbowDownRight();
        break;
      }
      case Vert:{
        pipe=new Vertical();
        break;
      }
      case Horiz:{
        pipe=new Horizontal();
        break;
      }
      case BowlH:{
        pipe=new HorizontalBowl();
        break;
      }
      case BowlV:{
        pipe=new VerticalBowl();
        break;
      }
      case CrossPipe:{
        pipe=new Cross();
        break;
      }
    }
    pipe->setBonus(pipetable[i].bonus);
    if (pipetable[i].restricted_output1 != Void)
      pipe->restrictAsOutput(pipetable[i].restricted_output1);
    if (pipetable[i].restricted_output2 != Void)
      pipe->restrictAsOutput(pipetable[i].restricted_output2);
    pipe->setFixed(pipetable[i].fixed);
    row=pipetable[i].row;
    column=pipetable[i].column;
    i++;
    return pipe;
  }
  return NULL;
}

void Level::ee(){
  int row=0, column=0;
  CardinalPoint con=North;
  int aux_token=token;

  if (token==ENTRY || token==EXIT){
    if (yylex()==O_BLOCK){
      token=yylex();
      while (token!=C_BLOCK && !error){
        switch (token){
          case ROW:{
            row=numberAssignment();
            break;
          }
          case COLUMN:{
            column=numberAssignment();
            break;
          }
          case SIDE:{
            con=sideAssignment();
            break;
          }
          default:{
            if (aux_token==ENTRY)
              printError(new Str("invalid word in entry structure"));
            else
              printError(new Str("invalid word in exit structure"));
          }
        }
      }
      if (!error) {
        token=yylex();
        if (aux_token==ENTRY){
          pipetable[0].row=row;
          pipetable[0].column=column;
          pipetable[0].restricted_output1=con;
          entry_ready=true;
        }else{
          pipetable[1].row=row;
          pipetable[1].column=column;
          pipetable[1].restricted_output1=con;
          exit_ready=true;
        }
      }
    }else printError(new Str("'{' expected"));
  }else printError(new Str("'entry' or 'exit' expected"));
}

void Level::pipe(){

  pipetable[npipes].row=0;
  pipetable[npipes].column=0;
  pipetable[npipes].type=ElbowNW;
  pipetable[npipes].restricted_output1=Void;
  pipetable[npipes].restricted_output2=Void;
  pipetable[npipes].bonus=NormalBonus;
  pipetable[npipes].fixed=false;

  if (token==PIPE){
    if (yylex()==O_BLOCK){
      token=yylex();
      while (token!=C_BLOCK && !error){
        switch (token){
          case ROW:{
            pipetable[npipes].row=numberAssignment();
            break;
          }
          case COLUMN:{
            pipetable[npipes].column=numberAssignment();
            break;
          }
          case TYPE:{
            pipetable[npipes].type=pipeTypeAssignment();
            break;
          }
          case RESTRICTED_OUTPUT:{
            if (pipetable[npipes].restricted_output1!=Void){
              if (pipetable[npipes].restricted_output2!=Void)
                pipetable[npipes].restricted_output1=pipetable[npipes].restricted_output2;
              pipetable[npipes].restricted_output2=sideAssignment();
            }else pipetable[npipes].restricted_output1=sideAssignment();
            break;
          }
          case BONUS:{
            pipetable[npipes].bonus=bonusAssignment();
            break;
          }
          case FIXED:{
            pipetable[npipes].fixed=boolAssignment();
            break;
          }
          default:printError(new Str("invalid word in pipe structure"));
        }
      }
      npipes++;
      token=yylex();
    }else printError(new Str("'{' expected"));
  }else printError(new Str("'pipe' expected"));
}


void Level::level(){
  Str * s;

  /** Default values for a level*/
  entry_ready=false;
  exit_ready=false;
  level_number=0;
  error=false;
  npipes=2;
  start_delay=0;
  restrict_coef=0;
  fixed_coef=0;
  speed=1;
  if (graph_dirs) graph_dirs->empty(true);
  else graph_dirs=new List();

  pipetable[0].row=0;
  pipetable[0].column=0;
  pipetable[0].restricted_output1=East;

  pipetable[1].row=0;
  pipetable[1].column=1;
  pipetable[1].restricted_output1=West;

  if ((token=yylex())==PIPENIGHTDREAMS){
    if (yylex()==O_BLOCK){
      token=yylex();
      while (token!=C_BLOCK && !error){
        switch (token){
          case LEVEL:{
            level_number=numberAssignment();
            break;
          }
          case SPEED:{
            speed=numberAssignment();
            break;
          }
          case GRAPH_DIR:{
            s=stringAssignment();
            if (!error){
              s->crop(1, s->lenght()-1);
              graph_dirs->insert(graph_dirs->getEnd(), s);
            }
            break;
          }
          case START_DELAY:{
            start_delay=numberAssignment();
            break;
          }
          case RESTRICT_COEF:{
            restrict_coef=numberAssignment();
            break;
          }
          case FIXED_COEF:{
            fixed_coef=numberAssignment();
            break;
          }
          case BACKGROUND:{
            background=backgroundAssignment();
            break;
          }
          case REQUIRED:{
          	required=numberAssignment();
          	break;
          }
          case ENTRY:case EXIT:{
            ee();
            break;
          }
          case PIPE:{
            pipe();
            break;
          }
          default:printError(new Str("invalid word in level structure"));
        }
      }
      if (!entry_ready && !error) printError(new Str("no entry available"));
      if (!exit_ready && !error)  printError(new Str("no exit available"));
    }else printError(new Str("'{' expected"));
  }else printError(new Str("'pipenightdreams' not found"));
}

int Level::load(Str * filename){
  YY_FLUSH_BUFFER;
  nline=1;
  if (!(yyin=fopen(filename->get(), "r"))){
   delete filename;
   return 1;
  }
  delete filename;
  level();
  fclose(yyin);
  if (error) return -1;
  return 0;
}

